From 62bd7bb1535eb5bc2ba6c700fcdacaf2e2ee01ed Mon Sep 17 00:00:00 2001 From: Tutus Development Date: Fri, 19 Dec 2025 14:23:04 +0000 Subject: [PATCH] Initial Tutus chain fork from NeoGo - Renamed module to github.com/tutus-one/tutus-chain - Created Tutus network configurations - Updated Makefile for tutus binary - Updated service templates - Added Tutus README --- .docker/docker-compose.yml | 74 + .docker/privnet-entrypoint.ps1 | 15 + .docker/privnet-entrypoint.sh | 15 + .docker/wallets/wallet1.json | 55 + .docker/wallets/wallet1_solo.json | 72 + .docker/wallets/wallet2.json | 55 + .docker/wallets/wallet3.json | 55 + .docker/wallets/wallet4.json | 55 + .dockerignore | 2 + .github/CODEOWNERS | 1 + .github/PULL_REQUEST_TEMPLATE.md | 7 + .github/logo_dark.png | Bin 0 -> 41579 bytes .github/logo_light.png | Bin 0 -> 54889 bytes .github/workflows/build.yml | 152 + .github/workflows/contribution_guidelines.yml | 11 + .github/workflows/tests.yml | 197 + .gitignore | 60 + .gitmodules | 4 + CHANGELOG.md | 3368 +++++++++++++ CONTRIBUTING.md | 25 + Dockerfile | 36 + Dockerfile.wsc | 37 + LICENSE.md | 21 + Makefile | 159 + README.md | 88 + ROADMAP.md | 36 + cli/.gitignore | 1 + cli/app/app.go | 41 + cli/app/main_test.go | 19 + cli/cmdargs/parser.go | 360 ++ cli/cmdargs/parser_test.go | 379 ++ cli/flags/address.go | 146 + cli/flags/address_test.go | 211 + cli/flags/fixed8.go | 123 + cli/flags/fixed8_test.go | 128 + cli/input/input.go | 71 + cli/input/readpass_unix.go | 29 + cli/input/readpass_windows.go | 21 + cli/main.go | 17 + cli/nep_test/nep11_test.go | 682 +++ cli/nep_test/nep17_test.go | 435 ++ cli/options/cli_options_test.go | 33 + cli/options/filtering_core.go | 28 + cli/options/options.go | 486 ++ cli/options/options_test.go | 57 + cli/paramcontext/context.go | 50 + cli/query/query.go | 379 ++ cli/query/query_test.go | 267 + cli/server/cli_dump_test.go | 163 + cli/server/cli_server_test.go | 132 + cli/server/dump.go | 102 + cli/server/dump_bin.go | 87 + cli/server/dump_bin_test.go | 91 + cli/server/dump_test.go | 23 + cli/server/metrics.go | 23 + cli/server/server.go | 709 +++ cli/server/server_test.go | 408 ++ cli/server/signals_unix.go | 11 + cli/server/signals_windows.go | 12 + cli/smartcontract/contract_test.go | 1448 ++++++ cli/smartcontract/generate.go | 124 + cli/smartcontract/generate_test.go | 701 +++ cli/smartcontract/manifest.go | 104 + cli/smartcontract/permission.go | 130 + .../nft-d/dynamic_hash/rpcbindings_test.go | 60 + .../rpcbindings/nft-d/rpcbindings_test.go | 65 + .../nft-nd/dynamic_hash/rpcbindings_test.go | 234 + .../rpcbindings/nft-nd/rpcbindings_test.go | 239 + .../rpcbindings/extended/rpcbindings_test.go | 746 +++ .../rpcbindings/guessed/rpcbindings_test.go | 759 +++ .../rpcbindings/rpcbindings_test.go | 500 ++ .../dynamic_hash/rpcbindings_test.go | 53 + .../royalty/rpcbindings/rpcbindings_test.go | 57 + .../dynamic_hash/rpcbindings_test.go | 2860 +++++++++++ .../structs/rpcbindings/rpcbindings_test.go | 2864 +++++++++++ .../dynamic_hash/rpcbindings_test.go | 563 +++ .../types/rpcbindings/rpcbindings_test.go | 567 +++ .../rpcbindings/verify/rpcbindings_test.go | 147 + cli/smartcontract/smart_contract.go | 993 ++++ cli/smartcontract/smart_contract_test.go | 138 + cli/smartcontract/testdata/deploy/main.go | 91 + cli/smartcontract/testdata/deploy/neo-go.yml | 4 + cli/smartcontract/testdata/deploy/sub/put.go | 14 + .../testdata/deploy/update.manifest.json | 1 + cli/smartcontract/testdata/deploy/updated.go | 6 + cli/smartcontract/testdata/gas/gas.go | 52 + .../testdata/gas/gas.manifest.json | 1 + .../testdata/invalid1/invalid.go | 21 + .../testdata/invalid1/invalid.yml | 7 + .../testdata/invalid2/invalid.go | 21 + .../testdata/invalid2/invalid.yml | 7 + .../testdata/invalid3/invalid.go | 21 + .../testdata/invalid3/invalid.yml | 7 + .../testdata/invalid4/invalid.go | 5 + .../testdata/invalid4/invalid.yml | 2 + cli/smartcontract/testdata/nameservice/nns.go | 511 ++ .../testdata/nameservice/nns.manifest.json | 441 ++ cli/smartcontract/testdata/nex/nex.go | 339 ++ .../testdata/nex/nex.manifest.json | 275 + cli/smartcontract/testdata/nonepiter/iter.go | 63 + .../testdata/nonepiter/iter.manifest.json | 33 + .../testdata/rpcbindings/invalid1/invalid.go | 7 + .../testdata/rpcbindings/invalid1/invalid.yml | 1 + .../testdata/rpcbindings/invalid2/invalid.go | 7 + .../testdata/rpcbindings/invalid2/invalid.yml | 6 + .../testdata/rpcbindings/invalid3/invalid.go | 7 + .../testdata/rpcbindings/invalid3/invalid.yml | 8 + .../testdata/rpcbindings/invalid4/invalid.go | 17 + .../testdata/rpcbindings/invalid4/invalid.yml | 6 + .../testdata/rpcbindings/invalid5/invalid.go | 12 + .../testdata/rpcbindings/invalid5/invalid.yml | 16 + .../testdata/rpcbindings/invalid6/invalid.go | 14 + .../testdata/rpcbindings/invalid6/invalid.yml | 18 + .../testdata/rpcbindings/invalid7/invalid.go | 14 + .../testdata/rpcbindings/invalid7/invalid.yml | 6 + .../testdata/rpcbindings/invalid8/invalid.go | 16 + .../testdata/rpcbindings/invalid8/invalid.yml | 1 + .../rpcbindings/notifications/config.yml | 23 + .../notifications/config_extended.yml | 60 + .../notifications/config_guessed.yml | 23 + .../notifications/notifications.go | 33 + .../testdata/rpcbindings/royalty/config.yml | 16 + .../testdata/rpcbindings/royalty/royalty.go | 13 + .../testdata/rpcbindings/structs/config.yml | 3 + .../testdata/rpcbindings/structs/structs.go | 40 + .../testdata/rpcbindings/types/config.yml | 3 + .../testdata/rpcbindings/types/types.go | 103 + .../testdata/verify.bindings.yml | 297 ++ cli/smartcontract/testdata/verify.go | 20 + .../testdata/verify.manifest.json | 1 + cli/smartcontract/testdata/verify.nef | Bin 0 -> 87 bytes cli/smartcontract/testdata/verify.yml | 12 + .../testdata/verifyrpc/verify.manifest.json | 80 + cli/testdata/testwallet.json | 30 + cli/testdata/testwallet_multi.json | 64 + cli/testdata/wallet1_solo.json | 83 + cli/txctx/tx.go | 115 + cli/util/audit-bin.go | 261 + cli/util/cancel.go | 103 + cli/util/convert.go | 275 + cli/util/dump.go | 57 + cli/util/send.go | 53 + cli/util/upload_bin.go | 373 ++ cli/util/upload_state.go | 191 + cli/util/util_test.go | 241 + cli/vm/cli.go | 1574 ++++++ cli/vm/cli_test.go | 1447 ++++++ cli/vm/vm.go | 48 + cli/wallet/candidate_test.go | 289 ++ cli/wallet/legacy.go | 177 + cli/wallet/legacy_test.go | 106 + cli/wallet/multisig.go | 102 + cli/wallet/multisig_test.go | 266 + cli/wallet/nep11.go | 375 ++ cli/wallet/nep17.go | 730 +++ cli/wallet/validator.go | 197 + cli/wallet/wallet.go | 1107 ++++ cli/wallet/wallet_test.go | 1154 +++++ codecov.yml | 3 + config/config_embed.go | 37 + config/protocol.mainnet.neofs.yml | 100 + config/protocol.mainnet.yml | 117 + config/protocol.privnet.docker.four.yml | 82 + config/protocol.privnet.docker.one.yml | 82 + config/protocol.privnet.docker.single.yml | 73 + config/protocol.privnet.docker.three.yml | 82 + config/protocol.privnet.docker.two.yml | 82 + config/protocol.privnet.yml | 76 + config/protocol.testnet.neofs.yml | 107 + config/protocol.testnet.yml | 118 + config/protocol.tutus.testnet.yml | 62 + config/protocol.tutus.yml | 73 + config/protocol.unit_testnet.single.yml | 56 + config/protocol.unit_testnet.yml | 68 + docs/cli.md | 727 +++ docs/compiler.md | 625 +++ docs/consensus.md | 209 + docs/conventions.md | 44 + docs/labels.md | 23 + docs/neofs-blockstorage.md | 168 + docs/node-configuration.md | 609 +++ docs/notary.md | 513 ++ docs/notifications.md | 664 +++ docs/oracle.md | 73 + docs/release-instruction.md | 80 + docs/rpc.md | 474 ++ docs/stateroots.md | 53 + docs/vm.md | 234 + examples/README.md | 59 + examples/engine/engine.go | 29 + examples/engine/engine.yml | 20 + examples/engine/go.mod | 5 + examples/engine/go.sum | 2 + examples/events/events.go | 36 + examples/events/events.yml | 28 + examples/events/go.mod | 5 + examples/events/go.sum | 2 + examples/iterator/go.mod | 5 + examples/iterator/go.sum | 2 + examples/iterator/iterator.go | 33 + examples/iterator/iterator.yml | 12 + examples/my_wallet.json | 30 + examples/neofs/go.mod | 5 + examples/neofs/go.sum | 2 + examples/neofs/neofs.go | 79 + examples/neofs/neofs.yml | 6 + examples/nft-d/go.mod | 5 + examples/nft-d/go.sum | 2 + examples/nft-d/nft.go | 467 ++ examples/nft-d/nft.yml | 22 + examples/nft-nd-nns/go.mod | 62 + examples/nft-nd-nns/go.sum | 284 ++ examples/nft-nd-nns/namestate.go | 31 + examples/nft-nd-nns/nns.go | 727 +++ examples/nft-nd-nns/nns.yml | 21 + examples/nft-nd-nns/nns_test.go | 454 ++ examples/nft-nd-nns/recordtype.go | 20 + examples/nft-nd/go.mod | 5 + examples/nft-nd/go.sum | 2 + examples/nft-nd/nft.go | 354 ++ examples/nft-nd/nft.yml | 19 + examples/oracle/go.mod | 5 + examples/oracle/go.sum | 2 + examples/oracle/oracle.go | 37 + examples/oracle/oracle.yml | 6 + examples/runtime/go.mod | 5 + examples/runtime/go.sum | 2 + examples/runtime/runtime.go | 81 + examples/runtime/runtime.yml | 12 + examples/storage/go.mod | 5 + examples/storage/go.sum | 2 + examples/storage/storage.go | 62 + examples/storage/storage.yml | 7 + examples/timer/go.mod | 5 + examples/timer/go.sum | 2 + examples/timer/timer.go | 79 + examples/timer/timer.yml | 6 + examples/token/go.mod | 5 + examples/token/go.sum | 2 + examples/token/nep17/nep17.go | 134 + examples/token/token.go | 63 + examples/token/token.yml | 15 + examples/zkp/cubic_circuit/README.md | 63 + examples/zkp/cubic_circuit/go.mod | 61 + examples/zkp/cubic_circuit/go.sum | 214 + examples/zkp/cubic_circuit/main.go | 93 + examples/zkp/cubic_circuit/main_test.go | 326 ++ examples/zkp/cubic_circuit/response8 | Bin 0 -> 74992 bytes examples/zkp/xor_compat/go.mod | 5 + examples/zkp/xor_compat/go.sum | 2 + examples/zkp/xor_compat/verify.go | 90 + examples/zkp/xor_compat/verify.yml | 3 + go.mod | 74 + go.sum | 322 ++ internal/basicchain/basic.go | 310 ++ .../basicchain/testdata/invoke/invoke.yml | 3 + .../testdata/invoke/invokescript_contract.go | 17 + .../testdata/storage/storage_contract.go | 34 + .../testdata/storage/storage_contract.yml | 2 + internal/basicchain/testdata/test_contract.go | 122 + .../basicchain/testdata/test_contract.yml | 17 + .../testdata/verify/verification_contract.go | 15 + .../testdata/verify/verification_contract.yml | 2 + .../verification_with_args_contract.go | 7 + .../verification_with_args_contract.yml | 2 + internal/contracts/contracts.go | 105 + internal/contracts/contracts_test.go | 492 ++ .../contracts/management_helper/README.md | 9 + .../management_helper1.manifest.json | 1 + .../management_helper/management_helper1.nef | Bin 0 -> 516 bytes .../management_helper2.manifest.json | 1 + .../management_helper/management_helper2.nef | Bin 0 -> 79 bytes internal/contracts/oracle_contract/README.md | 9 + internal/contracts/oracle_contract/go.mod | 5 + internal/contracts/oracle_contract/go.sum | 2 + internal/contracts/oracle_contract/oracle.go | 42 + .../oracle_contract/oracle.manifest.json | 1 + internal/contracts/oracle_contract/oracle.nef | Bin 0 -> 461 bytes internal/contracts/oracle_contract/oracle.yml | 7 + internal/fakechain/fakechain.go | 529 ++ internal/keytestcases/testcases.go | 49 + internal/random/random_util.go | 49 + internal/testchain/address.go | 179 + internal/testchain/network.go | 8 + internal/testchain/transaction.go | 145 + internal/testcli/executor.go | 406 ++ internal/testserdes/testing.go | 98 + internal/versionutil/init.go | 14 + pkg/compiler/analysis.go | 760 +++ pkg/compiler/assign_test.go | 199 + pkg/compiler/binary_expr_test.go | 404 ++ pkg/compiler/builtin_test.go | 298 ++ pkg/compiler/byte_conversion_test.go | 81 + pkg/compiler/codegen.go | 2786 ++++++++++ pkg/compiler/codegen_test.go | 92 + pkg/compiler/compiler.go | 530 ++ pkg/compiler/compiler_test.go | 678 +++ pkg/compiler/constant_test.go | 88 + pkg/compiler/convert_test.go | 363 ++ pkg/compiler/debug.go | 635 +++ pkg/compiler/debug_test.go | 414 ++ pkg/compiler/defer_test.go | 301 ++ pkg/compiler/doc.go | 4 + pkg/compiler/for_test.go | 878 ++++ pkg/compiler/func_scope.go | 140 + pkg/compiler/function_call_test.go | 431 ++ pkg/compiler/generics_test.go | 91 + pkg/compiler/global_test.go | 943 ++++ pkg/compiler/if_test.go | 152 + pkg/compiler/import_test.go | 91 + pkg/compiler/init_test.go | 107 + pkg/compiler/inline.go | 315 ++ pkg/compiler/inline_test.go | 500 ++ pkg/compiler/interop_test.go | 801 +++ pkg/compiler/jumps_test.go | 193 + pkg/compiler/lambda_test.go | 28 + pkg/compiler/limit_test.go | 29 + pkg/compiler/map_test.go | 322 ++ pkg/compiler/native_test.go | 421 ++ pkg/compiler/nilcheck_test.go | 55 + pkg/compiler/numeric_test.go | 33 + pkg/compiler/panic_test.go | 46 + pkg/compiler/pointer_test.go | 68 + pkg/compiler/return_test.go | 149 + pkg/compiler/slice_test.go | 713 +++ pkg/compiler/struct_test.go | 516 ++ pkg/compiler/switch_test.go | 390 ++ pkg/compiler/syscall_test.go | 494 ++ pkg/compiler/testdata/bar/bar.go | 9 + pkg/compiler/testdata/block/block.go | 9 + pkg/compiler/testdata/compile/test.go | 7 + pkg/compiler/testdata/constonly/const.go | 4 + pkg/compiler/testdata/foo/foo.go | 22 + pkg/compiler/testdata/foobar/bar.go | 3 + pkg/compiler/testdata/foobar/foo.go | 7 + .../testdata/globalvar/funccall/main.go | 18 + pkg/compiler/testdata/globalvar/main.go | 14 + .../testdata/globalvar/nested1/main.go | 29 + .../testdata/globalvar/nested2/main.go | 20 + .../testdata/globalvar/nested3/main.go | 18 + .../testdata/importcycle/pkg1/pkg1.go | 9 + .../testdata/importcycle/pkg2/pkg2.go | 11 + .../testdata/importcycle/pkg3/pkg3.go | 11 + pkg/compiler/testdata/inline/a/a.go | 7 + pkg/compiler/testdata/inline/b/b.go | 7 + pkg/compiler/testdata/inline/c/null.go | 22 + pkg/compiler/testdata/inline/d/d.go | 26 + pkg/compiler/testdata/inline/inline.go | 75 + pkg/compiler/testdata/method/struct.go | 16 + pkg/compiler/testdata/multi/file1.go | 9 + pkg/compiler/testdata/multi/file2.go | 11 + pkg/compiler/testdata/nestedcall/call.go | 43 + .../testdata/nestedcall/inner/call.go | 6 + pkg/compiler/testdata/notify/event.go | 15 + pkg/compiler/testdata/pkg1/pkg1.go | 7 + pkg/compiler/testdata/pkg2/pkg2.go | 9 + pkg/compiler/testdata/pkg3/pkg3.go | 7 + pkg/compiler/testdata/runh/hash.go | 13 + pkg/compiler/testdata/strange/normal.go | 4 + pkg/compiler/testdata/types/types.go | 7 + pkg/compiler/testdata/util/equals.go | 6 + pkg/compiler/type_test.go | 39 + pkg/compiler/types.go | 115 + pkg/compiler/vardecl_test.go | 76 + pkg/compiler/vars.go | 103 + pkg/compiler/verify_test.go | 57 + pkg/compiler/vm_test.go | 207 + pkg/config/application_config.go | 173 + pkg/config/application_config_test.go | 259 + pkg/config/basic_service.go | 9 + pkg/config/config.go | 167 + pkg/config/config_test.go | 56 + pkg/config/consensus.go | 4 + pkg/config/doc.go | 4 + pkg/config/genesis_extensions.go | 120 + pkg/config/hardfork.go | 125 + pkg/config/hardfork_string.go | 51 + pkg/config/internal_service.go | 8 + pkg/config/ledger_config.go | 97 + pkg/config/ledger_config_test.go | 27 + pkg/config/limits/limits.go | 17 + pkg/config/logger.go | 21 + pkg/config/neofsservice_config.go | 76 + pkg/config/netmode/doc.go | 4 + pkg/config/netmode/netmode.go | 41 + pkg/config/notary_config.go | 4 + pkg/config/oracle_config.go | 24 + pkg/config/p2p.go | 22 + pkg/config/protocol_config.go | 261 + pkg/config/protocol_config_test.go | 391 ++ pkg/config/rpc_config.go | 51 + pkg/config/state_root.go | 4 + pkg/config/wallet_config.go | 7 + pkg/consensus/block.go | 73 + pkg/consensus/block_test.go | 41 + pkg/consensus/cache.go | 73 + pkg/consensus/cache_test.go | 63 + pkg/consensus/change_view.go | 33 + pkg/consensus/change_view_test.go | 18 + pkg/consensus/commit.go | 30 + pkg/consensus/commit_test.go | 18 + pkg/consensus/consensus.go | 807 +++ pkg/consensus/consensus_test.go | 603 +++ pkg/consensus/crypto_test.go | 25 + pkg/consensus/doc.go | 7 + pkg/consensus/payload.go | 236 + pkg/consensus/payload_test.go | 294 ++ pkg/consensus/prepare_request.go | 57 + pkg/consensus/prepare_request_test.go | 51 + pkg/consensus/prepare_response.go | 27 + pkg/consensus/prepare_response_test.go | 16 + pkg/consensus/recovery_message.go | 314 ++ pkg/consensus/recovery_message_test.go | 158 + pkg/consensus/recovery_request.go | 26 + pkg/consensus/recovery_request_test.go | 15 + pkg/consensus/testdata/wallet1.json | 55 + pkg/consensus/testdata/wallet2.json | 55 + pkg/consensus/testdata/wallet3.json | 55 + pkg/consensus/testdata/wallet4.json | 55 + pkg/core/basic_chain_test.go | 60 + pkg/core/bench_test.go | 216 + pkg/core/block/block.go | 250 + pkg/core/block/block_test.go | 282 ++ pkg/core/block/doc.go | 6 + pkg/core/block/header.go | 266 + pkg/core/block/header_test.go | 54 + pkg/core/block/helper_test.go | 37 + pkg/core/blockchain.go | 3534 +++++++++++++ pkg/core/blockchain_core_test.go | 505 ++ pkg/core/blockchain_neotest_test.go | 3030 +++++++++++ pkg/core/chaindump/dump.go | 93 + pkg/core/chaindump/dump_test.go | 87 + pkg/core/custom_native_test.go | 261 + pkg/core/dao/dao.go | 1106 ++++ pkg/core/dao/dao_test.go | 473 ++ pkg/core/doc.go | 28 + pkg/core/fee/calculate.go | 42 + pkg/core/fee/opcode.go | 213 + pkg/core/fee/opcode_test.go | 19 + pkg/core/headerhashes.go | 262 + pkg/core/helper_test.go | 128 + pkg/core/interop/context.go | 636 +++ pkg/core/interop/context_test.go | 53 + pkg/core/interop/contract/account.go | 71 + pkg/core/interop/contract/account_test.go | 178 + pkg/core/interop/contract/call.go | 202 + pkg/core/interop/contract/call_test.go | 743 +++ pkg/core/interop/crypto/ecdsa.go | 51 + pkg/core/interop/crypto/ecdsa_test.go | 234 + pkg/core/interop/crypto/interop.go | 21 + pkg/core/interop/doc.go | 7 + pkg/core/interop/gas_price.go | 12 + pkg/core/interop/interopnames/convert.go | 25 + pkg/core/interop/interopnames/convert_test.go | 20 + pkg/core/interop/interopnames/names.go | 89 + pkg/core/interop/iterator/interop.go | 69 + pkg/core/interop/iterator/interop_test.go | 51 + pkg/core/interop/runtime/engine.go | 194 + pkg/core/interop/runtime/engine_test.go | 173 + pkg/core/interop/runtime/ext_test.go | 760 +++ pkg/core/interop/runtime/util.go | 119 + pkg/core/interop/runtime/util_test.go | 135 + pkg/core/interop/runtime/witness.go | 143 + pkg/core/interop/storage/basic.go | 185 + pkg/core/interop/storage/bench_test.go | 111 + pkg/core/interop/storage/find.go | 150 + pkg/core/interop/storage/interops_test.go | 39 + pkg/core/interop/storage/storage_test.go | 392 ++ pkg/core/interops.go | 95 + pkg/core/mempool/bench_test.go | 61 + pkg/core/mempool/feer.go | 14 + pkg/core/mempool/mem_pool.go | 645 +++ pkg/core/mempool/mem_pool_test.go | 829 +++ pkg/core/mempool/subscriptions.go | 68 + pkg/core/mempool/subscriptions_test.go | 95 + pkg/core/mempoolevent/event.go | 70 + pkg/core/mpt/base.go | 121 + pkg/core/mpt/batch.go | 288 ++ pkg/core/mpt/batch_test.go | 366 ++ pkg/core/mpt/bench_test.go | 38 + pkg/core/mpt/billet.go | 369 ++ pkg/core/mpt/billet_test.go | 210 + pkg/core/mpt/branch.go | 104 + pkg/core/mpt/compat_test.go | 441 ++ pkg/core/mpt/doc.go | 45 + pkg/core/mpt/empty.go | 59 + pkg/core/mpt/extension.go | 106 + pkg/core/mpt/hash.go | 86 + pkg/core/mpt/helpers.go | 98 + pkg/core/mpt/helpers_test.go | 67 + pkg/core/mpt/leaf.go | 85 + pkg/core/mpt/node.go | 122 + pkg/core/mpt/node_test.go | 157 + pkg/core/mpt/proof.go | 78 + pkg/core/mpt/proof_test.go | 76 + pkg/core/mpt/trie.go | 633 +++ pkg/core/mpt/trie_store.go | 125 + pkg/core/mpt/trie_store_test.go | 74 + pkg/core/mpt/trie_test.go | 693 +++ pkg/core/native/compatibility_test.go | 33 + pkg/core/native/contract.go | 277 + pkg/core/native/contract_test.go | 24 + pkg/core/native/crypto.go | 472 ++ pkg/core/native/crypto_blspoints.go | 293 ++ pkg/core/native/crypto_test.go | 395 ++ pkg/core/native/designate.go | 465 ++ pkg/core/native/doc.go | 4 + pkg/core/native/interop.go | 124 + pkg/core/native/invocation_test.go | 219 + pkg/core/native/ledger.go | 242 + pkg/core/native/management.go | 863 ++++ pkg/core/native/management_neotest_test.go | 138 + pkg/core/native/management_test.go | 194 + pkg/core/native/native_gas.go | 160 + pkg/core/native/native_neo.go | 1408 ++++++ pkg/core/native/native_neo_candidate.go | 45 + pkg/core/native/native_neo_test.go | 17 + pkg/core/native/native_nep17.go | 432 ++ pkg/core/native/native_test/common_test.go | 171 + pkg/core/native/native_test/cryptolib_test.go | 597 +++ .../cryptolib_verification_test.go | 891 ++++ pkg/core/native/native_test/designate_test.go | 185 + pkg/core/native/native_test/gas_test.go | 155 + .../helpers/policyhelper/policyhelper.go | 5 + .../helpers/policyhelper/policyhelper.yml | 4 + pkg/core/native/native_test/ledger_test.go | 299 ++ .../native/native_test/management_test.go | 1272 +++++ pkg/core/native/native_test/neo_test.go | 1019 ++++ pkg/core/native/native_test/notary_test.go | 325 ++ pkg/core/native/native_test/oracle_test.go | 179 + pkg/core/native/native_test/policy_test.go | 711 +++ pkg/core/native/native_test/std_test.go | 66 + pkg/core/native/native_test/treasury_test.go | 66 + pkg/core/native/nativehashes/gen.go | 70 + pkg/core/native/nativehashes/hashes.go | 34 + pkg/core/native/nativeids/gen.go | 66 + pkg/core/native/nativeids/ids.go | 32 + pkg/core/native/nativenames/names.go | 46 + pkg/core/native/nativenames_test.go | 19 + pkg/core/native/nativeprices/prices.go | 4 + pkg/core/native/neo_types.go | 110 + pkg/core/native/noderoles/role.go | 44 + pkg/core/native/noderoles/role_string.go | 41 + pkg/core/native/noderoles/role_test.go | 46 + pkg/core/native/notary.go | 476 ++ pkg/core/native/oracle.go | 607 +++ pkg/core/native/oracle_types.go | 86 + pkg/core/native/oracle_types_test.go | 63 + pkg/core/native/policy.go | 992 ++++ pkg/core/native/policy_test.go | 119 + pkg/core/native/std.go | 549 ++ pkg/core/native/std_test.go | 689 +++ pkg/core/native/treasury.go | 107 + pkg/core/native/util.go | 62 + pkg/core/native_designate_test.go | 99 + pkg/core/prometheus.go | 80 + pkg/core/state/contract.go | 122 + pkg/core/state/contract_invocation.go | 120 + pkg/core/state/contract_invocation_test.go | 51 + pkg/core/state/contract_test.go | 134 + pkg/core/state/deposit.go | 51 + pkg/core/state/deposit_test.go | 54 + pkg/core/state/mpt_root.go | 49 + pkg/core/state/mpt_root_test.go | 61 + pkg/core/state/native_state.go | 168 + pkg/core/state/native_state_test.go | 126 + pkg/core/state/notification_event.go | 362 ++ pkg/core/state/notification_event_test.go | 254 + pkg/core/state/oracle.go | 104 + pkg/core/state/oracle_test.go | 70 + pkg/core/state/storage_item.go | 4 + pkg/core/state/tokens.go | 224 + pkg/core/state/tokens_test.go | 135 + pkg/core/state/validator.go | 13 + pkg/core/stateroot/callbacks.go | 12 + pkg/core/stateroot/module.go | 383 ++ pkg/core/stateroot/prometheus.go | 20 + pkg/core/stateroot/store.go | 84 + pkg/core/stateroot/validators.go | 44 + pkg/core/statesync/module.go | 776 +++ pkg/core/statesync/module_test.go | 111 + pkg/core/statesync/mptpool.go | 129 + pkg/core/statesync/mptpool_test.go | 123 + pkg/core/statesync/neotest_test.go | 730 +++ pkg/core/statesync/storagesyncmode_string.go | 25 + pkg/core/storage/boltdb_store.go | 192 + pkg/core/storage/boltdb_store_test.go | 59 + pkg/core/storage/dbconfig/store_config.go | 24 + pkg/core/storage/dbconfig/store_type.go | 11 + pkg/core/storage/dboper/operation.go | 13 + pkg/core/storage/leveldb_store.go | 126 + pkg/core/storage/leveldb_store_test.go | 45 + pkg/core/storage/memcached_store.go | 446 ++ pkg/core/storage/memcached_store_test.go | 433 ++ pkg/core/storage/memory_store.go | 154 + pkg/core/storage/memory_store_test.go | 37 + pkg/core/storage/store.go | 178 + pkg/core/storage/store_test.go | 29 + pkg/core/storage/store_type_test.go | 29 + pkg/core/storage/storeandbatch_test.go | 246 + pkg/core/test_data/block_1.json | 55 + pkg/core/test_data/block_2.json | 57 + pkg/core/transaction/attribute.go | 128 + pkg/core/transaction/attribute_test.go | 216 + pkg/core/transaction/attrtype.go | 48 + pkg/core/transaction/attrtype_string.go | 40 + pkg/core/transaction/bench_test.go | 74 + pkg/core/transaction/conflicts.go | 32 + pkg/core/transaction/doc.go | 6 + pkg/core/transaction/fuzz_test.go | 24 + pkg/core/transaction/helper_test.go | 21 + pkg/core/transaction/not_valid_before.go | 31 + pkg/core/transaction/notary_assisted.go | 36 + pkg/core/transaction/oracle.go | 128 + pkg/core/transaction/oracle_test.go | 75 + .../transaction/oracleresponsecode_string.go | 61 + pkg/core/transaction/reserved.go | 31 + pkg/core/transaction/signer.go | 107 + pkg/core/transaction/signer_test.go | 65 + pkg/core/transaction/transaction.go | 560 +++ pkg/core/transaction/transaction_test.go | 389 ++ pkg/core/transaction/witness.go | 50 + pkg/core/transaction/witness_condition.go | 719 +++ .../transaction/witness_condition_test.go | 424 ++ pkg/core/transaction/witness_rule.go | 104 + pkg/core/transaction/witness_rule_test.go | 87 + pkg/core/transaction/witness_scope.go | 120 + pkg/core/transaction/witness_scope_string.go | 46 + pkg/core/transaction/witness_scope_test.go | 115 + pkg/core/transaction/witness_test.go | 51 + pkg/core/transaction/witnessaction_string.go | 25 + .../witnessconditiontype_string.go | 50 + pkg/core/util.go | 117 + pkg/core/util_test.go | 54 + pkg/crypto/doc.go | 6 + pkg/crypto/hash/doc.go | 6 + pkg/crypto/hash/hash.go | 78 + pkg/crypto/hash/hash_test.go | 66 + pkg/crypto/hash/merkle_bench_test.go | 30 + pkg/crypto/hash/merkle_tree.go | 115 + pkg/crypto/hash/merkle_tree_test.go | 103 + pkg/crypto/keys/aes256.go | 93 + pkg/crypto/keys/doc.go | 4 + pkg/crypto/keys/internal/scrypt/scrypt.go | 214 + .../keys/internal/scrypt/scrypt_test.go | 162 + pkg/crypto/keys/nep2.go | 156 + pkg/crypto/keys/nep2_test.go | 80 + pkg/crypto/keys/private_key.go | 185 + pkg/crypto/keys/private_key_test.go | 77 + pkg/crypto/keys/publickey.go | 419 ++ pkg/crypto/keys/publickey_test.go | 334 ++ pkg/crypto/keys/sign_verify_test.go | 100 + pkg/crypto/keys/wif.go | 87 + pkg/crypto/keys/wif_test.go | 100 + pkg/crypto/verifiable.go | 11 + pkg/encoding/address/address.go | 39 + pkg/encoding/address/address_test.go | 57 + pkg/encoding/address/doc.go | 4 + pkg/encoding/base58/base58.go | 39 + pkg/encoding/base58/base58_test.go | 41 + pkg/encoding/base58/doc.go | 4 + pkg/encoding/bigint/bench_test.go | 17 + pkg/encoding/bigint/bigint.go | 153 + pkg/encoding/bigint/bigint_test.go | 193 + pkg/encoding/bigint/doc.go | 4 + pkg/encoding/bigint/fuzz_test.go | 48 + pkg/encoding/fixedn/decimal.go | 80 + pkg/encoding/fixedn/decimal_test.go | 53 + pkg/encoding/fixedn/doc.go | 4 + pkg/encoding/fixedn/fixed8.go | 169 + pkg/encoding/fixedn/fixed8_test.go | 176 + pkg/interop/LICENSE.md | 22 + pkg/interop/contract/contract.go | 52 + pkg/interop/convert/convert.go | 148 + pkg/interop/crypto/crypto.go | 22 + pkg/interop/doc.go | 29 + pkg/interop/go.mod | 3 + pkg/interop/iterator/iterator.go | 31 + pkg/interop/lib/address/address.go | 34 + pkg/interop/lib/contract/contract.go | 23 + pkg/interop/math/math.go | 47 + pkg/interop/native/crypto/crypto.go | 117 + pkg/interop/native/gas/gas.go | 40 + pkg/interop/native/ledger/block.go | 58 + pkg/interop/native/ledger/ledger.go | 71 + pkg/interop/native/ledger/transaction.go | 27 + .../native/ledger/transaction_signer.go | 114 + pkg/interop/native/management/contract.go | 83 + pkg/interop/native/management/management.go | 93 + pkg/interop/native/neo/neo.go | 133 + pkg/interop/native/neo/neo_candidate.go | 9 + pkg/interop/native/notary/notary.go | 47 + pkg/interop/native/oracle/oracle.go | 82 + pkg/interop/native/policy/attrtype.go | 14 + pkg/interop/native/policy/policy.go | 130 + pkg/interop/native/roles/roles.go | 38 + pkg/interop/native/std/std.go | 198 + pkg/interop/neogointernal/call.go | 12 + pkg/interop/neogointernal/doc.go | 5 + pkg/interop/neogointernal/opcode.go | 28 + pkg/interop/neogointernal/syscall.go | 46 + pkg/interop/runtime/engine.go | 42 + pkg/interop/runtime/runtime.go | 137 + pkg/interop/storage/limits.go | 12 + pkg/interop/storage/storage.go | 121 + pkg/interop/types.go | 82 + pkg/interop/util/util.go | 46 + pkg/io/binaryBufWriter.go | 50 + pkg/io/binaryReader.go | 203 + pkg/io/binaryWriter.go | 159 + pkg/io/binaryrw_test.go | 488 ++ pkg/io/fileWriter.go | 18 + pkg/io/fileWriter_test.go | 32 + pkg/io/serializable.go | 20 + pkg/io/size.go | 96 + pkg/io/size_test.go | 207 + pkg/neorpc/errors.go | 324 ++ pkg/neorpc/errors_test.go | 35 + pkg/neorpc/events.go | 100 + pkg/neorpc/filters.go | 308 ++ pkg/neorpc/filters_test.go | 159 + pkg/neorpc/result/application_log.go | 84 + pkg/neorpc/result/block.go | 66 + pkg/neorpc/result/block_header.go | 57 + pkg/neorpc/result/block_notifications.go | 16 + pkg/neorpc/result/findstates.go | 13 + pkg/neorpc/result/findstorage.go | 10 + pkg/neorpc/result/invoke.go | 259 + pkg/neorpc/result/invoke_test.go | 91 + pkg/neorpc/result/mempool_event.go | 13 + pkg/neorpc/result/mpt.go | 108 + pkg/neorpc/result/mpt_test.go | 62 + pkg/neorpc/result/netfee.go | 6 + pkg/neorpc/result/notary_request_event.go | 13 + pkg/neorpc/result/peers.go | 97 + pkg/neorpc/result/peers_test.go | 49 + pkg/neorpc/result/raw_mempool.go | 10 + pkg/neorpc/result/raw_notary_pool.go | 48 + pkg/neorpc/result/relay_result.go | 8 + pkg/neorpc/result/tokens.go | 88 + pkg/neorpc/result/tx_raw_output.go | 62 + pkg/neorpc/result/unclaimed_gas.go | 50 + pkg/neorpc/result/validate_address.go | 9 + pkg/neorpc/result/validator.go | 51 + pkg/neorpc/result/validator_test.go | 22 + pkg/neorpc/result/version.go | 197 + pkg/neorpc/result/version_test.go | 157 + pkg/neorpc/rpcevent/filter.go | 134 + pkg/neorpc/rpcevent/filter_test.go | 438 ++ pkg/neorpc/types.go | 179 + pkg/neorpc/types_test.go | 92 + pkg/neotest/account.go | 9 + pkg/neotest/basic.go | 463 ++ pkg/neotest/chain/chain.go | 294 ++ pkg/neotest/chain/chain_test.go | 21 + pkg/neotest/chain/doc.go | 7 + pkg/neotest/client.go | 159 + pkg/neotest/compile.go | 127 + pkg/neotest/compile_test.go | 35 + pkg/neotest/coverage.go | 244 + pkg/neotest/doc.go | 36 + pkg/neotest/signer.go | 235 + pkg/neotest/signer_test.go | 48 + pkg/network/bqueue/operationmode_string.go | 25 + pkg/network/bqueue/queue.go | 223 + pkg/network/bqueue/queue_test.go | 111 + pkg/network/bqueue_adapters.go | 75 + pkg/network/capability/capability.go | 191 + pkg/network/capability/capability_test.go | 48 + pkg/network/capability/type.go | 25 + pkg/network/compress.go | 40 + pkg/network/discovery.go | 326 ++ pkg/network/discovery_test.go | 241 + pkg/network/extpool/pool.go | 139 + pkg/network/extpool/pool_test.go | 145 + pkg/network/fuzz_test.go | 24 + pkg/network/helper_test.go | 223 + pkg/network/message.go | 264 + pkg/network/message_string.go | 95 + pkg/network/message_test.go | 401 ++ pkg/network/notary_feer.go | 34 + pkg/network/payload/address.go | 91 + pkg/network/payload/address_test.go | 99 + pkg/network/payload/extensible.go | 92 + pkg/network/payload/extensible_test.go | 67 + pkg/network/payload/getblockbyindex.go | 36 + pkg/network/payload/getblockbyindex_test.go | 25 + pkg/network/payload/getblocks.go | 43 + pkg/network/payload/getblocks_test.go | 28 + pkg/network/payload/headers.go | 62 + pkg/network/payload/headers_test.go | 87 + pkg/network/payload/inventory.go | 70 + pkg/network/payload/inventory_test.go | 52 + pkg/network/payload/merkleblock.go | 44 + pkg/network/payload/merkleblock_test.go | 70 + pkg/network/payload/mptdata.go | 35 + pkg/network/payload/mptdata_test.go | 38 + pkg/network/payload/mptinventory.go | 32 + pkg/network/payload/mptinventory_test.go | 38 + pkg/network/payload/notary_request.go | 150 + pkg/network/payload/notary_request_test.go | 255 + pkg/network/payload/payload.go | 26 + pkg/network/payload/ping.go | 40 + pkg/network/payload/ping_test.go | 19 + pkg/network/payload/version.go | 60 + pkg/network/payload/version_test.go | 64 + pkg/network/peer.go | 108 + pkg/network/prometheus.go | 120 + pkg/network/server.go | 1939 +++++++ pkg/network/server_config.go | 128 + pkg/network/server_test.go | 1169 +++++ pkg/network/state_sync.go | 27 + pkg/network/tcp_peer.go | 528 ++ pkg/network/tcp_peer_test.go | 88 + pkg/network/tcp_transport.go | 115 + pkg/network/transport.go | 13 + pkg/rpcclient/actor/actor.go | 329 ++ pkg/rpcclient/actor/actor_test.go | 339 ++ pkg/rpcclient/actor/compat_test.go | 13 + pkg/rpcclient/actor/doc_test.go | 122 + pkg/rpcclient/actor/maker.go | 235 + pkg/rpcclient/actor/maker_test.go | 217 + pkg/rpcclient/client.go | 252 + pkg/rpcclient/client_test.go | 18 + pkg/rpcclient/doc.go | 123 + pkg/rpcclient/doc_test.go | 45 + pkg/rpcclient/gas/gas.go | 26 + pkg/rpcclient/gas/gas_test.go | 40 + pkg/rpcclient/invoker/compat_test.go | 16 + pkg/rpcclient/invoker/doc_test.go | 101 + pkg/rpcclient/invoker/invoker.go | 248 + pkg/rpcclient/invoker/invoker_test.go | 216 + pkg/rpcclient/local.go | 76 + pkg/rpcclient/local_test.go | 38 + pkg/rpcclient/management/management.go | 350 ++ pkg/rpcclient/management/management_test.go | 429 ++ pkg/rpcclient/neo/doc_test.go | 90 + pkg/rpcclient/neo/neo.go | 474 ++ pkg/rpcclient/neo/neo_test.go | 569 +++ pkg/rpcclient/nep11/base.go | 317 ++ pkg/rpcclient/nep11/base_test.go | 341 ++ pkg/rpcclient/nep11/divisible.go | 147 + pkg/rpcclient/nep11/divisible_test.go | 238 + pkg/rpcclient/nep11/doc_test.go | 167 + pkg/rpcclient/nep11/nondivisible.go | 34 + pkg/rpcclient/nep11/nondivisible_test.go | 49 + pkg/rpcclient/nep17/doc_test.go | 67 + pkg/rpcclient/nep17/nep17.go | 215 + pkg/rpcclient/nep17/nep17_test.go | 171 + pkg/rpcclient/nep22/nep22.go | 79 + pkg/rpcclient/nep22/nep22_test.go | 111 + pkg/rpcclient/nep24/doc_test.go | 32 + pkg/rpcclient/nep24/royalty.go | 185 + pkg/rpcclient/nep24/royalty_test.go | 300 ++ pkg/rpcclient/nep31/nep31.go | 75 + pkg/rpcclient/nep31/nep31_test.go | 71 + pkg/rpcclient/neptoken/base.go | 72 + pkg/rpcclient/neptoken/base_test.go | 167 + pkg/rpcclient/neptoken/info.go | 47 + pkg/rpcclient/neptoken/info_test.go | 152 + pkg/rpcclient/nns/contract.go | 549 ++ pkg/rpcclient/nns/contract_test.go | 768 +++ pkg/rpcclient/nns/iterators.go | 90 + pkg/rpcclient/nns/record.go | 80 + pkg/rpcclient/nns/record_test.go | 39 + pkg/rpcclient/notary/accounts.go | 59 + pkg/rpcclient/notary/accounts_test.go | 36 + pkg/rpcclient/notary/actor.go | 358 ++ pkg/rpcclient/notary/actor_test.go | 608 +++ pkg/rpcclient/notary/contract.go | 254 + pkg/rpcclient/notary/contract_test.go | 245 + pkg/rpcclient/notary/doc_test.go | 89 + pkg/rpcclient/oracle/oracle.go | 112 + pkg/rpcclient/oracle/oracle_test.go | 86 + pkg/rpcclient/policy/policy.go | 601 +++ pkg/rpcclient/policy/policy_test.go | 449 ++ pkg/rpcclient/rolemgmt/roles.go | 152 + pkg/rpcclient/rolemgmt/roles_test.go | 194 + pkg/rpcclient/rpc.go | 1069 ++++ pkg/rpcclient/rpc_test.go | 2305 +++++++++ pkg/rpcclient/unwrap/unwrap.go | 477 ++ pkg/rpcclient/unwrap/unwrap_test.go | 479 ++ pkg/rpcclient/waiter/waiter.go | 407 ++ pkg/rpcclient/waiter/waiter_test.go | 266 + pkg/rpcclient/wsclient.go | 1129 +++++ pkg/rpcclient/wsclient_test.go | 1053 ++++ pkg/services/blockfetcher/blockfetcher.go | 455 ++ .../blockfetcher/blockfetcher_test.go | 129 + .../blockfetcher/operationmode_string.go | 25 + pkg/services/helpers/neofs/blockstorage.go | 153 + pkg/services/helpers/neofs/neofs.go | 320 ++ pkg/services/helpers/neofs/neofs_test.go | 71 + .../helpers/rpcbroadcaster/broadcaster.go | 80 + pkg/services/helpers/rpcbroadcaster/client.go | 78 + pkg/services/metrics/metrics.go | 79 + pkg/services/metrics/pprof.go | 36 + pkg/services/metrics/prometheus.go | 29 + pkg/services/notary/core_test.go | 805 +++ pkg/services/notary/node.go | 52 + pkg/services/notary/node_test.go | 76 + pkg/services/notary/notary.go | 574 +++ pkg/services/notary/notary_test.go | 490 ++ pkg/services/notary/request_type.go | 13 + pkg/services/notary/testdata/notary1.json | 64 + pkg/services/notary/testdata/notary2.json | 30 + pkg/services/oracle/broadcaster/oracle.go | 63 + pkg/services/oracle/filter.go | 39 + pkg/services/oracle/filter_test.go | 83 + pkg/services/oracle/jsonpath/jsonpath.go | 465 ++ pkg/services/oracle/jsonpath/jsonpath_test.go | 336 ++ pkg/services/oracle/network.go | 98 + pkg/services/oracle/network_test.go | 47 + pkg/services/oracle/nodes.go | 62 + pkg/services/oracle/oracle.go | 292 ++ pkg/services/oracle/oracle_test.go | 587 +++ pkg/services/oracle/request.go | 286 ++ pkg/services/oracle/request_test.go | 19 + pkg/services/oracle/response.go | 202 + pkg/services/oracle/testdata/oracle1.json | 30 + pkg/services/oracle/testdata/oracle2.json | 30 + pkg/services/oracle/transaction.go | 118 + pkg/services/rpcsrv/client_test.go | 2975 +++++++++++ pkg/services/rpcsrv/error.go | 36 + pkg/services/rpcsrv/local_test.go | 53 + .../rpcsrv/notification_comparator.go | 65 + pkg/services/rpcsrv/params/param.go | 509 ++ pkg/services/rpcsrv/params/param_test.go | 576 +++ pkg/services/rpcsrv/params/params.go | 39 + pkg/services/rpcsrv/params/params_test.go | 31 + pkg/services/rpcsrv/params/txBuilder.go | 176 + pkg/services/rpcsrv/params/tx_builder_test.go | 168 + pkg/services/rpcsrv/params/types.go | 110 + pkg/services/rpcsrv/params/types_test.go | 47 + pkg/services/rpcsrv/prometheus.go | 40 + pkg/services/rpcsrv/server.go | 3413 +++++++++++++ pkg/services/rpcsrv/server_helper_test.go | 178 + pkg/services/rpcsrv/server_test.go | 4464 +++++++++++++++++ pkg/services/rpcsrv/subscription.go | 58 + pkg/services/rpcsrv/subscription_test.go | 854 ++++ pkg/services/rpcsrv/testdata/testblocks.acc | Bin 0 -> 36535 bytes pkg/services/rpcsrv/tokens.go | 27 + pkg/services/rpcsrv/util.go | 20 + pkg/services/statefetcher/statefetcher.go | 385 ++ .../statefetcher/statefetcher_test.go | 83 + pkg/services/stateroot/message.go | 53 + pkg/services/stateroot/network.go | 111 + pkg/services/stateroot/service.go | 189 + pkg/services/stateroot/service_test.go | 333 ++ pkg/services/stateroot/signature.go | 108 + pkg/services/stateroot/validators.go | 158 + pkg/services/stateroot/vote.go | 27 + pkg/smartcontract/binding/generate.go | 356 ++ pkg/smartcontract/binding/generate_test.go | 229 + pkg/smartcontract/binding/override.go | 77 + pkg/smartcontract/binding/override_test.go | 32 + pkg/smartcontract/builder.go | 88 + pkg/smartcontract/builder_test.go | 23 + pkg/smartcontract/callflag/call_flags.go | 139 + pkg/smartcontract/callflag/call_flags_test.go | 92 + pkg/smartcontract/context/context.go | 239 + pkg/smartcontract/context/context_test.go | 268 + pkg/smartcontract/context/item.go | 24 + pkg/smartcontract/context/item_test.go | 58 + pkg/smartcontract/contract.go | 65 + pkg/smartcontract/contract_test.go | 129 + pkg/smartcontract/doc.go | 10 + pkg/smartcontract/doc_test.go | 63 + pkg/smartcontract/entry.go | 128 + pkg/smartcontract/manifest/abi.go | 147 + pkg/smartcontract/manifest/abi_test.go | 41 + pkg/smartcontract/manifest/container.go | 104 + pkg/smartcontract/manifest/container_test.go | 151 + pkg/smartcontract/manifest/event.go | 77 + pkg/smartcontract/manifest/event_test.go | 76 + pkg/smartcontract/manifest/group.go | 144 + pkg/smartcontract/manifest/group_test.go | 65 + pkg/smartcontract/manifest/manifest.go | 303 ++ pkg/smartcontract/manifest/manifest_test.go | 506 ++ pkg/smartcontract/manifest/method.go | 94 + pkg/smartcontract/manifest/method_test.go | 105 + pkg/smartcontract/manifest/parameter.go | 109 + pkg/smartcontract/manifest/parameter_test.go | 61 + pkg/smartcontract/manifest/permission.go | 329 ++ pkg/smartcontract/manifest/permission_test.go | 216 + pkg/smartcontract/manifest/standard/check.go | 17 + pkg/smartcontract/manifest/standard/comply.go | 186 + .../manifest/standard/comply_test.go | 212 + pkg/smartcontract/manifest/standard/doc.go | 5 + pkg/smartcontract/manifest/standard/nep11.go | 127 + pkg/smartcontract/manifest/standard/nep17.go | 45 + pkg/smartcontract/manifest/standard/nep22.go | 25 + pkg/smartcontract/manifest/standard/nep24.go | 51 + pkg/smartcontract/manifest/standard/nep26.go | 24 + pkg/smartcontract/manifest/standard/nep27.go | 23 + pkg/smartcontract/manifest/standard/nep29.go | 24 + pkg/smartcontract/manifest/standard/nep30.go | 23 + pkg/smartcontract/manifest/standard/nep31.go | 21 + pkg/smartcontract/manifest/standard/token.go | 34 + pkg/smartcontract/nef/method_token.go | 57 + pkg/smartcontract/nef/method_token_test.go | 49 + pkg/smartcontract/nef/nef.go | 198 + pkg/smartcontract/nef/nef_test.go | 192 + pkg/smartcontract/param_type.go | 432 ++ pkg/smartcontract/param_type_test.go | 484 ++ pkg/smartcontract/parameter.go | 438 ++ pkg/smartcontract/parameter_test.go | 975 ++++ pkg/smartcontract/rpcbinding/binding.go | 1283 +++++ pkg/smartcontract/scparser/bench_test.go | 17 + pkg/smartcontract/scparser/context.go | 172 + pkg/smartcontract/scparser/contract_checks.go | 213 + .../scparser/contract_checks_test.go | 284 ++ pkg/smartcontract/scparser/doc.go | 7 + pkg/smartcontract/scparser/fuzz_test.go | 81 + .../adjustValToType_filebytes_good.txt | 1 + pkg/smartcontract/trigger/trigger_type.go | 53 + .../trigger/trigger_type_string.go | 43 + .../trigger/trigger_type_test.go | 67 + pkg/smartcontract/zkpbinding/binding.go | 332 ++ pkg/util/bench_test.go | 13 + pkg/util/bitfield/bitfield.go | 70 + pkg/util/bitfield/bitfield_test.go | 46 + pkg/util/uint160.go | 177 + pkg/util/uint160_test.go | 141 + pkg/util/uint256.go | 159 + pkg/util/uint256_test.go | 133 + pkg/vm/bench_test.go | 50 + pkg/vm/context.go | 220 + pkg/vm/debug_test.go | 68 + pkg/vm/emit/emit.go | 339 ++ pkg/vm/emit/emit_test.go | 651 +++ pkg/vm/exception.go | 85 + pkg/vm/fuzz_test.go | 47 + pkg/vm/invocation_tree_test.go | 70 + pkg/vm/invocations/invocation_tree.go | 12 + pkg/vm/iterator_test.go | 121 + pkg/vm/json_test.go | 482 ++ pkg/vm/opcode/from_string.go | 20 + pkg/vm/opcode/isvalid_test.go | 15 + pkg/vm/opcode/opcode.go | 246 + pkg/vm/opcode/opcode_string.go | 415 ++ pkg/vm/opcode/opcode_test.go | 37 + pkg/vm/opcodebench_test.go | 787 +++ pkg/vm/ref_counter.go | 74 + pkg/vm/ref_counter_test.go | 80 + pkg/vm/slot.go | 64 + pkg/vm/slot_test.go | 27 + pkg/vm/stack.go | 379 ++ pkg/vm/stack_test.go | 393 ++ pkg/vm/stackitem/immutable.go | 21 + pkg/vm/stackitem/item.go | 1274 +++++ pkg/vm/stackitem/item_test.go | 604 +++ pkg/vm/stackitem/json.go | 545 ++ pkg/vm/stackitem/json_test.go | 485 ++ pkg/vm/stackitem/reference.go | 15 + pkg/vm/stackitem/serialization.go | 400 ++ pkg/vm/stackitem/serialization_test.go | 328 ++ pkg/vm/stackitem/type.go | 90 + pkg/vm/stackitem/type_test.go | 16 + pkg/vm/vm.go | 2095 ++++++++ pkg/vm/vm_test.go | 2981 +++++++++++ pkg/vm/vmstate/state.go | 88 + pkg/vm/vmstate/state_test.go | 97 + pkg/wallet/account.go | 329 ++ pkg/wallet/account_test.go | 243 + pkg/wallet/regenerate_test.go | 286 ++ pkg/wallet/testdata/wallet1.json | 55 + pkg/wallet/testdata/wallet2.json | 72 + pkg/wallet/token.go | 31 + pkg/wallet/token_test.go | 30 + pkg/wallet/wallet.go | 252 + pkg/wallet/wallet_test.go | 230 + scripts/check_deps.sh | 35 + scripts/compare-dumps/compare-dumps.go | 192 + scripts/compare-dumps/compare-dumps_test.go | 19 + scripts/compare-states/compare-states.go | 181 + scripts/gendump/main.go | 182 + scripts/go.mod | 62 + scripts/go.sum | 282 ++ scripts/update_deps.sh | 29 + tutus.service.template | 18 + 1080 files changed, 209043 insertions(+) create mode 100644 .docker/docker-compose.yml create mode 100644 .docker/privnet-entrypoint.ps1 create mode 100755 .docker/privnet-entrypoint.sh create mode 100644 .docker/wallets/wallet1.json create mode 100644 .docker/wallets/wallet1_solo.json create mode 100644 .docker/wallets/wallet2.json create mode 100644 .docker/wallets/wallet3.json create mode 100644 .docker/wallets/wallet4.json create mode 100644 .dockerignore create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/logo_dark.png create mode 100644 .github/logo_light.png create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/contribution_guidelines.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 Dockerfile.wsc create mode 100644 LICENSE.md create mode 100644 Makefile create mode 100644 README.md create mode 100644 ROADMAP.md create mode 100644 cli/.gitignore create mode 100644 cli/app/app.go create mode 100644 cli/app/main_test.go create mode 100644 cli/cmdargs/parser.go create mode 100644 cli/cmdargs/parser_test.go create mode 100644 cli/flags/address.go create mode 100644 cli/flags/address_test.go create mode 100644 cli/flags/fixed8.go create mode 100644 cli/flags/fixed8_test.go create mode 100644 cli/input/input.go create mode 100644 cli/input/readpass_unix.go create mode 100644 cli/input/readpass_windows.go create mode 100644 cli/main.go create mode 100644 cli/nep_test/nep11_test.go create mode 100644 cli/nep_test/nep17_test.go create mode 100644 cli/options/cli_options_test.go create mode 100644 cli/options/filtering_core.go create mode 100644 cli/options/options.go create mode 100644 cli/options/options_test.go create mode 100644 cli/paramcontext/context.go create mode 100644 cli/query/query.go create mode 100644 cli/query/query_test.go create mode 100644 cli/server/cli_dump_test.go create mode 100644 cli/server/cli_server_test.go create mode 100644 cli/server/dump.go create mode 100644 cli/server/dump_bin.go create mode 100644 cli/server/dump_bin_test.go create mode 100644 cli/server/dump_test.go create mode 100644 cli/server/metrics.go create mode 100644 cli/server/server.go create mode 100644 cli/server/server_test.go create mode 100644 cli/server/signals_unix.go create mode 100644 cli/server/signals_windows.go create mode 100644 cli/smartcontract/contract_test.go create mode 100644 cli/smartcontract/generate.go create mode 100644 cli/smartcontract/generate_test.go create mode 100644 cli/smartcontract/manifest.go create mode 100644 cli/smartcontract/permission.go create mode 100644 cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/nft-nd/dynamic_hash/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go create mode 100755 cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go create mode 100755 cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go create mode 100755 cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go create mode 100755 cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go create mode 100644 cli/smartcontract/rpcbindings/verify/rpcbindings_test.go create mode 100644 cli/smartcontract/smart_contract.go create mode 100644 cli/smartcontract/smart_contract_test.go create mode 100644 cli/smartcontract/testdata/deploy/main.go create mode 100644 cli/smartcontract/testdata/deploy/neo-go.yml create mode 100644 cli/smartcontract/testdata/deploy/sub/put.go create mode 100755 cli/smartcontract/testdata/deploy/update.manifest.json create mode 100644 cli/smartcontract/testdata/deploy/updated.go create mode 100644 cli/smartcontract/testdata/gas/gas.go create mode 100644 cli/smartcontract/testdata/gas/gas.manifest.json create mode 100644 cli/smartcontract/testdata/invalid1/invalid.go create mode 100644 cli/smartcontract/testdata/invalid1/invalid.yml create mode 100644 cli/smartcontract/testdata/invalid2/invalid.go create mode 100644 cli/smartcontract/testdata/invalid2/invalid.yml create mode 100644 cli/smartcontract/testdata/invalid3/invalid.go create mode 100644 cli/smartcontract/testdata/invalid3/invalid.yml create mode 100644 cli/smartcontract/testdata/invalid4/invalid.go create mode 100644 cli/smartcontract/testdata/invalid4/invalid.yml create mode 100644 cli/smartcontract/testdata/nameservice/nns.go create mode 100644 cli/smartcontract/testdata/nameservice/nns.manifest.json create mode 100644 cli/smartcontract/testdata/nex/nex.go create mode 100644 cli/smartcontract/testdata/nex/nex.manifest.json create mode 100644 cli/smartcontract/testdata/nonepiter/iter.go create mode 100644 cli/smartcontract/testdata/nonepiter/iter.manifest.json create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid1/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid2/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid3/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid4/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid5/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid6/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid7/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid8/invalid.go create mode 100644 cli/smartcontract/testdata/rpcbindings/invalid8/invalid.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/notifications/config.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/notifications/config_extended.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/notifications/config_guessed.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/notifications/notifications.go create mode 100644 cli/smartcontract/testdata/rpcbindings/royalty/config.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/royalty/royalty.go create mode 100644 cli/smartcontract/testdata/rpcbindings/structs/config.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/structs/structs.go create mode 100644 cli/smartcontract/testdata/rpcbindings/types/config.yml create mode 100644 cli/smartcontract/testdata/rpcbindings/types/types.go create mode 100755 cli/smartcontract/testdata/verify.bindings.yml create mode 100644 cli/smartcontract/testdata/verify.go create mode 100755 cli/smartcontract/testdata/verify.manifest.json create mode 100755 cli/smartcontract/testdata/verify.nef create mode 100644 cli/smartcontract/testdata/verify.yml create mode 100755 cli/smartcontract/testdata/verifyrpc/verify.manifest.json create mode 100644 cli/testdata/testwallet.json create mode 100644 cli/testdata/testwallet_multi.json create mode 100644 cli/testdata/wallet1_solo.json create mode 100644 cli/txctx/tx.go create mode 100644 cli/util/audit-bin.go create mode 100644 cli/util/cancel.go create mode 100644 cli/util/convert.go create mode 100644 cli/util/dump.go create mode 100644 cli/util/send.go create mode 100644 cli/util/upload_bin.go create mode 100644 cli/util/upload_state.go create mode 100644 cli/util/util_test.go create mode 100644 cli/vm/cli.go create mode 100644 cli/vm/cli_test.go create mode 100644 cli/vm/vm.go create mode 100644 cli/wallet/candidate_test.go create mode 100644 cli/wallet/legacy.go create mode 100644 cli/wallet/legacy_test.go create mode 100644 cli/wallet/multisig.go create mode 100644 cli/wallet/multisig_test.go create mode 100644 cli/wallet/nep11.go create mode 100644 cli/wallet/nep17.go create mode 100644 cli/wallet/validator.go create mode 100644 cli/wallet/wallet.go create mode 100644 cli/wallet/wallet_test.go create mode 100644 codecov.yml create mode 100644 config/config_embed.go create mode 100644 config/protocol.mainnet.neofs.yml create mode 100644 config/protocol.mainnet.yml create mode 100644 config/protocol.privnet.docker.four.yml create mode 100644 config/protocol.privnet.docker.one.yml create mode 100644 config/protocol.privnet.docker.single.yml create mode 100644 config/protocol.privnet.docker.three.yml create mode 100644 config/protocol.privnet.docker.two.yml create mode 100644 config/protocol.privnet.yml create mode 100644 config/protocol.testnet.neofs.yml create mode 100644 config/protocol.testnet.yml create mode 100644 config/protocol.tutus.testnet.yml create mode 100644 config/protocol.tutus.yml create mode 100644 config/protocol.unit_testnet.single.yml create mode 100644 config/protocol.unit_testnet.yml create mode 100644 docs/cli.md create mode 100644 docs/compiler.md create mode 100644 docs/consensus.md create mode 100644 docs/conventions.md create mode 100644 docs/labels.md create mode 100644 docs/neofs-blockstorage.md create mode 100644 docs/node-configuration.md create mode 100644 docs/notary.md create mode 100644 docs/notifications.md create mode 100644 docs/oracle.md create mode 100644 docs/release-instruction.md create mode 100644 docs/rpc.md create mode 100644 docs/stateroots.md create mode 100644 docs/vm.md create mode 100644 examples/README.md create mode 100644 examples/engine/engine.go create mode 100644 examples/engine/engine.yml create mode 100644 examples/engine/go.mod create mode 100644 examples/engine/go.sum create mode 100644 examples/events/events.go create mode 100644 examples/events/events.yml create mode 100644 examples/events/go.mod create mode 100644 examples/events/go.sum create mode 100644 examples/iterator/go.mod create mode 100644 examples/iterator/go.sum create mode 100644 examples/iterator/iterator.go create mode 100644 examples/iterator/iterator.yml create mode 100644 examples/my_wallet.json create mode 100644 examples/neofs/go.mod create mode 100644 examples/neofs/go.sum create mode 100644 examples/neofs/neofs.go create mode 100644 examples/neofs/neofs.yml create mode 100644 examples/nft-d/go.mod create mode 100644 examples/nft-d/go.sum create mode 100644 examples/nft-d/nft.go create mode 100644 examples/nft-d/nft.yml create mode 100644 examples/nft-nd-nns/go.mod create mode 100644 examples/nft-nd-nns/go.sum create mode 100644 examples/nft-nd-nns/namestate.go create mode 100644 examples/nft-nd-nns/nns.go create mode 100644 examples/nft-nd-nns/nns.yml create mode 100644 examples/nft-nd-nns/nns_test.go create mode 100644 examples/nft-nd-nns/recordtype.go create mode 100644 examples/nft-nd/go.mod create mode 100644 examples/nft-nd/go.sum create mode 100644 examples/nft-nd/nft.go create mode 100644 examples/nft-nd/nft.yml create mode 100644 examples/oracle/go.mod create mode 100644 examples/oracle/go.sum create mode 100644 examples/oracle/oracle.go create mode 100644 examples/oracle/oracle.yml create mode 100644 examples/runtime/go.mod create mode 100644 examples/runtime/go.sum create mode 100644 examples/runtime/runtime.go create mode 100644 examples/runtime/runtime.yml create mode 100644 examples/storage/go.mod create mode 100644 examples/storage/go.sum create mode 100644 examples/storage/storage.go create mode 100644 examples/storage/storage.yml create mode 100644 examples/timer/go.mod create mode 100644 examples/timer/go.sum create mode 100644 examples/timer/timer.go create mode 100644 examples/timer/timer.yml create mode 100644 examples/token/go.mod create mode 100644 examples/token/go.sum create mode 100644 examples/token/nep17/nep17.go create mode 100644 examples/token/token.go create mode 100644 examples/token/token.yml create mode 100644 examples/zkp/cubic_circuit/README.md create mode 100644 examples/zkp/cubic_circuit/go.mod create mode 100644 examples/zkp/cubic_circuit/go.sum create mode 100644 examples/zkp/cubic_circuit/main.go create mode 100644 examples/zkp/cubic_circuit/main_test.go create mode 100644 examples/zkp/cubic_circuit/response8 create mode 100644 examples/zkp/xor_compat/go.mod create mode 100644 examples/zkp/xor_compat/go.sum create mode 100644 examples/zkp/xor_compat/verify.go create mode 100644 examples/zkp/xor_compat/verify.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/basicchain/basic.go create mode 100644 internal/basicchain/testdata/invoke/invoke.yml create mode 100644 internal/basicchain/testdata/invoke/invokescript_contract.go create mode 100644 internal/basicchain/testdata/storage/storage_contract.go create mode 100644 internal/basicchain/testdata/storage/storage_contract.yml create mode 100644 internal/basicchain/testdata/test_contract.go create mode 100644 internal/basicchain/testdata/test_contract.yml create mode 100644 internal/basicchain/testdata/verify/verification_contract.go create mode 100644 internal/basicchain/testdata/verify/verification_contract.yml create mode 100644 internal/basicchain/testdata/verify_args/verification_with_args_contract.go create mode 100644 internal/basicchain/testdata/verify_args/verification_with_args_contract.yml create mode 100644 internal/contracts/contracts.go create mode 100644 internal/contracts/contracts_test.go create mode 100644 internal/contracts/management_helper/README.md create mode 100644 internal/contracts/management_helper/management_helper1.manifest.json create mode 100644 internal/contracts/management_helper/management_helper1.nef create mode 100644 internal/contracts/management_helper/management_helper2.manifest.json create mode 100644 internal/contracts/management_helper/management_helper2.nef create mode 100644 internal/contracts/oracle_contract/README.md create mode 100644 internal/contracts/oracle_contract/go.mod create mode 100644 internal/contracts/oracle_contract/go.sum create mode 100644 internal/contracts/oracle_contract/oracle.go create mode 100644 internal/contracts/oracle_contract/oracle.manifest.json create mode 100644 internal/contracts/oracle_contract/oracle.nef create mode 100644 internal/contracts/oracle_contract/oracle.yml create mode 100644 internal/fakechain/fakechain.go create mode 100644 internal/keytestcases/testcases.go create mode 100644 internal/random/random_util.go create mode 100644 internal/testchain/address.go create mode 100644 internal/testchain/network.go create mode 100644 internal/testchain/transaction.go create mode 100644 internal/testcli/executor.go create mode 100644 internal/testserdes/testing.go create mode 100644 internal/versionutil/init.go create mode 100644 pkg/compiler/analysis.go create mode 100644 pkg/compiler/assign_test.go create mode 100644 pkg/compiler/binary_expr_test.go create mode 100644 pkg/compiler/builtin_test.go create mode 100644 pkg/compiler/byte_conversion_test.go create mode 100644 pkg/compiler/codegen.go create mode 100644 pkg/compiler/codegen_test.go create mode 100644 pkg/compiler/compiler.go create mode 100644 pkg/compiler/compiler_test.go create mode 100644 pkg/compiler/constant_test.go create mode 100644 pkg/compiler/convert_test.go create mode 100644 pkg/compiler/debug.go create mode 100644 pkg/compiler/debug_test.go create mode 100644 pkg/compiler/defer_test.go create mode 100644 pkg/compiler/doc.go create mode 100644 pkg/compiler/for_test.go create mode 100644 pkg/compiler/func_scope.go create mode 100644 pkg/compiler/function_call_test.go create mode 100644 pkg/compiler/generics_test.go create mode 100644 pkg/compiler/global_test.go create mode 100644 pkg/compiler/if_test.go create mode 100644 pkg/compiler/import_test.go create mode 100644 pkg/compiler/init_test.go create mode 100644 pkg/compiler/inline.go create mode 100644 pkg/compiler/inline_test.go create mode 100644 pkg/compiler/interop_test.go create mode 100644 pkg/compiler/jumps_test.go create mode 100644 pkg/compiler/lambda_test.go create mode 100644 pkg/compiler/limit_test.go create mode 100644 pkg/compiler/map_test.go create mode 100644 pkg/compiler/native_test.go create mode 100644 pkg/compiler/nilcheck_test.go create mode 100644 pkg/compiler/numeric_test.go create mode 100644 pkg/compiler/panic_test.go create mode 100644 pkg/compiler/pointer_test.go create mode 100644 pkg/compiler/return_test.go create mode 100644 pkg/compiler/slice_test.go create mode 100644 pkg/compiler/struct_test.go create mode 100644 pkg/compiler/switch_test.go create mode 100644 pkg/compiler/syscall_test.go create mode 100644 pkg/compiler/testdata/bar/bar.go create mode 100644 pkg/compiler/testdata/block/block.go create mode 100644 pkg/compiler/testdata/compile/test.go create mode 100644 pkg/compiler/testdata/constonly/const.go create mode 100644 pkg/compiler/testdata/foo/foo.go create mode 100644 pkg/compiler/testdata/foobar/bar.go create mode 100644 pkg/compiler/testdata/foobar/foo.go create mode 100644 pkg/compiler/testdata/globalvar/funccall/main.go create mode 100644 pkg/compiler/testdata/globalvar/main.go create mode 100644 pkg/compiler/testdata/globalvar/nested1/main.go create mode 100644 pkg/compiler/testdata/globalvar/nested2/main.go create mode 100644 pkg/compiler/testdata/globalvar/nested3/main.go create mode 100644 pkg/compiler/testdata/importcycle/pkg1/pkg1.go create mode 100644 pkg/compiler/testdata/importcycle/pkg2/pkg2.go create mode 100644 pkg/compiler/testdata/importcycle/pkg3/pkg3.go create mode 100644 pkg/compiler/testdata/inline/a/a.go create mode 100644 pkg/compiler/testdata/inline/b/b.go create mode 100644 pkg/compiler/testdata/inline/c/null.go create mode 100644 pkg/compiler/testdata/inline/d/d.go create mode 100644 pkg/compiler/testdata/inline/inline.go create mode 100644 pkg/compiler/testdata/method/struct.go create mode 100644 pkg/compiler/testdata/multi/file1.go create mode 100644 pkg/compiler/testdata/multi/file2.go create mode 100644 pkg/compiler/testdata/nestedcall/call.go create mode 100644 pkg/compiler/testdata/nestedcall/inner/call.go create mode 100644 pkg/compiler/testdata/notify/event.go create mode 100644 pkg/compiler/testdata/pkg1/pkg1.go create mode 100644 pkg/compiler/testdata/pkg2/pkg2.go create mode 100644 pkg/compiler/testdata/pkg3/pkg3.go create mode 100644 pkg/compiler/testdata/runh/hash.go create mode 100644 pkg/compiler/testdata/strange/normal.go create mode 100644 pkg/compiler/testdata/types/types.go create mode 100644 pkg/compiler/testdata/util/equals.go create mode 100644 pkg/compiler/type_test.go create mode 100644 pkg/compiler/types.go create mode 100644 pkg/compiler/vardecl_test.go create mode 100644 pkg/compiler/vars.go create mode 100644 pkg/compiler/verify_test.go create mode 100644 pkg/compiler/vm_test.go create mode 100644 pkg/config/application_config.go create mode 100644 pkg/config/application_config_test.go create mode 100644 pkg/config/basic_service.go create mode 100644 pkg/config/config.go create mode 100644 pkg/config/config_test.go create mode 100644 pkg/config/consensus.go create mode 100644 pkg/config/doc.go create mode 100644 pkg/config/genesis_extensions.go create mode 100644 pkg/config/hardfork.go create mode 100644 pkg/config/hardfork_string.go create mode 100644 pkg/config/internal_service.go create mode 100644 pkg/config/ledger_config.go create mode 100644 pkg/config/ledger_config_test.go create mode 100644 pkg/config/limits/limits.go create mode 100644 pkg/config/logger.go create mode 100644 pkg/config/neofsservice_config.go create mode 100644 pkg/config/netmode/doc.go create mode 100644 pkg/config/netmode/netmode.go create mode 100644 pkg/config/notary_config.go create mode 100644 pkg/config/oracle_config.go create mode 100644 pkg/config/p2p.go create mode 100644 pkg/config/protocol_config.go create mode 100644 pkg/config/protocol_config_test.go create mode 100644 pkg/config/rpc_config.go create mode 100644 pkg/config/state_root.go create mode 100644 pkg/config/wallet_config.go create mode 100644 pkg/consensus/block.go create mode 100644 pkg/consensus/block_test.go create mode 100644 pkg/consensus/cache.go create mode 100644 pkg/consensus/cache_test.go create mode 100644 pkg/consensus/change_view.go create mode 100644 pkg/consensus/change_view_test.go create mode 100644 pkg/consensus/commit.go create mode 100644 pkg/consensus/commit_test.go create mode 100644 pkg/consensus/consensus.go create mode 100644 pkg/consensus/consensus_test.go create mode 100644 pkg/consensus/crypto_test.go create mode 100644 pkg/consensus/doc.go create mode 100644 pkg/consensus/payload.go create mode 100644 pkg/consensus/payload_test.go create mode 100644 pkg/consensus/prepare_request.go create mode 100644 pkg/consensus/prepare_request_test.go create mode 100644 pkg/consensus/prepare_response.go create mode 100644 pkg/consensus/prepare_response_test.go create mode 100644 pkg/consensus/recovery_message.go create mode 100644 pkg/consensus/recovery_message_test.go create mode 100644 pkg/consensus/recovery_request.go create mode 100644 pkg/consensus/recovery_request_test.go create mode 100644 pkg/consensus/testdata/wallet1.json create mode 100644 pkg/consensus/testdata/wallet2.json create mode 100644 pkg/consensus/testdata/wallet3.json create mode 100644 pkg/consensus/testdata/wallet4.json create mode 100644 pkg/core/basic_chain_test.go create mode 100644 pkg/core/bench_test.go create mode 100644 pkg/core/block/block.go create mode 100644 pkg/core/block/block_test.go create mode 100644 pkg/core/block/doc.go create mode 100644 pkg/core/block/header.go create mode 100644 pkg/core/block/header_test.go create mode 100644 pkg/core/block/helper_test.go create mode 100644 pkg/core/blockchain.go create mode 100644 pkg/core/blockchain_core_test.go create mode 100644 pkg/core/blockchain_neotest_test.go create mode 100644 pkg/core/chaindump/dump.go create mode 100644 pkg/core/chaindump/dump_test.go create mode 100644 pkg/core/custom_native_test.go create mode 100644 pkg/core/dao/dao.go create mode 100644 pkg/core/dao/dao_test.go create mode 100644 pkg/core/doc.go create mode 100644 pkg/core/fee/calculate.go create mode 100644 pkg/core/fee/opcode.go create mode 100644 pkg/core/fee/opcode_test.go create mode 100644 pkg/core/headerhashes.go create mode 100644 pkg/core/helper_test.go create mode 100644 pkg/core/interop/context.go create mode 100644 pkg/core/interop/context_test.go create mode 100644 pkg/core/interop/contract/account.go create mode 100644 pkg/core/interop/contract/account_test.go create mode 100644 pkg/core/interop/contract/call.go create mode 100644 pkg/core/interop/contract/call_test.go create mode 100644 pkg/core/interop/crypto/ecdsa.go create mode 100644 pkg/core/interop/crypto/ecdsa_test.go create mode 100644 pkg/core/interop/crypto/interop.go create mode 100644 pkg/core/interop/doc.go create mode 100644 pkg/core/interop/gas_price.go create mode 100644 pkg/core/interop/interopnames/convert.go create mode 100644 pkg/core/interop/interopnames/convert_test.go create mode 100644 pkg/core/interop/interopnames/names.go create mode 100644 pkg/core/interop/iterator/interop.go create mode 100644 pkg/core/interop/iterator/interop_test.go create mode 100644 pkg/core/interop/runtime/engine.go create mode 100644 pkg/core/interop/runtime/engine_test.go create mode 100644 pkg/core/interop/runtime/ext_test.go create mode 100644 pkg/core/interop/runtime/util.go create mode 100644 pkg/core/interop/runtime/util_test.go create mode 100644 pkg/core/interop/runtime/witness.go create mode 100644 pkg/core/interop/storage/basic.go create mode 100644 pkg/core/interop/storage/bench_test.go create mode 100644 pkg/core/interop/storage/find.go create mode 100644 pkg/core/interop/storage/interops_test.go create mode 100644 pkg/core/interop/storage/storage_test.go create mode 100644 pkg/core/interops.go create mode 100644 pkg/core/mempool/bench_test.go create mode 100644 pkg/core/mempool/feer.go create mode 100644 pkg/core/mempool/mem_pool.go create mode 100644 pkg/core/mempool/mem_pool_test.go create mode 100644 pkg/core/mempool/subscriptions.go create mode 100644 pkg/core/mempool/subscriptions_test.go create mode 100644 pkg/core/mempoolevent/event.go create mode 100644 pkg/core/mpt/base.go create mode 100644 pkg/core/mpt/batch.go create mode 100644 pkg/core/mpt/batch_test.go create mode 100644 pkg/core/mpt/bench_test.go create mode 100644 pkg/core/mpt/billet.go create mode 100644 pkg/core/mpt/billet_test.go create mode 100644 pkg/core/mpt/branch.go create mode 100644 pkg/core/mpt/compat_test.go create mode 100644 pkg/core/mpt/doc.go create mode 100644 pkg/core/mpt/empty.go create mode 100644 pkg/core/mpt/extension.go create mode 100644 pkg/core/mpt/hash.go create mode 100644 pkg/core/mpt/helpers.go create mode 100644 pkg/core/mpt/helpers_test.go create mode 100644 pkg/core/mpt/leaf.go create mode 100644 pkg/core/mpt/node.go create mode 100644 pkg/core/mpt/node_test.go create mode 100644 pkg/core/mpt/proof.go create mode 100644 pkg/core/mpt/proof_test.go create mode 100644 pkg/core/mpt/trie.go create mode 100644 pkg/core/mpt/trie_store.go create mode 100644 pkg/core/mpt/trie_store_test.go create mode 100644 pkg/core/mpt/trie_test.go create mode 100644 pkg/core/native/compatibility_test.go create mode 100644 pkg/core/native/contract.go create mode 100644 pkg/core/native/contract_test.go create mode 100644 pkg/core/native/crypto.go create mode 100644 pkg/core/native/crypto_blspoints.go create mode 100644 pkg/core/native/crypto_test.go create mode 100644 pkg/core/native/designate.go create mode 100644 pkg/core/native/doc.go create mode 100644 pkg/core/native/interop.go create mode 100644 pkg/core/native/invocation_test.go create mode 100644 pkg/core/native/ledger.go create mode 100644 pkg/core/native/management.go create mode 100644 pkg/core/native/management_neotest_test.go create mode 100644 pkg/core/native/management_test.go create mode 100644 pkg/core/native/native_gas.go create mode 100644 pkg/core/native/native_neo.go create mode 100644 pkg/core/native/native_neo_candidate.go create mode 100644 pkg/core/native/native_neo_test.go create mode 100644 pkg/core/native/native_nep17.go create mode 100644 pkg/core/native/native_test/common_test.go create mode 100644 pkg/core/native/native_test/cryptolib_test.go create mode 100644 pkg/core/native/native_test/cryptolib_verification_test.go create mode 100644 pkg/core/native/native_test/designate_test.go create mode 100644 pkg/core/native/native_test/gas_test.go create mode 100644 pkg/core/native/native_test/helpers/policyhelper/policyhelper.go create mode 100644 pkg/core/native/native_test/helpers/policyhelper/policyhelper.yml create mode 100644 pkg/core/native/native_test/ledger_test.go create mode 100644 pkg/core/native/native_test/management_test.go create mode 100644 pkg/core/native/native_test/neo_test.go create mode 100644 pkg/core/native/native_test/notary_test.go create mode 100644 pkg/core/native/native_test/oracle_test.go create mode 100644 pkg/core/native/native_test/policy_test.go create mode 100644 pkg/core/native/native_test/std_test.go create mode 100644 pkg/core/native/native_test/treasury_test.go create mode 100644 pkg/core/native/nativehashes/gen.go create mode 100644 pkg/core/native/nativehashes/hashes.go create mode 100644 pkg/core/native/nativeids/gen.go create mode 100644 pkg/core/native/nativeids/ids.go create mode 100644 pkg/core/native/nativenames/names.go create mode 100644 pkg/core/native/nativenames_test.go create mode 100644 pkg/core/native/nativeprices/prices.go create mode 100644 pkg/core/native/neo_types.go create mode 100644 pkg/core/native/noderoles/role.go create mode 100644 pkg/core/native/noderoles/role_string.go create mode 100644 pkg/core/native/noderoles/role_test.go create mode 100644 pkg/core/native/notary.go create mode 100644 pkg/core/native/oracle.go create mode 100644 pkg/core/native/oracle_types.go create mode 100644 pkg/core/native/oracle_types_test.go create mode 100644 pkg/core/native/policy.go create mode 100644 pkg/core/native/policy_test.go create mode 100644 pkg/core/native/std.go create mode 100644 pkg/core/native/std_test.go create mode 100644 pkg/core/native/treasury.go create mode 100644 pkg/core/native/util.go create mode 100644 pkg/core/native_designate_test.go create mode 100644 pkg/core/prometheus.go create mode 100644 pkg/core/state/contract.go create mode 100644 pkg/core/state/contract_invocation.go create mode 100644 pkg/core/state/contract_invocation_test.go create mode 100644 pkg/core/state/contract_test.go create mode 100644 pkg/core/state/deposit.go create mode 100644 pkg/core/state/deposit_test.go create mode 100644 pkg/core/state/mpt_root.go create mode 100644 pkg/core/state/mpt_root_test.go create mode 100644 pkg/core/state/native_state.go create mode 100644 pkg/core/state/native_state_test.go create mode 100644 pkg/core/state/notification_event.go create mode 100644 pkg/core/state/notification_event_test.go create mode 100644 pkg/core/state/oracle.go create mode 100644 pkg/core/state/oracle_test.go create mode 100644 pkg/core/state/storage_item.go create mode 100644 pkg/core/state/tokens.go create mode 100644 pkg/core/state/tokens_test.go create mode 100644 pkg/core/state/validator.go create mode 100644 pkg/core/stateroot/callbacks.go create mode 100644 pkg/core/stateroot/module.go create mode 100644 pkg/core/stateroot/prometheus.go create mode 100644 pkg/core/stateroot/store.go create mode 100644 pkg/core/stateroot/validators.go create mode 100644 pkg/core/statesync/module.go create mode 100644 pkg/core/statesync/module_test.go create mode 100644 pkg/core/statesync/mptpool.go create mode 100644 pkg/core/statesync/mptpool_test.go create mode 100644 pkg/core/statesync/neotest_test.go create mode 100644 pkg/core/statesync/storagesyncmode_string.go create mode 100644 pkg/core/storage/boltdb_store.go create mode 100644 pkg/core/storage/boltdb_store_test.go create mode 100644 pkg/core/storage/dbconfig/store_config.go create mode 100644 pkg/core/storage/dbconfig/store_type.go create mode 100644 pkg/core/storage/dboper/operation.go create mode 100644 pkg/core/storage/leveldb_store.go create mode 100644 pkg/core/storage/leveldb_store_test.go create mode 100644 pkg/core/storage/memcached_store.go create mode 100644 pkg/core/storage/memcached_store_test.go create mode 100644 pkg/core/storage/memory_store.go create mode 100644 pkg/core/storage/memory_store_test.go create mode 100644 pkg/core/storage/store.go create mode 100644 pkg/core/storage/store_test.go create mode 100644 pkg/core/storage/store_type_test.go create mode 100644 pkg/core/storage/storeandbatch_test.go create mode 100644 pkg/core/test_data/block_1.json create mode 100644 pkg/core/test_data/block_2.json create mode 100644 pkg/core/transaction/attribute.go create mode 100644 pkg/core/transaction/attribute_test.go create mode 100644 pkg/core/transaction/attrtype.go create mode 100644 pkg/core/transaction/attrtype_string.go create mode 100644 pkg/core/transaction/bench_test.go create mode 100644 pkg/core/transaction/conflicts.go create mode 100644 pkg/core/transaction/doc.go create mode 100644 pkg/core/transaction/fuzz_test.go create mode 100644 pkg/core/transaction/helper_test.go create mode 100644 pkg/core/transaction/not_valid_before.go create mode 100644 pkg/core/transaction/notary_assisted.go create mode 100644 pkg/core/transaction/oracle.go create mode 100644 pkg/core/transaction/oracle_test.go create mode 100644 pkg/core/transaction/oracleresponsecode_string.go create mode 100644 pkg/core/transaction/reserved.go create mode 100644 pkg/core/transaction/signer.go create mode 100644 pkg/core/transaction/signer_test.go create mode 100644 pkg/core/transaction/transaction.go create mode 100644 pkg/core/transaction/transaction_test.go create mode 100644 pkg/core/transaction/witness.go create mode 100644 pkg/core/transaction/witness_condition.go create mode 100644 pkg/core/transaction/witness_condition_test.go create mode 100644 pkg/core/transaction/witness_rule.go create mode 100644 pkg/core/transaction/witness_rule_test.go create mode 100644 pkg/core/transaction/witness_scope.go create mode 100644 pkg/core/transaction/witness_scope_string.go create mode 100644 pkg/core/transaction/witness_scope_test.go create mode 100644 pkg/core/transaction/witness_test.go create mode 100644 pkg/core/transaction/witnessaction_string.go create mode 100644 pkg/core/transaction/witnessconditiontype_string.go create mode 100644 pkg/core/util.go create mode 100644 pkg/core/util_test.go create mode 100644 pkg/crypto/doc.go create mode 100644 pkg/crypto/hash/doc.go create mode 100644 pkg/crypto/hash/hash.go create mode 100644 pkg/crypto/hash/hash_test.go create mode 100644 pkg/crypto/hash/merkle_bench_test.go create mode 100644 pkg/crypto/hash/merkle_tree.go create mode 100644 pkg/crypto/hash/merkle_tree_test.go create mode 100644 pkg/crypto/keys/aes256.go create mode 100644 pkg/crypto/keys/doc.go create mode 100644 pkg/crypto/keys/internal/scrypt/scrypt.go create mode 100644 pkg/crypto/keys/internal/scrypt/scrypt_test.go create mode 100644 pkg/crypto/keys/nep2.go create mode 100644 pkg/crypto/keys/nep2_test.go create mode 100644 pkg/crypto/keys/private_key.go create mode 100644 pkg/crypto/keys/private_key_test.go create mode 100644 pkg/crypto/keys/publickey.go create mode 100644 pkg/crypto/keys/publickey_test.go create mode 100644 pkg/crypto/keys/sign_verify_test.go create mode 100644 pkg/crypto/keys/wif.go create mode 100644 pkg/crypto/keys/wif_test.go create mode 100644 pkg/crypto/verifiable.go create mode 100644 pkg/encoding/address/address.go create mode 100644 pkg/encoding/address/address_test.go create mode 100644 pkg/encoding/address/doc.go create mode 100644 pkg/encoding/base58/base58.go create mode 100644 pkg/encoding/base58/base58_test.go create mode 100644 pkg/encoding/base58/doc.go create mode 100644 pkg/encoding/bigint/bench_test.go create mode 100644 pkg/encoding/bigint/bigint.go create mode 100644 pkg/encoding/bigint/bigint_test.go create mode 100644 pkg/encoding/bigint/doc.go create mode 100644 pkg/encoding/bigint/fuzz_test.go create mode 100644 pkg/encoding/fixedn/decimal.go create mode 100644 pkg/encoding/fixedn/decimal_test.go create mode 100644 pkg/encoding/fixedn/doc.go create mode 100644 pkg/encoding/fixedn/fixed8.go create mode 100644 pkg/encoding/fixedn/fixed8_test.go create mode 100644 pkg/interop/LICENSE.md create mode 100644 pkg/interop/contract/contract.go create mode 100644 pkg/interop/convert/convert.go create mode 100644 pkg/interop/crypto/crypto.go create mode 100644 pkg/interop/doc.go create mode 100644 pkg/interop/go.mod create mode 100644 pkg/interop/iterator/iterator.go create mode 100644 pkg/interop/lib/address/address.go create mode 100644 pkg/interop/lib/contract/contract.go create mode 100644 pkg/interop/math/math.go create mode 100644 pkg/interop/native/crypto/crypto.go create mode 100644 pkg/interop/native/gas/gas.go create mode 100644 pkg/interop/native/ledger/block.go create mode 100644 pkg/interop/native/ledger/ledger.go create mode 100644 pkg/interop/native/ledger/transaction.go create mode 100644 pkg/interop/native/ledger/transaction_signer.go create mode 100644 pkg/interop/native/management/contract.go create mode 100644 pkg/interop/native/management/management.go create mode 100644 pkg/interop/native/neo/neo.go create mode 100644 pkg/interop/native/neo/neo_candidate.go create mode 100644 pkg/interop/native/notary/notary.go create mode 100644 pkg/interop/native/oracle/oracle.go create mode 100644 pkg/interop/native/policy/attrtype.go create mode 100644 pkg/interop/native/policy/policy.go create mode 100644 pkg/interop/native/roles/roles.go create mode 100644 pkg/interop/native/std/std.go create mode 100644 pkg/interop/neogointernal/call.go create mode 100644 pkg/interop/neogointernal/doc.go create mode 100644 pkg/interop/neogointernal/opcode.go create mode 100644 pkg/interop/neogointernal/syscall.go create mode 100644 pkg/interop/runtime/engine.go create mode 100644 pkg/interop/runtime/runtime.go create mode 100644 pkg/interop/storage/limits.go create mode 100644 pkg/interop/storage/storage.go create mode 100644 pkg/interop/types.go create mode 100644 pkg/interop/util/util.go create mode 100644 pkg/io/binaryBufWriter.go create mode 100644 pkg/io/binaryReader.go create mode 100644 pkg/io/binaryWriter.go create mode 100644 pkg/io/binaryrw_test.go create mode 100644 pkg/io/fileWriter.go create mode 100644 pkg/io/fileWriter_test.go create mode 100644 pkg/io/serializable.go create mode 100644 pkg/io/size.go create mode 100644 pkg/io/size_test.go create mode 100644 pkg/neorpc/errors.go create mode 100644 pkg/neorpc/errors_test.go create mode 100644 pkg/neorpc/events.go create mode 100644 pkg/neorpc/filters.go create mode 100644 pkg/neorpc/filters_test.go create mode 100644 pkg/neorpc/result/application_log.go create mode 100644 pkg/neorpc/result/block.go create mode 100644 pkg/neorpc/result/block_header.go create mode 100644 pkg/neorpc/result/block_notifications.go create mode 100644 pkg/neorpc/result/findstates.go create mode 100644 pkg/neorpc/result/findstorage.go create mode 100644 pkg/neorpc/result/invoke.go create mode 100644 pkg/neorpc/result/invoke_test.go create mode 100644 pkg/neorpc/result/mempool_event.go create mode 100644 pkg/neorpc/result/mpt.go create mode 100644 pkg/neorpc/result/mpt_test.go create mode 100644 pkg/neorpc/result/netfee.go create mode 100644 pkg/neorpc/result/notary_request_event.go create mode 100644 pkg/neorpc/result/peers.go create mode 100644 pkg/neorpc/result/peers_test.go create mode 100644 pkg/neorpc/result/raw_mempool.go create mode 100644 pkg/neorpc/result/raw_notary_pool.go create mode 100644 pkg/neorpc/result/relay_result.go create mode 100644 pkg/neorpc/result/tokens.go create mode 100644 pkg/neorpc/result/tx_raw_output.go create mode 100644 pkg/neorpc/result/unclaimed_gas.go create mode 100644 pkg/neorpc/result/validate_address.go create mode 100644 pkg/neorpc/result/validator.go create mode 100644 pkg/neorpc/result/validator_test.go create mode 100644 pkg/neorpc/result/version.go create mode 100644 pkg/neorpc/result/version_test.go create mode 100644 pkg/neorpc/rpcevent/filter.go create mode 100644 pkg/neorpc/rpcevent/filter_test.go create mode 100644 pkg/neorpc/types.go create mode 100644 pkg/neorpc/types_test.go create mode 100644 pkg/neotest/account.go create mode 100644 pkg/neotest/basic.go create mode 100644 pkg/neotest/chain/chain.go create mode 100644 pkg/neotest/chain/chain_test.go create mode 100644 pkg/neotest/chain/doc.go create mode 100644 pkg/neotest/client.go create mode 100644 pkg/neotest/compile.go create mode 100644 pkg/neotest/compile_test.go create mode 100644 pkg/neotest/coverage.go create mode 100644 pkg/neotest/doc.go create mode 100644 pkg/neotest/signer.go create mode 100644 pkg/neotest/signer_test.go create mode 100644 pkg/network/bqueue/operationmode_string.go create mode 100644 pkg/network/bqueue/queue.go create mode 100644 pkg/network/bqueue/queue_test.go create mode 100644 pkg/network/bqueue_adapters.go create mode 100644 pkg/network/capability/capability.go create mode 100644 pkg/network/capability/capability_test.go create mode 100644 pkg/network/capability/type.go create mode 100644 pkg/network/compress.go create mode 100644 pkg/network/discovery.go create mode 100644 pkg/network/discovery_test.go create mode 100644 pkg/network/extpool/pool.go create mode 100644 pkg/network/extpool/pool_test.go create mode 100644 pkg/network/fuzz_test.go create mode 100644 pkg/network/helper_test.go create mode 100644 pkg/network/message.go create mode 100644 pkg/network/message_string.go create mode 100644 pkg/network/message_test.go create mode 100644 pkg/network/notary_feer.go create mode 100644 pkg/network/payload/address.go create mode 100644 pkg/network/payload/address_test.go create mode 100644 pkg/network/payload/extensible.go create mode 100644 pkg/network/payload/extensible_test.go create mode 100644 pkg/network/payload/getblockbyindex.go create mode 100644 pkg/network/payload/getblockbyindex_test.go create mode 100644 pkg/network/payload/getblocks.go create mode 100644 pkg/network/payload/getblocks_test.go create mode 100644 pkg/network/payload/headers.go create mode 100644 pkg/network/payload/headers_test.go create mode 100644 pkg/network/payload/inventory.go create mode 100644 pkg/network/payload/inventory_test.go create mode 100644 pkg/network/payload/merkleblock.go create mode 100644 pkg/network/payload/merkleblock_test.go create mode 100644 pkg/network/payload/mptdata.go create mode 100644 pkg/network/payload/mptdata_test.go create mode 100644 pkg/network/payload/mptinventory.go create mode 100644 pkg/network/payload/mptinventory_test.go create mode 100644 pkg/network/payload/notary_request.go create mode 100644 pkg/network/payload/notary_request_test.go create mode 100644 pkg/network/payload/payload.go create mode 100644 pkg/network/payload/ping.go create mode 100644 pkg/network/payload/ping_test.go create mode 100644 pkg/network/payload/version.go create mode 100644 pkg/network/payload/version_test.go create mode 100644 pkg/network/peer.go create mode 100644 pkg/network/prometheus.go create mode 100644 pkg/network/server.go create mode 100644 pkg/network/server_config.go create mode 100644 pkg/network/server_test.go create mode 100644 pkg/network/state_sync.go create mode 100644 pkg/network/tcp_peer.go create mode 100644 pkg/network/tcp_peer_test.go create mode 100644 pkg/network/tcp_transport.go create mode 100644 pkg/network/transport.go create mode 100644 pkg/rpcclient/actor/actor.go create mode 100644 pkg/rpcclient/actor/actor_test.go create mode 100644 pkg/rpcclient/actor/compat_test.go create mode 100644 pkg/rpcclient/actor/doc_test.go create mode 100644 pkg/rpcclient/actor/maker.go create mode 100644 pkg/rpcclient/actor/maker_test.go create mode 100644 pkg/rpcclient/client.go create mode 100644 pkg/rpcclient/client_test.go create mode 100644 pkg/rpcclient/doc.go create mode 100644 pkg/rpcclient/doc_test.go create mode 100644 pkg/rpcclient/gas/gas.go create mode 100644 pkg/rpcclient/gas/gas_test.go create mode 100644 pkg/rpcclient/invoker/compat_test.go create mode 100644 pkg/rpcclient/invoker/doc_test.go create mode 100644 pkg/rpcclient/invoker/invoker.go create mode 100644 pkg/rpcclient/invoker/invoker_test.go create mode 100644 pkg/rpcclient/local.go create mode 100644 pkg/rpcclient/local_test.go create mode 100644 pkg/rpcclient/management/management.go create mode 100644 pkg/rpcclient/management/management_test.go create mode 100644 pkg/rpcclient/neo/doc_test.go create mode 100644 pkg/rpcclient/neo/neo.go create mode 100644 pkg/rpcclient/neo/neo_test.go create mode 100644 pkg/rpcclient/nep11/base.go create mode 100644 pkg/rpcclient/nep11/base_test.go create mode 100644 pkg/rpcclient/nep11/divisible.go create mode 100644 pkg/rpcclient/nep11/divisible_test.go create mode 100644 pkg/rpcclient/nep11/doc_test.go create mode 100644 pkg/rpcclient/nep11/nondivisible.go create mode 100644 pkg/rpcclient/nep11/nondivisible_test.go create mode 100644 pkg/rpcclient/nep17/doc_test.go create mode 100644 pkg/rpcclient/nep17/nep17.go create mode 100644 pkg/rpcclient/nep17/nep17_test.go create mode 100644 pkg/rpcclient/nep22/nep22.go create mode 100644 pkg/rpcclient/nep22/nep22_test.go create mode 100644 pkg/rpcclient/nep24/doc_test.go create mode 100644 pkg/rpcclient/nep24/royalty.go create mode 100644 pkg/rpcclient/nep24/royalty_test.go create mode 100644 pkg/rpcclient/nep31/nep31.go create mode 100644 pkg/rpcclient/nep31/nep31_test.go create mode 100644 pkg/rpcclient/neptoken/base.go create mode 100644 pkg/rpcclient/neptoken/base_test.go create mode 100644 pkg/rpcclient/neptoken/info.go create mode 100644 pkg/rpcclient/neptoken/info_test.go create mode 100644 pkg/rpcclient/nns/contract.go create mode 100644 pkg/rpcclient/nns/contract_test.go create mode 100644 pkg/rpcclient/nns/iterators.go create mode 100644 pkg/rpcclient/nns/record.go create mode 100644 pkg/rpcclient/nns/record_test.go create mode 100644 pkg/rpcclient/notary/accounts.go create mode 100644 pkg/rpcclient/notary/accounts_test.go create mode 100644 pkg/rpcclient/notary/actor.go create mode 100644 pkg/rpcclient/notary/actor_test.go create mode 100644 pkg/rpcclient/notary/contract.go create mode 100644 pkg/rpcclient/notary/contract_test.go create mode 100644 pkg/rpcclient/notary/doc_test.go create mode 100644 pkg/rpcclient/oracle/oracle.go create mode 100644 pkg/rpcclient/oracle/oracle_test.go create mode 100644 pkg/rpcclient/policy/policy.go create mode 100644 pkg/rpcclient/policy/policy_test.go create mode 100644 pkg/rpcclient/rolemgmt/roles.go create mode 100644 pkg/rpcclient/rolemgmt/roles_test.go create mode 100644 pkg/rpcclient/rpc.go create mode 100644 pkg/rpcclient/rpc_test.go create mode 100644 pkg/rpcclient/unwrap/unwrap.go create mode 100644 pkg/rpcclient/unwrap/unwrap_test.go create mode 100644 pkg/rpcclient/waiter/waiter.go create mode 100644 pkg/rpcclient/waiter/waiter_test.go create mode 100644 pkg/rpcclient/wsclient.go create mode 100644 pkg/rpcclient/wsclient_test.go create mode 100644 pkg/services/blockfetcher/blockfetcher.go create mode 100644 pkg/services/blockfetcher/blockfetcher_test.go create mode 100644 pkg/services/blockfetcher/operationmode_string.go create mode 100644 pkg/services/helpers/neofs/blockstorage.go create mode 100644 pkg/services/helpers/neofs/neofs.go create mode 100644 pkg/services/helpers/neofs/neofs_test.go create mode 100644 pkg/services/helpers/rpcbroadcaster/broadcaster.go create mode 100644 pkg/services/helpers/rpcbroadcaster/client.go create mode 100644 pkg/services/metrics/metrics.go create mode 100644 pkg/services/metrics/pprof.go create mode 100644 pkg/services/metrics/prometheus.go create mode 100644 pkg/services/notary/core_test.go create mode 100644 pkg/services/notary/node.go create mode 100644 pkg/services/notary/node_test.go create mode 100644 pkg/services/notary/notary.go create mode 100644 pkg/services/notary/notary_test.go create mode 100644 pkg/services/notary/request_type.go create mode 100644 pkg/services/notary/testdata/notary1.json create mode 100644 pkg/services/notary/testdata/notary2.json create mode 100644 pkg/services/oracle/broadcaster/oracle.go create mode 100644 pkg/services/oracle/filter.go create mode 100644 pkg/services/oracle/filter_test.go create mode 100644 pkg/services/oracle/jsonpath/jsonpath.go create mode 100644 pkg/services/oracle/jsonpath/jsonpath_test.go create mode 100644 pkg/services/oracle/network.go create mode 100644 pkg/services/oracle/network_test.go create mode 100644 pkg/services/oracle/nodes.go create mode 100644 pkg/services/oracle/oracle.go create mode 100644 pkg/services/oracle/oracle_test.go create mode 100644 pkg/services/oracle/request.go create mode 100644 pkg/services/oracle/request_test.go create mode 100644 pkg/services/oracle/response.go create mode 100644 pkg/services/oracle/testdata/oracle1.json create mode 100644 pkg/services/oracle/testdata/oracle2.json create mode 100644 pkg/services/oracle/transaction.go create mode 100644 pkg/services/rpcsrv/client_test.go create mode 100644 pkg/services/rpcsrv/error.go create mode 100644 pkg/services/rpcsrv/local_test.go create mode 100644 pkg/services/rpcsrv/notification_comparator.go create mode 100644 pkg/services/rpcsrv/params/param.go create mode 100644 pkg/services/rpcsrv/params/param_test.go create mode 100644 pkg/services/rpcsrv/params/params.go create mode 100644 pkg/services/rpcsrv/params/params_test.go create mode 100644 pkg/services/rpcsrv/params/txBuilder.go create mode 100644 pkg/services/rpcsrv/params/tx_builder_test.go create mode 100644 pkg/services/rpcsrv/params/types.go create mode 100644 pkg/services/rpcsrv/params/types_test.go create mode 100644 pkg/services/rpcsrv/prometheus.go create mode 100644 pkg/services/rpcsrv/server.go create mode 100644 pkg/services/rpcsrv/server_helper_test.go create mode 100644 pkg/services/rpcsrv/server_test.go create mode 100644 pkg/services/rpcsrv/subscription.go create mode 100644 pkg/services/rpcsrv/subscription_test.go create mode 100644 pkg/services/rpcsrv/testdata/testblocks.acc create mode 100644 pkg/services/rpcsrv/tokens.go create mode 100644 pkg/services/rpcsrv/util.go create mode 100644 pkg/services/statefetcher/statefetcher.go create mode 100644 pkg/services/statefetcher/statefetcher_test.go create mode 100644 pkg/services/stateroot/message.go create mode 100644 pkg/services/stateroot/network.go create mode 100644 pkg/services/stateroot/service.go create mode 100644 pkg/services/stateroot/service_test.go create mode 100644 pkg/services/stateroot/signature.go create mode 100644 pkg/services/stateroot/validators.go create mode 100644 pkg/services/stateroot/vote.go create mode 100644 pkg/smartcontract/binding/generate.go create mode 100644 pkg/smartcontract/binding/generate_test.go create mode 100644 pkg/smartcontract/binding/override.go create mode 100644 pkg/smartcontract/binding/override_test.go create mode 100644 pkg/smartcontract/builder.go create mode 100644 pkg/smartcontract/builder_test.go create mode 100644 pkg/smartcontract/callflag/call_flags.go create mode 100644 pkg/smartcontract/callflag/call_flags_test.go create mode 100644 pkg/smartcontract/context/context.go create mode 100644 pkg/smartcontract/context/context_test.go create mode 100644 pkg/smartcontract/context/item.go create mode 100644 pkg/smartcontract/context/item_test.go create mode 100644 pkg/smartcontract/contract.go create mode 100644 pkg/smartcontract/contract_test.go create mode 100644 pkg/smartcontract/doc.go create mode 100644 pkg/smartcontract/doc_test.go create mode 100644 pkg/smartcontract/entry.go create mode 100644 pkg/smartcontract/manifest/abi.go create mode 100644 pkg/smartcontract/manifest/abi_test.go create mode 100644 pkg/smartcontract/manifest/container.go create mode 100644 pkg/smartcontract/manifest/container_test.go create mode 100644 pkg/smartcontract/manifest/event.go create mode 100644 pkg/smartcontract/manifest/event_test.go create mode 100644 pkg/smartcontract/manifest/group.go create mode 100644 pkg/smartcontract/manifest/group_test.go create mode 100644 pkg/smartcontract/manifest/manifest.go create mode 100644 pkg/smartcontract/manifest/manifest_test.go create mode 100644 pkg/smartcontract/manifest/method.go create mode 100644 pkg/smartcontract/manifest/method_test.go create mode 100644 pkg/smartcontract/manifest/parameter.go create mode 100644 pkg/smartcontract/manifest/parameter_test.go create mode 100644 pkg/smartcontract/manifest/permission.go create mode 100644 pkg/smartcontract/manifest/permission_test.go create mode 100644 pkg/smartcontract/manifest/standard/check.go create mode 100644 pkg/smartcontract/manifest/standard/comply.go create mode 100644 pkg/smartcontract/manifest/standard/comply_test.go create mode 100644 pkg/smartcontract/manifest/standard/doc.go create mode 100644 pkg/smartcontract/manifest/standard/nep11.go create mode 100644 pkg/smartcontract/manifest/standard/nep17.go create mode 100644 pkg/smartcontract/manifest/standard/nep22.go create mode 100644 pkg/smartcontract/manifest/standard/nep24.go create mode 100644 pkg/smartcontract/manifest/standard/nep26.go create mode 100644 pkg/smartcontract/manifest/standard/nep27.go create mode 100644 pkg/smartcontract/manifest/standard/nep29.go create mode 100644 pkg/smartcontract/manifest/standard/nep30.go create mode 100644 pkg/smartcontract/manifest/standard/nep31.go create mode 100644 pkg/smartcontract/manifest/standard/token.go create mode 100644 pkg/smartcontract/nef/method_token.go create mode 100644 pkg/smartcontract/nef/method_token_test.go create mode 100644 pkg/smartcontract/nef/nef.go create mode 100644 pkg/smartcontract/nef/nef_test.go create mode 100644 pkg/smartcontract/param_type.go create mode 100644 pkg/smartcontract/param_type_test.go create mode 100644 pkg/smartcontract/parameter.go create mode 100644 pkg/smartcontract/parameter_test.go create mode 100644 pkg/smartcontract/rpcbinding/binding.go create mode 100644 pkg/smartcontract/scparser/bench_test.go create mode 100644 pkg/smartcontract/scparser/context.go create mode 100644 pkg/smartcontract/scparser/contract_checks.go create mode 100644 pkg/smartcontract/scparser/contract_checks_test.go create mode 100644 pkg/smartcontract/scparser/doc.go create mode 100644 pkg/smartcontract/scparser/fuzz_test.go create mode 100644 pkg/smartcontract/testdata/adjustValToType_filebytes_good.txt create mode 100644 pkg/smartcontract/trigger/trigger_type.go create mode 100644 pkg/smartcontract/trigger/trigger_type_string.go create mode 100644 pkg/smartcontract/trigger/trigger_type_test.go create mode 100644 pkg/smartcontract/zkpbinding/binding.go create mode 100644 pkg/util/bench_test.go create mode 100644 pkg/util/bitfield/bitfield.go create mode 100644 pkg/util/bitfield/bitfield_test.go create mode 100644 pkg/util/uint160.go create mode 100644 pkg/util/uint160_test.go create mode 100644 pkg/util/uint256.go create mode 100644 pkg/util/uint256_test.go create mode 100644 pkg/vm/bench_test.go create mode 100644 pkg/vm/context.go create mode 100644 pkg/vm/debug_test.go create mode 100644 pkg/vm/emit/emit.go create mode 100644 pkg/vm/emit/emit_test.go create mode 100644 pkg/vm/exception.go create mode 100644 pkg/vm/fuzz_test.go create mode 100644 pkg/vm/invocation_tree_test.go create mode 100644 pkg/vm/invocations/invocation_tree.go create mode 100644 pkg/vm/iterator_test.go create mode 100644 pkg/vm/json_test.go create mode 100644 pkg/vm/opcode/from_string.go create mode 100644 pkg/vm/opcode/isvalid_test.go create mode 100644 pkg/vm/opcode/opcode.go create mode 100644 pkg/vm/opcode/opcode_string.go create mode 100644 pkg/vm/opcode/opcode_test.go create mode 100644 pkg/vm/opcodebench_test.go create mode 100644 pkg/vm/ref_counter.go create mode 100644 pkg/vm/ref_counter_test.go create mode 100644 pkg/vm/slot.go create mode 100644 pkg/vm/slot_test.go create mode 100644 pkg/vm/stack.go create mode 100644 pkg/vm/stack_test.go create mode 100644 pkg/vm/stackitem/immutable.go create mode 100644 pkg/vm/stackitem/item.go create mode 100644 pkg/vm/stackitem/item_test.go create mode 100644 pkg/vm/stackitem/json.go create mode 100644 pkg/vm/stackitem/json_test.go create mode 100644 pkg/vm/stackitem/reference.go create mode 100644 pkg/vm/stackitem/serialization.go create mode 100644 pkg/vm/stackitem/serialization_test.go create mode 100644 pkg/vm/stackitem/type.go create mode 100644 pkg/vm/stackitem/type_test.go create mode 100644 pkg/vm/vm.go create mode 100644 pkg/vm/vm_test.go create mode 100644 pkg/vm/vmstate/state.go create mode 100644 pkg/vm/vmstate/state_test.go create mode 100644 pkg/wallet/account.go create mode 100644 pkg/wallet/account_test.go create mode 100644 pkg/wallet/regenerate_test.go create mode 100644 pkg/wallet/testdata/wallet1.json create mode 100644 pkg/wallet/testdata/wallet2.json create mode 100644 pkg/wallet/token.go create mode 100644 pkg/wallet/token_test.go create mode 100644 pkg/wallet/wallet.go create mode 100644 pkg/wallet/wallet_test.go create mode 100755 scripts/check_deps.sh create mode 100644 scripts/compare-dumps/compare-dumps.go create mode 100644 scripts/compare-dumps/compare-dumps_test.go create mode 100644 scripts/compare-states/compare-states.go create mode 100644 scripts/gendump/main.go create mode 100644 scripts/go.mod create mode 100644 scripts/go.sum create mode 100755 scripts/update_deps.sh create mode 100644 tutus.service.template diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml new file mode 100644 index 0000000..2413ec0 --- /dev/null +++ b/.docker/docker-compose.yml @@ -0,0 +1,74 @@ +networks: + default: + name: neo_go_network + ipam: + config: + - subnet: 172.200.0.0/24 + gateway: 172.200.0.254 + +volumes: + volume_chain: + driver: local + +services: + node_one: + container_name: neo_go_node_one + image: env_neo_go_image + command: "node --config-path /config --privnet --force-timestamp-logs" + volumes: + - ../config/protocol.privnet.docker.one.yml:/config/protocol.privnet.yml + - ./wallets/wallet1.json:/wallet1.json + - volume_chain:/chains + ports: + - 20333:20333 + - 30333:30333 + - 20001:20001 + node_two: + container_name: neo_go_node_two + image: env_neo_go_image + command: "node --config-path /config --privnet --force-timestamp-logs" + volumes: + - ../config/protocol.privnet.docker.two.yml:/config/protocol.privnet.yml + - ./wallets/wallet2.json:/wallet2.json + - volume_chain:/chains + ports: + - 20334:20334 + - 30334:30334 + - 20002:20002 + node_three: + container_name: neo_go_node_three + image: env_neo_go_image + command: "node --config-path /config --privnet --force-timestamp-logs" + volumes: + - ../config/protocol.privnet.docker.three.yml:/config/protocol.privnet.yml + - ./wallets/wallet3.json:/wallet3.json + - volume_chain:/chains + ports: + - 20335:20335 + - 30335:30335 + - 20003:20003 + node_four: + container_name: neo_go_node_four + image: env_neo_go_image + command: "node --config-path /config --privnet --force-timestamp-logs" + volumes: + - ../config/protocol.privnet.docker.four.yml:/config/protocol.privnet.yml + - ./wallets/wallet4.json:/wallet4.json + - volume_chain:/chains + ports: + - 20336:20336 + - 30336:30336 + - 20004:20004 + node_single: + container_name: neo_go_node_single + image: env_neo_go_image + command: "node --config-path /config --privnet --force-timestamp-logs" + volumes: + - ../config/protocol.privnet.docker.single.yml:/config/protocol.privnet.yml + - ./wallets/wallet1_solo.json:/wallet1.json + - volume_chain:/chains + ports: + - 20333:20333 + - 30333:30333 + - 20001:20001 + diff --git a/.docker/privnet-entrypoint.ps1 b/.docker/privnet-entrypoint.ps1 new file mode 100644 index 0000000..94cd4fc --- /dev/null +++ b/.docker/privnet-entrypoint.ps1 @@ -0,0 +1,15 @@ +#!C:\Program Files\PowerShell\7\pwsh.EXE -File + +$bin = '/usr/bin/neo-go.exe' + +for ( $i = 0; $i -lt $args.count; $i++ ) { + if ($args[$i] -eq "node"){ + Write-Host "=> Try to restore blocks before running node" + if (($Env:ACC -ne $null) -and (Test-Path $Env:ACC -PathType Leaf)) { + & $bin db restore -p --config-path /config -i $Env:ACC + } + break + } +} + +& $bin $args diff --git a/.docker/privnet-entrypoint.sh b/.docker/privnet-entrypoint.sh new file mode 100755 index 0000000..3289c04 --- /dev/null +++ b/.docker/privnet-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +BIN=/usr/bin/neo-go + +case $@ in +"node"*) + echo "=> Try to restore blocks before running node" + if [ -f "$ACC" ]; then + gunzip --stdout "$ACC" >/privnet.acc + ${BIN} db restore -p --config-path /config -i /privnet.acc + fi + ;; +esac + +${BIN} "$@" diff --git a/.docker/wallets/wallet1.json b/.docker/wallets/wallet1.json new file mode 100644 index 0000000..647eb85 --- /dev/null +++ b/.docker/wallets/wallet1.json @@ -0,0 +1,55 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", + "key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY", + "label": "", + "contract": { + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", + "key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY", + "label": "", + "contract": { + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + }, + { + "name": "parameter1", + "type": "Signature" + }, + { + "name": "parameter2", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 16384, + "r": 8, + "p": 8 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/.docker/wallets/wallet1_solo.json b/.docker/wallets/wallet1_solo.json new file mode 100644 index 0000000..7ebcf53 --- /dev/null +++ b/.docker/wallets/wallet1_solo.json @@ -0,0 +1,72 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", + "key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY", + "label": "", + "contract": { + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", + "key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY", + "label": "", + "contract": { + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + }, + { + "name": "parameter1", + "type": "Signature" + }, + { + "name": "parameter2", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP", + "key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY", + "label": "", + "contract": { + "script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 16384, + "r": 8, + "p": 8 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/.docker/wallets/wallet2.json b/.docker/wallets/wallet2.json new file mode 100644 index 0000000..ad3e879 --- /dev/null +++ b/.docker/wallets/wallet2.json @@ -0,0 +1,55 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "NMUedC8TSV2rE17wGguSvPk9XcmHSaT275", + "key": "6PYSYoZaxqDu5vqvm7yUFT3sPJJFwyLyYDnp8zwj1YVPcBWxacz64bNX59", + "label": "", + "contract": { + "script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG5BVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", + "key": "6PYSYoZaxqDu5vqvm7yUFT3sPJJFwyLyYDnp8zwj1YVPcBWxacz64bNX59", + "label": "", + "contract": { + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + }, + { + "name": "parameter1", + "type": "Signature" + }, + { + "name": "parameter2", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 16384, + "r": 8, + "p": 8 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/.docker/wallets/wallet3.json b/.docker/wallets/wallet3.json new file mode 100644 index 0000000..de6446c --- /dev/null +++ b/.docker/wallets/wallet3.json @@ -0,0 +1,55 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo", + "key": "6PYVxMnzPMFTYY16xRvXm2SJcvaChabLzaARAb1Mmej9U7rYLYWMSPtfam", + "label": "", + "contract": { + "script": "DCED2QwH32PmkM53kS4Qq1GsyUS2aGAje2CMT4+DCece5plBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", + "key": "6PYVxMnzPMFTYY16xRvXm2SJcvaChabLzaARAb1Mmej9U7rYLYWMSPtfam", + "label": "", + "contract": { + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + }, + { + "name": "parameter1", + "type": "Signature" + }, + { + "name": "parameter2", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 16384, + "r": 8, + "p": 8 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/.docker/wallets/wallet4.json b/.docker/wallets/wallet4.json new file mode 100644 index 0000000..95d6f33 --- /dev/null +++ b/.docker/wallets/wallet4.json @@ -0,0 +1,55 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "NPrB7BmTMYxf9UVroJp4RQExM9tqKmsHTz", + "key": "6PYX8eELiDduPW3RGiZxZNmG6KtWtXkyRFi47f8w6quEBpRkpBPxH5u5AP", + "label": "", + "contract": { + "script": "DCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWJBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", + "key": "6PYX8eELiDduPW3RGiZxZNmG6KtWtXkyRFi47f8w6quEBpRkpBPxH5u5AP", + "label": "", + "contract": { + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + }, + { + "name": "parameter1", + "type": "Signature" + }, + { + "name": "parameter2", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 16384, + "r": 8, + "p": 8 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6ebc433 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +chains diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..50e5f83 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @AnnaShaleva @roman-khimov diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a27a88a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +### Problem + +... + +### Solution + +... diff --git a/.github/logo_dark.png b/.github/logo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..32a3a3ef2ccda76cb55e688cf35b8b7fbc250fcf GIT binary patch literal 41579 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 z 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 $GITHUB_OUTPUT + + - name: Set latest tag + id: setlatest + if: ${{ (github.event_name == 'release' && github.event.release.target_commitish == 'master') || (github.event_name == 'workflow_dispatch' && github.event.inputs.use_latest_tag == 'true') }} + run: echo "latest=,${{ steps.setvars.outputs.repo }}:latest" >> $GITHUB_OUTPUT + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }} + platforms: linux/amd64,linux/arm64 + build-args: | + REPO=github.com/${{ github.repository }} + VERSION=${{ steps.setvars.outputs.version }} + tags: ${{ steps.setvars.outputs.repo }}:${{ steps.setvars.outputs.version }}${{ steps.setvars.outputs.suffix }}${{ steps.setlatest.outputs.latest }} + + build_image_wsc: + needs: build_cli + name: Build and push docker image (Windows Server Core) + runs-on: windows-latest + if: ${{ github.event_name != 'pull_request' }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.ref }} + fetch-depth: 0 + + # For proper `deps` make target execution. + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.25' + + - name: Login to DockerHub + if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build Docker image + run: make image + + - name: Push image to registry + if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }} + run: make image-push diff --git a/.github/workflows/contribution_guidelines.yml b/.github/workflows/contribution_guidelines.yml new file mode 100644 index 0000000..d75d5c0 --- /dev/null +++ b/.github/workflows/contribution_guidelines.yml @@ -0,0 +1,11 @@ +name: Contribution guidelines + +on: + pull_request: + branches: + - master + +jobs: + commits_check_job: + name: DCO check + uses: nspcc-dev/.github/.github/workflows/dco.yml@master diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..07e63e5 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,197 @@ +name: Tests + +on: + push: + branches: [ master ] + pull_request: + branches: + - master + types: [opened, synchronize] + paths-ignore: + - 'scripts/*.sh' + - '**/*.md' + workflow_dispatch: + +jobs: + lint: + name: 'Lint: NeoGo' + uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master + + lint_examples: + name: 'Lint: examples (${{ matrix.contract }})' + uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master + + strategy: + fail-fast: false + matrix: + contract: [ 'engine', 'events', 'iterator', 'nft-d', 'nft-nd', 'nft-nd-nns', 'oracle', + 'runtime', 'storage', 'timer', 'token', 'zkp/cubic_circuit', 'zkp/xor_compat'] + with: + workdir: examples/${{ matrix.contract }} + + lint_scripts: + name: 'Lint: scripts' + uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master + with: + workdir: scripts + + lint_interops: + name: 'Lint: interop' + uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master + with: + workdir: pkg/interop + + gomodcheck: + name: Check internal dependencies + runs-on: ubuntu-slim + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Check dependencies + run: | + ./scripts/check_deps.sh + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - name: Check go.mod is tidy + run: | + go mod tidy + if [[ $(git diff --name-only go.* | grep '' -c) != 0 ]]; then + echo "go mod tidy should be executed before the merge, following packages are unused or out of date:"; + git diff go.*; + exit 1; + fi + + codegencheck: + name: Check code generated with 'go generate' is up-to-date + runs-on: ubuntu-slim + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - name: Install stringer + run: go install golang.org/x/tools/cmd/stringer@latest + + - name: Run go generate + run: go generate ./... + + - name: Check that autogenerated code is up-to-date + run: | + if [[ $(git diff --name-only | grep '' -c) != 0 ]]; then + echo "Fresh version of autogenerated code should be committed for the following files:"; + git diff --name-only; + exit 1; + fi + + codeql: + name: CodeQL + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + + test_cover: + name: Coverage + runs-on: ubuntu-latest + + env: + CGO_ENABLED: 0 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: 'true' + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.25' + cache: true + + - name: Write coverage profile + run: DISABLE_NEOTEST_COVER=1 go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/... + + - name: Upload coverage results to Codecov + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true # if something is wrong on uploading codecov results, then this job will fail + files: ./coverage.txt + slug: nspcc-dev/neo-go + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + + tests: + name: Run tests + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + go_versions: [ '1.24', '1.25' ] + exclude: + # Only latest Go version for Windows and MacOS. + - os: windows-latest + go_versions: '1.24' + - os: macos-latest + go_versions: '1.24' + # Exclude latest Go version for Ubuntu as Coverage uses it. + - os: ubuntu-latest + go_versions: '1.25' + fail-fast: false + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: 'true' + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '${{ matrix.go_versions }}' + + - name: Run tests + run: go test -timeout 15m -v -race ./... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..459a619 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Added by CoZ developers +vendor/ +bin/ +!examples/**/vendor + +# text editors +# vscode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +# goland +.idea/* +# emacs +*~ +TAGS + +# storage +/chains + +# patch +*.orig +*.rej + +# Coverage +coverage.txt +coverage.html + +# Compiler output +examples/*/*.nef +examples/*/*.json + +# Fuzzing testdata. +testdata/ +!cli/testdata +!internal/basicchain/testdata +!pkg/compiler/testdata +!pkg/config/testdata +!pkg/consensus/testdata +!pkg/services/rpcsrv/testdata +!pkg/services/notary/testdata +!pkg/services/oracle/testdata +!pkg/smartcontract/testdata +!cli/smartcontract/testdata +pkg/vm/testdata/fuzz +!pkg/vm/testdata +!pkg/wallet/testdata + +# Linter +.golangci.yml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..34736ca --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "pkg/vm/testdata/neo-vm"] + path = pkg/vm/testdata/neo-vm + url = https://github.com/neo-project/neo.git + branch = master diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f997d1e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3368 @@ +# Changelog + +This document outlines major changes between releases. + +## 0.114.0 "Chemosorption" (17 Nov 2025) + +This version fixes the state difference at block 11074661 of testnet caused by +improper handling of native NEP-17 token transfer. It also fixes a deadlock +caused by inability to terminate an expired iterator session. It's fully +compatible with the C# node 3.8+ and delivers a set of improvements for +compiler, NeoFS block storage audit and uploader along with a preview of Faun +changes. + +Deprecated functionality has been removed according to the schedule (`math.Max` +and `math.Min` interop functions along with `SessionExpirationTime` RPC server +config), consider upgrading affected smart contracts and node config +correspondingly. The default ports of Prometheus and pprof services have been +changed for mainnet config. Testnet node resynchronisation (or state reset to +block 11074660 for full nodes) is recommended but not required since the state +is the same at higher block (starting from 11091272), and the only real +difference now is two application logs for two transactions. + +New features: + * local analogs of `System.Storage.*` interop API added to Faun preview (#4031) + +Behavior changes: + * migrate to single threaded search in NeoFS block storage uploader (#4030) + * adjust Prometheus and pprof ports in default mainnet config (#4074) + * removal of deprecated `math.Max` and `math.Min` interop functions (#4083) + * removal of deprecated `SessionExpirationTime` RPC server config (#4083) + +Improvements: + * documentation updates (#4035, #4045, #4079) + * NeoFS SDK Go update to RC15 (#4028) + * dependencies upgrade (#4067) + * support assignment operators `&=`, `|=`, `>>=`, `<<=` in compiler (#4059) + * add an example of NeoFS usage to smart contracts (#4064) + * refactor NeoFS block storage uploader (#4070, #4080) + * improve NeoFS block storage audit CLI tool (#4019) + * properly handle circular reference in stackitem JSON serialization (#4044) + +Bugs fixed: + * RPC bindings generator generates incorrect bindings for NEP-22/NEP-31 (#4022, + #4024, #4048) + * panic in the compiler on missing external package in the imported one (#4027) + * execution error caused by iteration over nil slice (#4026) + * add source URL to NEF file on compilation in neotest (#4042) + * `getnextblockvalidator` RPC handler returns result only for enrollments + (#4047) + * negative native NEP-17 transfer doesn't lead to VM FAULT (#4073) + * inability to force-terminate expired iterator session (#4082) + +## 0.113.0 "Circumnavigation" (24 Sept 2025) + +This version includes an upgrade to Go 1.24 and adds a couple of new native +contract APIs to Faun hardfork preview. But what is more important, it delivers +a set of essential compiler updates and bug fixes. + +We strongly recommend smart contract developers to upgrade to this version and +review the list of compiler-related improvements. Also, the upgrade is +recommended to the users of `rpcclient` package to avoid possible problems with +node version unmarshalling after upcoming Faun hardfork activation. Node +operators may safely skip this update. + +Behavior changes: + * switch from go.etcd.io/bbolt to custom (and upgraded) nspcc-dev/bbolt (#4006) + +Improvements: + * `hexEncode` and `hexDecode` APIs of native StdLib contract added to Faun + preview (#4004) + * `getBlockedAccounts` API of native Policy contract added to Faun preview + (#4004) + * built-in `clear()` support in compiler (#3091) + * range over integers in compiler (#3525) + * Go 1.24 support (#4010) + * byte slice/integer conversion helpers added to SC interop module (#3983) + * NEP-32 support for `db dump` CLI command (#3987) + +Bugs fixed: + * invalid string representation of Faun hardfork (#4001) + * increment operator doesn't work on struct fields in compiler (#3981) + * panic on inlined function invocation in compiler (#4002) + * list items swap doesn't work as expected in compiler (#4005) + * panic on fetching map values with `ok` flag (#3716) + * inability to `go install` neo-go due to `replace` directive in go.mod (#4006) + * initialization statement of `switch` is ignored by compiler (#3991) + +## 0.112.0 "Hibernation" (29 Aug 2025) + +This version fixes state difference at block 8813651 of testnet caused by +improper `ABORTMSG` and `AASSERTMSG` arguments handling. It also introduces +support for a set of newly-added NEPs and ability to customize the list of +native contracts. + +DB resynchronisation (or state reset to block 8813650 for full nodes) is +required on testnet nodes upgrade. `SessionExpirationTime` RPC server setting is +deprecated and replaced by `SessionLifetime` of `Duration` type, consider +upgrading the node's config. `math.Min` and `math.Max` interop utilities are +deprecated and replaced by Go built-in `min` and `max` functions, consider +upgrading affected smart contracts. Also, some deprecated functionality has been +removed according to the schedule. + +New features: + * customizable native contracts (#3966) + * NEP-22/NEP-31 (contract update and destroy functionality) support in CLI, + `smartcontract` package and compiler (#3968, #3971) + * NEP-29/NEP-30 (contract `_deploy` and `verify` methods) support in + `smartcontract` package and compiler (#3978) + * NEP-32 support for `db restore` CLI command (unified chain dump format) + (#3974) + * mempool events RPC web-socket subscription (#3967) + +Behavior changes: + * `wallet candidate register` CLI command is migrated onto GAS transfer (#3973) + * `SessionExpirationTime` RPC server setting is deprecated and replaced by + `SessionLifetime` (#3953) + * `math.Min` and `math.Max` interop helpers are deprecated and replaced with + Go built-in min/max (#3984) + * deprecated functionality removal (`GetBlockHeader` and + `GetBlockHeaderVerbose` methods of RPC client) (#3995) + * RPC client's `Waiter.Wait()` is extended with context parameter (#3959) + +Improvements: + * `storage.KeyValue` interop type (#3982) + * `util convert` CLI command is extended with Base64 public key convertor + * stackitem conversion is supported for `rolemgmt.DesignationEvent` native + RoleManagement RPC binding (#3956) + * `getversion` RPC response is extended with `SaveInvocations`, + `KeepOnlyLatestState` and `RemoveUntraceableBlocks` settings (#3954) + * Go built-in min/max support in compiler (#3984) + * web-socket client connection errors improvement (#3975) + +Bugs fixed: + * panic on public key comparison with infinite operand (#3961) + * missing strict UTF-8 check for `ABORTMSG` and `ASSERTMSG` arguments (#3988) + +## 0.111.0 "Facilitation" (18 Jul 2025) + +We've decided to release one more v3.8.0-compatible version since the current +set of changes includes adaptive block time extension allowing to vary block +generation time from `TimePerBlock` to `MaxTimePerBlock`. In addition to that, +this version includes new audit module for NeoFS block storage, an optimisation +of light node synchronisation process, enhanced version of BoltDB and more. + +Some deprecated functionality has been removed according to the schedule, see +more details in the `Behaviour changes` section. Also, for those node operators +who would like to test the dynamic block time extension, new `MaxTimePerBlock` +option is added to the node's protocol configuration. Finally, configuration +changes have been introduced: starting from this version, +`RemoveUntraceableHeaders` mode is a part of `RemoveUntraceableBlocks` and +hence, should be removed from the node's configuration file in favour of +`RemoveUntraceableBlocks`. Also, `SessionExpirationTime` RPC server +configuration setting is replaced with `SessionLifetime` setting of type +`Duration`; `SkipIndexFilesSearch`, `IndexFileSize` and `IndexFileAttribute` +settings of NeoFSBlockFetcher service were dropped since index files are not +supported anymore. Please, upgrade your configuration accordingly. + +New features: + * `util audit-bin` CLI command for NeoFS block storage audit (#3943, #3945, + #3946) + * support of `GetBlockHeaderByIndex[Verbose]` RPC client API (#3939) + * `MaxTimePerblock` protocol configuration setting enabling dynamic block + acceptance interval (#3948) + +Behavior changes: + * index files support is removed from `util upload-bin` CLI command and + NeoFSBlockFetcher service (#3938) + * `RemoveUntraceableHeaders` application configuration setting is merged into + `RemoveUntraceableBlocks` (#3922) + * `GetBlockHeaderVerbose` RPC client API is deprecated and replaced by + `GetBlockHeaderByHashVerbose` (#3939) + * deprecated `NEP11Payable` and `NEP17Payable` aliases are removed from the + smartcontract package in favour of `NEP26StandardName` and + `NEP27StandardName` (#3955) + * `SessionExpirationTime` RPC server configuration setting is deprecated and + replaced by `SessionLifetime` setting of `Duration` type (#3953) + +Improvements: + * light node synchronisation doesn't require full headers sync anymore (#3922) + * BoltDB dependency is upgraded to custom (and faster!) version (#3936, #3937) + * dBFT dependency upgrade empowering dynamic block time extension (#3948) + +Bugs fixed: + * misses in blocks uploaded by `util upload-bin` CLI command (#3942) + * panic on attempt to close local RPC client (#3947) + +## 0.110.0 "Utilization" (10 Jun 2025) + +This version fixes state difference at block 6701925 of testnet. It also introduces +NeoFSStateFetcher service that allows to synchronize contract storage state at +particular height based on checkpoint data stored in NeoFS. Moreover, for smooth +RPC client upgrade experience, the next Faun hardfork is added in a preview mode +(although it doesn't contain any changes yet). Also, new `BroadcastTxsBatchDelay` +P2P application configuration setting is introduced preliminary for custom networks +setup to be able to customize the maximum transaction batch broadcast delay. + +T5 nodes require DB resynchronisation (or state reset to 6701924 block for full nodes), +mainnet nodes DB resync is not required at the moment of release. No configuration +changes required. + +New features: + * ability to synchronize contract storage from NeoFS snapshot (#3844) + * Faun hardfork is introduced in a preview mode, but doesn't include + any changes yet (#3931) + +Improvements: + * migration to NeoFS SearchV2 API (#3913) + * support for graceful stop at given height for NeoFSBlockFetcher service (#3901) + * support slice expressions for string in compiler (#3916) + * integrate ArchivalNode capability (#3899) + * refactor code to use Go built-ins (#3897, #3923) + * update documentation (#3924) + * `BroadcastTxsBatchDelay` application setting that allows to customize the time + limit for transaction batch collection before its broadcast (#3930) + +Bugs fixed: + * race in cache persistence logic (#3931) + * whitespaces in argument of StdLib's `base64Decode` and `base64UrlDecode` + methods lead to decoding error (#3928) + * multiple simultaneous connections to a single peer (#3911) + * node peer state unambiguity (#3911) + * failed MPT initialization after node restart (#3919) + * bugs in seed discovery logic (#3912) + * intermediate state synchronisation stages are not persisted to the DB (#3900) + * intermediate state jump stages are not persisted to the DB (#3895) + * excessive logging in Notary service (#3896) + * missing block height initialization for statesync module (#3933) + +## 0.109.1 "Transformation" (14 May 2025) + +An urgent patch version that fixes state difference at block 5894663 of testnet. +It also contains a couple of other bug fixes including CommitteeChanged events +emission (that affects application execution results compatibility with C# node, +but likely not critical for anyone). + +DB resynchronisation for testnet nodes is required (operators of full node may +use `neo-go db reset` command to reset the node's state to height 5894662 and +avoid full chain resynchronisation). Mainnet DB resynchronisation is not +needed. + +Behavior changes: + * `subscribe` and `unsubscribe` requests will be served sequentially by web-socket + RPC client (#3893) + +Bugs fixed: + * CommitteeChanged event of native NeoToken is emitted irrespective of + Cockatrice fork (#3892) + * GasPerBlock update is applied starting from the next block (#3891) + * panic in subscriptions module of web-socket RPC client (#3893) + +## 0.109.0 "Separation" (01 May 2025) + +Long-awaited version that is fully compatible with the C# node 3.8.0. It extends +and enables Echidna hardfork, introduces a set of RPC extensions and new node +configuration settings and fixes a couple of critical bugs. + +Node operators should review and update node configuration. For N3 chains Echidna +hardfork is enabled at block `7300000` of Mainnet and `5870000` block of Testnet; +NeoFS Mainnet hardforks schedule is changed, see the updated node configuration +for reference; for NeoFS Testnet Echidna is enabled starting from the genesis +block. N3 Mainnet/Testnet nodes DB resynchronization is not needed, NeoFS chains +resynchronization is required. + +New features: + * block generation time is a part of native Policy contract + (`setMillisecondsPerBlock` and `getMillisecondsPerBlock` APIs) starting + from Echidna HF (#3835) + * `util upload-state` CLI command allowing to upload contract storage items + to NeoFS (#3808, #3845) + * `verifyWithEd25519` method support in CryptoLib native contract starting + from Echidna HF (#3852, #3853) + * `MaxWebSocketFeeds` node parameter allowing to configure maximum number + of RPC server subscriptions (#3828) + * `invokecontainedscript` RPC API allowing to invoke script in a + customizable execution environment (#3839) + * `LogEncoding` and `LogTimestamp` node parameters allowing to customize logs + output format and timestamps (#3846) + * `MaxValidUntilBlockIncrement` setting is a part of native Policy contract + (`setMaxValidUntilBlockIncrement` and `getMaxValidUntilBlockIncrement` APIs) + starting from Echidna HF (#3849) + * `MaxTraceableBlocks` setting is a part of native Policy contract + (`setMaxTraceableBlocks` and `getMaxTraceableBlocks` APIs) starting from + Echidna HF (#3858) + * `recoverSecp256K1` method support in CryptoLib native contract starting + from Echidna HF (#3863) + * `isContract` method support in ContractManagement native contract starting + from Echidna HF (#3867) + * `DisableCompression` node parameter allowing to disable P2P payloads + compression (#3881) + * `NotaryAssisted` attribute support is moved from `P2PSignatureExtensions` + under Echidna HF (#3854) + * Notary native contract activation is moved from `P2PSignatureExtensions` + under Echidna HF (#3478) + * Echidna HF is stable now (#3851) + +Behavior changes: + * restricted number of allowed contract notifications starting from Echidna + HF (#3640) + * NeoFS networks configuration update and forks reschedule (#3833, #3851) + +Improvements: + * RPC session iterator expansion extension (#3827) + * optimize VM script execution in case of disabled coverage (#3855) + * replace timers with tickers (#3861) + * extend NotaryAssisted attribute verification logic (#3865) + * `ErrConnClosedByUser` error designated to distinguish case when connection + is closed by RPC client user (#3868) + * Go 1.23 upgrade (#3847) + * NeoFS SDK Go update to RC13+ (#3847, #3870) + +Bugs fixed: + * NPE on `getpeers` RPC request serving (#3880) + * inability to decode genesis block header fetched from NeoFS (#3819) + * insufficient nesting depth restriction of transaction witness condition + (#3815) + * improper handling of empty filter for `getblocknotifications` RPC API + on the RPC client side (#3820) + * native contracts can't be initialized from non-genesis block (#3837) + * invalid signature of `base64UrlEncode` and `base64UrlDecode` (#3862) + * NPE on NeoFS BlockFetcher service shutdown (#3870) + +## 0.108.1 "Revalidation" (13 Feb 2025) + +An urgent fix for a very old behavior difference with C# node in Rules witness +condition parsing. It suddenly affected testnet compatibility at block 5450030 +and made the chain unprocessable by NeoGo. Please upgrade to fix it, DB is +compatible, no resynchronization required. + +Bugs fixed: + * incorrect rule depth limit for Rules witness conditions (#3810) + +## 0.108.0 "Participation" (11 Feb 2025) + +This version is compatible with the C# node 3.7.6, but also contains some +Echidna changes preview. Additional RPC extensions are introduced with this +release as well as some important fixes and improvements. + +We recommend to check your configurations wrt NeoFS synchronization options, +a new set of containers was introduced recently, old ones will eventually be +deleted (which won't break syncrhonization, but can delay it a bit). No DB +resync is required unless you want to use the new "SaveInvocations" option. + +New features: + * "Designation" event in RoleManagement native contract starting from Echidna + HF (#3761) + * base64Url encoding and decoding support in StdLib native contract starting + from Echidna HF (#3761) + * NEO candidate registration via NEP-27 payment starting from Echidna HF (#3700) + * ArchivalNode P2P capability (#3778) + * NEP-26 and NEP-27 support everywhere (#3792) + * "SaveInvocations" node parameter that allows to store more detailed + contract invocation data and retrieve it via RPC (#3569) + * "getblocknotifications" RPC API allowing to fetch filtered notifications + from all block execution contexts (#3805) + +Behavior changes: + * updated "upload-bin" command defaults (#3760) + * updated NeoFS containers for all networks (#3759) + * additional AllowNotify call flag for some NEO methods starting from Echidna + HF (#3761) + * Dump*Slot methods removed from vm.Context (#3806) + +Improvements: + * golang.org/x/crypto update from 0.26.0 to 0.31.0 (#3765) + * neotest can load contracts from NEF/manifest files now (#3771) + * more accurate memory management in persisting/processing cycles preventing + OOM conditions in most cases (#3787) + * RPC bindings now have ToStackItem and ToSCParameter methods for structures + (#3794, #3796, #3804) + * dBFT 0.3.2 with improved timers (#3799) + * github.com/consensys/gnark update from 0.11.0 to 0.12.0 (#3800) + * ability to fetch headers from NeoFS (#3789) + +Bugs fixed: + * potentially incorrect handling of misconfigured NeoFS endpoints (#3758) + * duplicate index objects are no longer an error for "upload-bin" (#3763) + * old transfer data removal problem in RemoveUntraceableBlocks configuration, + 0.107.2 regression (#3787) + * zkpbinding module producing code that can't be compiled, 0.107.0 regression + (#3802) + +## 0.107.2 "Obliteration" (13 Dec 2024) + +One more compatible patch-release that introduces `RemoveUntraceableHeaders` +application-level extension allowing to remove untraceable block headers from the DB. +This feature significantly reduces the database size, but for now it is supported in +an experimental mode, use it with care. Other than that, this release includes a fix +of BlockFetcher service that may hang on retry of NeoFS requests preventing the node +from syncing. Also, an improved algorithm of blocks uploading and extended list of +block and index file attributes are supported for `upload-bin` CLI command. + +No configuration update or DB resync is required. However, starting from this release +`NeoFSBlockFetcher` application configuration section is backed by default values for +every parameter except `Addresses` and `ContainerID`, hence if you don't like too +chatty configuration files, feel free to remove all optional parameters. + +New features: + * untraceable headers removal (#3750) + +Behavior changes: + * add `BlockTime` attribute to block objects stored in NeoFS block storage (#3749) + * use `Timestamp` attribute to hold object creation time for block and index objects + stored in NeoFS block storage (#3749) + * extended debug logs for `upload-bin` CLI command (#3751) + +Improvements: + * embed default UnitTestNet node configuration (#3696) + * NeoFS SDK dependency upgrade (#3725, #3756) + * dependent packages updates (#3746, #3747, #3748) + * refactor and speed up `upload-bin` CLI command (#3735) + * backup NeoFS BlockFetcher configuration with default values (#3742) + * reuse more of built-in NeoFS SDK functionality in `upload-bin` CLI handler (#3749) + +Bugs fixed: + * NeoFS BlockFetcher sometimes is hanging during the node startup (#3736) + * RPC server timers are improperly drained (#3737) + * basic unit test chain restore configuration (#3696) + +## 0.107.1 "Narrativization" (06 Dec 2024) + +An urgent version that fixes the problem of intensive CPU usage caused by improper +NeoFS BlockFetcher shutdown on the node's sync process completion and magnified by +additional bug at the peer discovery level. + +No configuration changes or DB resync is required. It is highly recommended to update +from 0.107.0 as soon as possible since described problems affect the speed of blocks +processing and the overall node functionality. + +Behavior changes: + * explicitly enable the list of stable hardforks in default NeoFS testnet + configuration (#3722) + +Improvements: + * decrease NeoFS storage nodes deal timeout for NeoFS BlockFetcher (#3723) + * adjust optimal number of peers for networks with small peer count (#3727) + * don't enable unstable hardforks by default (#3724) + +Bugs fixed: + * "unexpected empty payload: CMDVersion" error on peer disconnection (#3726) + * NeoFS BlockFetcher shutdown (#3728) + * connected peers count is not respected on attempt to gather more node addresses + (#3730) + +## 0.107.0 "Mongrelization" (03 Dec 2024) + +A large update that introduces a major node extension: NeoFS BlockFetcher service and +`util upload-bin` CLI command implemented as a part of [NeoFS snapshot storage +proposal](https://github.com/neo-project/neo/issues/3463). BlockFetcher service, as +an alternative to P2P synchronization mechanism, allows to download blocks and sync +chain from block dumps stored in NeoFS. Starting from this release, NeoSPCC team +maintains chain dumps in NeoFS for N3 and NeoFS public networks. The default NeoGo +node configuration for these networks has been changed to use NeoFS BlockFetcher as +a synchronization mechanism prior to P2P synchronization. Other than that, this +release includes NEP-24 standard support at both compiler and SC bindings generator +levels. A large number of tiny user-facing enhancements is rolled out for RPC +actor/ivoker, `neotest` and `unwrap` packages as far as for CLI utilities. Also, +this release contains a set of NeoGo VM bug fixes inspired by differential VM fuzzing +study conducted by our external contributor @Slava0135 and a set of tiny VM CLI +enhancements introduced by @ixje. + +Some deprecated functionality has been removed according to the schedule, see more +details in the `Behaviour changes` section. Also, for those node operators who would +like to check out our new NeoFS BlockFetcher node extension, we'd recommend to add +corresponding section to the node's configuration (see the default node configuration +for N3/NeoFS networks for example). + +This release is fully compatible with 3.7.5 version of C# node, no DB resync is +needed. + +New features: + * new NeoFS BlockFetcher service that allows to sync node from NeoFS chain dump + (#3515, #3636, #3637, #3632, #3691, #3706, #3668, #3713) + * new `util upload-bin` CLI command that allows to upload blocks from Neo chains to + NeoFS (#3578, #3625, #3626, #3633, #3637, #3638, #3643, #3650, #3662, #3684, + #3686, #3691) + * new `delete` and `ib` VM CLI commands for brealpoints management (#3674) + * NEP-24 standard support (#3560) + * Echidna hardfork introduced, but not yet enabled (please, note that this is only + a preview that includes a part of scheduled changes and will be changed in an + incompatible way before the full support, hence, this hardfork may be enabled for + experimental purpose only) (#3554) + +Behavior changes: + * neotest's AddSystemFee and TestInvoke are bound to Executor state (#3551) + * getversion RPC response is extended with seed list and standby committee (#3540) + * support only two latest versions of Go instead of three (#3567) + * some deprecated functionality is dropped: unmarshalling code for the old + `getpeers` RPC response, `NEOBalance` stackitem deserializer compatibility code, + `serv_node_version` Prometheus gauge metric, outdated RPC error codes support, + block-based web-socket transaction awaiting (#3690) + +Improvements: + * documentation updates (#3545, #3663, #3666, #3678, #3683, #3708) + * netmode package is extended with public NeoFS chain IDs (#3539) + * dBFT library upgrades (#3541, #3711) + * migration to Docker Compose V2 (#3547) + * system fee required for contract deployment in neotest is precisely calculated + (#3551) + * ability to customize awaiting options for PollingBased RPC waiter (#3556) + * Go 1.23 support, bump minimum required Go version up to Go 1.22 (#3567) + * a set of dependent libraries upgrades (#3570) + * extend the list of supported SC parameters for RPC actor/invoker (#3583) + * Null stackitem result handling for autogenerated RPC bindings (#3584) + * macos-12 support is removed, macos-14 support is added (#3657) + * allow to get NEP-11/NEP-17 balances via CLI using account address only (#3659) + * expose VM slot getters (#3677) + * add `unwrap.ErrNull` error to handle Null stackitem returned (#3695) + * extend web-socket notification subsystem with notification parameter filters + (#3689) + * explicitly prohibit unknown configuration fields for `contract generate-*` CLI + commands (#3708) + * extend compiled smart contract identifier in neotest cache (#3709) + * move `neogo_version` metric out of `network` package (#3712) + +Bugs fixed: + * fees of ditched transaction are not cleared from mempool (#3537) + * extension of SC permission descriptor doesn't clear wildcard status (#3544) + * example of NeoGo VM script fails VM execution (#3593) + * a call to `getunclaimedGas` of native Neo contract for account with zero balance + results in VM failure (#3589) + * `MODMUL` VM opcode handler returns wrong results for negative arguments (#3599) + * neotest coverage extension panics on attempt to collect coverage for contract with + missing debug information (#3600) + * `MODPOW`VM opcode handler returns wrong results for negative base (#3649) + * node panic on SIGHUP (#3661) + * HTTP return code of RPC requests diverges from C# RPC server's behaviour (#3665) + * a set of bugs in unit tests (#3442, #3680) + * `POPITEM` VM opcode handler counts stack references improperly (#3688) + * `PACKMAP` VM opcode handler ignores duplicating map keys (#3685) + * chain restore from genesis block fails with StateRootInHeader extension enabled + (#3697) + * hardfork-dependent methods are not included into native contract metadata starting + from the hardfork height (#3704) + * outdated keyword usage in Dockerfiles (#3710) + +## 0.106.3 "Lyophilization" (29 Jul 2024) + +This 3.7.5-compatible version includes a number of important fixes, so please +upgrade your nodes. Some minor extensions were also added. + +Resynchronization (or state reset) is required for testnet (because of a bug +leading to state difference since 4368840), but not required for mainnet. + +New features: + * embedded mainnet/testnet/NeoFS node configuration files (#3477, #3504) + +Behavior changes: + * CLI no longer panics if error occurs (#3495) + * MaxTraceableBlocks is 17280 now for NeoFS networks (#3518) + * minimal default RPC `SessionExpirationTime` is 5s now (#3529) + +Improvements: + * RPC actor interface extension with WaitSuccess method (#3491) + * Signers() API for RPC invokers (#3492) + * SignerAccounts() API for RPC actors (#3492) + * getpeers RPC extension with the user agent and last known block height data + (#3481) + * OnExecHook() API for VM (#3460) + * more details in witness verification error message (#3508) + * CLI help and error string format unification (#3495, #3520) + * CLI library (github.com/urfave/cli) upgrade to 2.27.2 (from v1 API, #3495) + * microoptimization of extensible sender list calculation (#3500) + * microoptimization of chain dump code (#3514) + * documentation and error messages (#3526, #3527) + +Bugs fixed: + * RPC `SessionExpirationTime` could be zero in some configurations (#3529) + * panic in WSClient unsubscription code in some multithreaded cases (#3532) + * missing PrimaryIndex in Ledger's getBlock() result (#3534) + * contract manifests with null groups were accepted (#3523) + * contract manifests with invalid features were accepted (#3523) + * contract manifests with null trusts were accepted (#3523) + * WSClient deadlock in some disconnection cases (#3535) + +## 0.106.2 "Keratinization" (13 Jun 2024) + +A 3.7.5-compatible version introducing new Domovoi hardfork that brings two fixes to +the protocol: using executing contract state to check contract call permissions +(included into this NeoGo release) and proper VM items refcounting for +System.Runtime.GetNotifications handler (not included into this NeoGo release because +we've never had this bug). Since the second bug is C#-specific and does not lead to +the state differences in mainnet/testnet, we've decided not to break the NeoGo node +to follow pre-Domovoi C# node implementation. Thus, differences in application logs +for several T5 transactions before Domovoi hardfork are expected and won't be fixed. + +Please, ensure your node configuration includes the Domovoi hardfork. No DB +resynchronisation is required. + +New features: + * Domovoi hardfork scheduled for 5570000 block of mainnet and 4144000 block of T5 + testnet (#3476, #3473, #3486, #3487) + +Behavior changes: + * hide node logs timestamp if the node is running not in TTY (#3468) + * distinguish log level for various node peer disconnection reasons (#3469) + +Improvements: + * ensure NeoFS nodes are configured when processing NeoFS oracle requests (#3455) + * NeoFS SDK dependency upgrade (#3483) + * ensure System.Runtime.GetNotifications handler can't break the MaxStackSize + constraint before and after Domovoi hardfork (#3485) + +Bugs fixed: + * deployed contract script is included into wallet's account (#3470) + * updated contract state is used to verify contract call permissions before the + Domovoi hardfork (#3473) + +## 0.106.1 "Implication" (3 Jun 2024) + +An urgent release that fixes mainnet state difference at block 5462944 which halts +blocks processing starting from the height 5468658. This release requires full node +DB resynchronization for mainnet nodes. T5 testnet DB state is not affected by this +bug (at least up to the current 4087361 height). Thus, DB resynchronisation may be +skipped for testnet nodes. No configuration changes implied. + +Bugs fixed: + * mainnet state difference at block 5462944 caused by runtime notification + permissions check against the updated contract state instead of executing state + (#3472) + * unused neofs-contract dependency in the node modules (#3458) + +## 0.106.0 "Zephyranthes" (21 May 2024) + +We're rolling out a large set of updates including all of Neo 3.7.4 protocol changes: +native contracts update functionality and extended native contract APIs united under +the upcoming Cockatrice hardfork. For smart contract developers an ability to +generate smart contract bindings with dynamic hash is supported as well as a number +of useful extensions for `unwrap` package, compatible NNS smart contract RPC binding +and a lot of other handy enhancements. This release also includes a couple of severe +regression bug fixes affecting the node state. As a bonus of fixing a wide range of +failing tests, we've refactored the node services start and shutdown procedures to +make it more stable. This version drops support for Go 1.19 and requires 1.20+ to +build (with Go 1.22 supported). + +Notice that this release requires full node resynchronization for both mainnet +and testnet nodes. Please pay a special attention to the Cockatrice hardfork schedule +and check your configurations. It should be configured for 3967000 of T5 testnet and +5450000 of mainnet. To ensure compatibility your node must be configured +appropriately. + +New features: + * native contracts update functionality bound to hardforks (#3402, #3444) + * Cockatrice hardfork planned for 5450000 block of mainnet and 3967000 block of T5 + testnet (#3402, #3448, #3402, #3446) + * `CommitteeChanged` events are emitted by NeoToken native contract starting from + Cockatrice hardfork (#3351, #3448) + * `getCommitteeAddress` method of NeoToken native contract is available starting + from Cockatrice hardfork (#3362, #3402) + * `keccak256` method of CryptoLib native contract is available starting from + Cockatrice hardfork (#3301, #3402) + * dynamic contract hash support for smart contract bindings (#3405) + * support `StringCompressed` API for `crypto.PublicKey` (#3408) + * support `Copy` API for `transaction.Transaction` and `payload.P2PNotaryRequest` + (#3407) + * extend `verifyWithECDsa` method of native CryptoLib contract with Keccak256 hasher + starting from Cockatrice and add an example of custom Koblitz-based and + Keccak256-based transaction witness verification (#3425) + * autogenerated `nativehashes` package (#3402, #3431) + * introduce `unwrap.Exception` type for better RPC invocation result exceptions + detection (#3438) + +Behavior changes: + * Neo Name Service smart contract RPC binding follows the official N3 implementation + (#3291) + * clear LastGasPerVote NeoToken account info on unvoting (#3349) + * return fault exception (if any) in `unwrap` RPC client helpers (#3356) + * move P2PNotary designation role out of P2PSigExtensions (#3452) + +Improvements: + * support `smartcontract.Convertible` interface as RPC Actor and Invoker call + parameters (#3297) + * allow to import multisignature account into wallet without WIF and password + specified (#3293) + * upgrade go-ordered-json dependency to avoid possible node state incompatibility + issues caused by smart contract manifest marshalling (#3331, #3333) + * Go 1.22 support, bump minimum required Go version up to Go 1.20 (#3336) + * a number of dependent libraries are updated to the fresh versions (#3338, #3418) + * improve error message of `System.Crypto.CheckMultisig` interop API handler (#3374) + * upgrade dBFT library to v0.2.0 (#3371, #3413) + * increase `ValidUntilBlock` value for transactions generated by CLI commands + (#3376) + * documentation updates (#3375, #3382) + * let default Notary Actor options be customizable (#3394) + * extend `getversion` RPC response with RPC server settings (#3386) + * make errors related to wallet opening more detailed (#3389) + * adjust error messages of `setExecFeeFactor` and `setStoragePrice` methods of + native PolicyContract (#3406) + * make neotest chain constructor options customizable (#3404) + * format improvements for CLI commands description (#3410) + * make state dumps comparator script more verbose (#3411) + * refactor storage `Get` implementation for BoltDB (#3441) + * add `updatecounter` field to the resulting items of `getnativecontracts` RPC API + call (#3439) + +Bugs fixed: + * node panic on committee missing from node configuration (#3294) + * autogenerated RPC bindings do not follow Go naming convention (#3299) + * a batch of failing tests is fixed (#3306, #3313, #3321, #3332, #3337, #3335, + #3344, #3340, #3350, #3355, #3364, #3368, #3377, #3393, #3397, #3392, #3398, + #3400) + * outdated minimum required Go version of boilerplate contract generated by + `neo-go contract init` (#3318) + * outdated address used as an example in RPC client documentation (#3327) + * ungraceful node services shutdown procedure (#3307) + * logging data race on node services shutdown (#3307) + * Map parameter support is missing from RPC server handlers (#3329) + * smart contract storage iterator prefix wasn't copied while iterating over values + (#3336) + * RPC error with -511 code (insufficient funds) returned on `sendrawtransaction` RPC + request should cover multiple cases of sender's insufficient funds (#3360, #3361) + * reentrancy possibility for Notary deposit withdrawal (#3357) + * null `findstorage` RPC response in case of missing storage items (#3385) + * uninitialized named return variables in compiler (#3401) + * wrong debug sequence points after `JUMP*` instructions shortening by compiler + (#3412) + * corrupted genesis block record and application log caused by improper Conflicts + attribute processing (#3437) + * false positive DB-based blocked accounts detection in native PolicyContract leaded + to invalid committee computations and reward distribution made by NeoToken (#3443) + +## 0.105.1 "Enumeration" (12 Jan 2024) + +This is another v3.6.2-compatible release that fixes mainnet state difference at +block 4688591. It is confirmed to have the same state up to 4723K height (which +is current), but to get proper mainnet state you need to resynchronize your node +from the genesis. T5 testnet state is not affected (at least up to the current +3323K height), thus DB resynchronisation may be skipped for testnet nodes. + +Improvements: + * better network services logging (#3287, #3290) + +Bugs fixed: + * state difference at block 4688591 of N3 mainnet caused by difference at + characters escaping during manifest's `Extra` field JSON serialisation (#3286) + +## 0.105.0 "Designation" (29 Dec 2023) + +We're rolling out an update for NeoGo nodes that contains a number of user-facing +API improvements for RPC web-socket notification subsystem, CLI utility, wallet +related packages and not only. Try out our new `--await` CLI option for those +commands that relay transactions to the network to automatically wait for the +on-chain transaction execution result. Subscribe for new block headers with +extended RPC notification subsystem interface. Use contract-based accounts +provided by `wallet` package and `neotest` framework to sign transactions. These +and a set of other improvements are available to our users while this release is +staying compatible with 3.6.2 version of C# node. + +This version also delivers a bug fix for consensus node panic caused by improper +native NeoToken cache initialisation. Moreover, there's a set of RPC server side +improvements, thus, we recommend to upgrade both consensus and RPC nodes to +provide more stable consensus node functioning and extended user APIs functionality. +No database resynchronisation is needed. + +New features: + * block headers RPC web-socket subscription (#3252) + * --await option to synchronize on transaction execution for CLI commands (#3265) + * partial session-based RPC iterator unwrapping (#3274) + * contract-based transaction signers in neotest framework (#3233) + * AMD64 release binaries for macOS (#3251) + * complex contract signature schemes in wallet.Account (#3256) + +Behavior changes: + * basic RPC subscription filter validity checks are implemented on both RPC + client and RPC server sides (#3258) + * filter of notary request event RPC subscription is extended with `type` field + (#3236) + * if available, use block headers RPC web-socket subscription for transaction + awaiting via `waiter` package API (#3283) + +Improvements: + * add smart contract storage limits to interop utilities (#3232) + * extend ZKP examples documentation with additional links to PoT ceremony + response files (#3234) + * support Go types in VM emitter API (#3237) + * documentation update (#3239, #3242, #3246) + * BoltDB (go.etcd.io/bbolt) dependency upgrade (#3250) + * CLI code refactoring (#2682) + * extend wallet package to work with byte slice based wallets (#3255) + * export RPC client side transaction awaiting functionality via `waiter` package + (#3265, #3283) + +Bugs fixed: + * remove stale `updatehistory` section of `getnativecontracts` RPC response (#3240) + * immediately check RPC client initialisation on access to blocks RPC subscription + API (#3257, #3261) + * fix CN panic caused by unexpected call to native NeoToken cache (#3253) + * make "automatically generated" warning of all automatically generated files + follow the standard (#3280) + +## 0.104.0 "Globalization" (27 Nov 2023) + +We're updating NeoGo to push out a number of useful updates and protocol +extensions as well as make it compatible with 3.6.2 version of C# node. The most +invasive behaviour changes are VM-level protocol constraints imposed on the size +of serialized stackitems and `NativeActivation` node setting removal. This version +also delivers a set of smaller useful changes in the interoperability layer and +native contract functionality including System.Runtime.CurrentSigners interop, +`strLen` StdLib method and PolicyContract-based transaction attributes pricing +as far as a user-facing `canceltx` CLI command and community-requested +`--relative-path` CLI option. + +Node operators must resynchronize their nodes to get fully compatible state +(which is confirmed to be compatible with 3.6.2 for current mainnet up to +block 4483627 and T5 testnet up to block 3078762). Please, ensure your node +configuration doesn't contain `NativeActivations` protocol configuration section +as this logic is hidden under Hardforks starting from the current release. + +New features: +* System.Runtime.CurrentSigners interop allowing to get signers of the currently + loaded transaction (#3058) +* `strLen` method to native StdLib contract (#3208) +* transaction attributes pricing regulation via native PolicyContract (#3155) +* `canceltx` CLI command as an alternative to unsupported `canceltransaction` RPC + request (#3223, #3214) + +Behaviour changes: +* reduce maximum allowed stackitem.Item size (#3185) +* restrict maximum allowed NEF file size and prohibit large contracts deployment (#3186) +* bind `NativeActivations` node setting to the `Hardforks` setting (#3212) +* introduce serialization limit to stackitem.Item and fail large contracts deploy + wrt this setting (#3218) +* add customizable `MaxRequestBodyBytes` and `MaxRequestHeaderBytes` RPC server + configuration setting (#3221) + +Improvements: +* provide more detailed error on attempt to read non-existent service wallet + provided via node configuration file (#3210) +* optimize emit of imported code for autogenerated RPC bindings (#3215) +* documentation update (#3203, #3222) +* add `--relative-path` CLI flag allowing to override configuration-specific relative + paths (#3206) +* update code owners (#3216, @fyrchik will live in our hearts forever) + +Bugs fixed: +* unify messages of RPC errors according to the RPC errors NEP (#3199) +* limit maximum allowed number ad depth of transaction signers and witnesses per + RPC request that accept transaction (#3207, #3221) +* forbid unknown fields usage in the node configuration file (#3209) +* enable hardfork-dependant code starting exactly from the block height specified + in the node configuration (#3211) +* require Notary native deposit to be valid for at least one subsequent block after + deposit transaction acceptance (#3211) +* deduplicate unnamed event types for autogenerated RPC bindings and make the + binding generation order strictly defined and stable (#3215, #3220) +* do not panic on trying to compile an import cycle (#3215) +* state difference at block 3002333 of T5 testnet caused by difference at characters + escaping during manifest's `Extra` field JSON serialisation (#3225) +* properly start node services that depend on native RoleManagement contract data + with genesis `Roles` protocol configuration setting specified (#3229) + +## 0.103.1 "Verification" (09 Nov 2023) + +An urgent 3.6.0-compatible version that contains a hotfix for the bug that +prevents node from starting from the existing database every new dBFT epoch. +Also, the release includes a bugfix that fails any non-zero NEO and GAS +roundtrips from accounts with zero balance. + +Although the DB format and storage states were not affected by this release, +the node resynchronization is still recommended for those who want to keep +correct application logs. Resynchronization can be skipped if you're ok with +wrong application logs for some transactions (e.g. CN doesn't care, and RPC node +cares a lot). + +Bugs fixed: +* properly initialize cache of native NeoToken contract every new dBFT epoch (#3187) +* forbid non-zero NEO and GAS roundtrips from accounts with zero balance (#3191) + +## 0.103.0 "Backwardation" (20 Oct 2023) + +A minor 3.6.0-compatible version of NeoGo with new salty ZKP-related +functionality, a large batch of deprecated APIs removal and an experimental +genesis block extension. Build Groth-16 circuits, generate proofs and deploy +verification contracts easily with the new end-to-end ZKP example and `zkpbinding` +NeoGo API. Setup node roles via the node configuration starting from the genesis +block without any painful multisignature transaction forming and invoke any +custom script in the genesis-level transaction with the new `Genesis` protocol +configuration extension. And don't forget to move from deprecated RPC client +APIs, as a lot of them have been removed. + +New node configuration format is finally adapted and the deprecated configuration +sections are permanently removed according to the schedule. Pay attention to the +`SecondsPerBlock` protocol configuration section that was replaced by +`TimePerBlock`, a set of node and services address- and port- related configuration +section that were replaced by the new `Addresses` format, direct `UnlockWallet` +consensus configuration that was replaced by a separate `Consensus` section and +a set of node-specific protocol configurations that were moved under a separate +`P2P` section. + +This release fixes a set of bugs including a vulnerability introduced by changes +in the transaction conflicts storage scheme implemented in #3061 (a part of +0.102.0 release). With the patch presented, it's impossible to uncontrollably +pollute the storage with malicious conflicting records. + +This version is fully compatible with C# node 3.6.0. However, it requires +complete resynchronization on upgrade due to the database storage scheme changes. + +New features: +* API for Groth-16 verification contracts autogeneration and end-to-end example for + proving and verifying statements on NeoGo (#3043, #3146) +* introduce genesis protocol extensions: default node roles designation and genesis + transactions (#3168) + +Behaviour changes: +* a lot of deprecated functionality is dropped: RPC client old APIs, shared + Notifications channel of WebSocket client, SecondsPerBlock protocol + configuration, old way of services and node address and port configuration, old + P2P related application settings configuration, direct UnlockWallet consensus + configuration, direct node-specific protocol configuration (#3150) +* database scheme is changed by new way of storing the transaction conflicting + records (#3138) + +Improvements: +* NeoFS SDK dependency upgrade (#3129) +* gnark and gnark-crypto dependencies upgrade (#3145, #3162, #3163) +* introduce timeout restriction for BoltDB opening (#3148) +* bump code coverage uploading action version (#3153) +* Go 1.21 support, bump minimum required Go version up to Go 1.19 (#3157) +* upgrade golang.org/x/net version (#3158) +* add protocol hardforks configuration to the `getversion` RPC response (#3160) +* format autogenerated smart contract bindings with accordance to standard `go fmt` rules (#3164) +* add "DO NOT EDIT" warning to the autogenerated smart contract bindings (#3167) + +Bugs fixed: +* avoid race between `getnextblockvalidators` RPC call handler and blockchain's + block handler routine, significantly refactor native validators caching scheme + (#3110) +* do not panic on failed NeoFS oracle requests (#3129) +* enable Notary subsystem in NeoFS mainnet configuration (#3136) +* reorganize storage scheme for transaction conflicting records to avoid possible attack (#3138) +* properly deserialize wildcard trusts field of smart contract manifest (#3140) +* use more strict GAS limit for transaction network fee calculation via RPC call (#3141) +* avoid WebSocket client request blocking (#3142) +* properly emit big uint64 constants during smart contract compilation (#3147) +* avoid race access to the node version by tests (#3149) +* make FindStorage* RPC client APIs to be always compatible with NeoC# RPC server (#3166) + +## 0.102.0 "Aberration" (06 Sep 2023) + +Long-awaited feature-packed 3.6.0-compatible version of NeoGo with all the +appropriate protocol updates and a set of tasty improvements and bug +fixes. Groth16 ZKP proof checks are there for contract developers as well as +new opcodes. A huge number of improvements went into the RPC server and +client, new RPCs, improved contract binding generator and standardized +extended error codes make developing dApps much easier. + +Users of smart contract utilities and RPC-related Prometheus metrics are +advised to take a look at the deprecated APIs removal schedule +(ROADMAP.md). This version was delayed for quite some time (waiting for 3.6), +so the next minor release (0.103.0) will remove a substantial amount of +compatibility code. It's expected to be released in November with 3.6 protocol +compatibility (3.7 cycle is likely to require more time). + +This is also the first version that drops support for Go 1.17 and requires +1.18+ to build (with Go 1.20 supported). Using generics in smart contracts is +not supported yet, but some elements of this support can be implemented in +future versions. + +Mainnet and testnet node operators, please pay attention to the Basilisk +hardfork schedule and check your configurations. It will happen at block +2680000 for T5 testnet and 4120000 for mainnet. To ensure compatibility your +node must be configured appropriately. This version requires DB +resynchronization, so schedule your updates accordingly. + +New features: + * ZKP proof checking support via CryptoLib native contract (operations with + BLS12-381 curve points) (#2940, #3042) + * System.Storage.Find interop now support "Backwards" flag to iterate in the + opposite direction (#2952) + * `util ops` CLI utility that pretty-prints VM script (#2965) + * `notarypool_unsorted_tx` Prometheus metric for notary-enabled nodes (#2696) + * `CloseNotificationChannelIfFull` WSClient option allowing the client to + close notification channel on overflow (#2988) + * autogenerated RPC bindings for contract events and conversion code from + stackitem.Item to structure (#3008, #3035, #3036, #3087) + * event type inference from contract code (#3008) + * dynamic contract hashes support for RPC bindings (#3012) + * "Basilisk" protocol hardfork support (#3056, #3080, #3086, #3104) + * ABORTMSG and ASSERTMSG VM opcodes (#3066) + * standardized RPC error codes (#3063) + * `findstorage` and `findstoragehistoric` RPC support (#3099) + * `getstoragehistoric` RPC support (#3099) + * `getrawnotarypool` and `getrawnotarytransaction` RPC (#3098) + +Behaviour changes: + * deprecated `FromAddress` smart contract helper is dropped from the `util` + interop package (#2941) + * a number of deprecated RPC-related Prometheus counters are permanently + removed (#2941) + * NEP-2 account label will be asked on account import via CLI (#2889) + * hashes and states of native contracts are now accessible via native + ContractManagement API (#2991) + * `serv_node_version` gauge Prometheus metric is marked as deprecated and + replaced by `neogo_version` and `server_id` (#3009) + * strict contract script check is back with Basilisk hardfork, it was + previously removed in 0.101.3 for 3.5 protocol compatibility (#3056) + * JSON number deserialization (via StdLib.jsonDeserialize) changes with + Basilisk hardfork allowing for more precision and handling more corner + cases (#3080) + * notification type errors are treated as fatal after Basilisk hardfork, + before it they're just logged (#3086) + * NULL and non-UTF8 items are not allowed for String event types (#3086) + * `Conflicts` and `NotValidBefore` transaction attributes are no longer NeoGo + extensions, they can be used on regular networks (#2962) + +Improvements: + * documentation and example improvements (#2945, #2972, #2979, #3020, #3084, + #3099, #3114, #3116, #3119, #3121) + * `getproof` and `verifyproof` RPC API support in RPC client (#2942) + * Go 1.20 support, bump minimum required Go version up to Go 1.18 (#2908) + * faster state reset process (#2819) + * optimized voting data storage scheme for NEO contract (#2892, #2893) + * NeoFS SDK dependency upgrades (#2995, #3032) + * special exported error returned in case of WSClient disconnection (#3000) + * automatic guessing of contract and manifest filenames for `contract + compile` CLI command and `loadnef` VM CLI command (#3013) + * support for pushing stackitem.Convertible objects via VM script emitter (#3016) + * economic adjustment for ranking of transactions with `Conflicts` attribute (#3031) + * `google.golang.org/grpc` dependency upgrade fixing high severity security + vulnerability (#3055, gRPC is only used to communicate with NeoFS nodes in + the oracle service) + * enforce default RPC server values when it's used as an independent package + (not a part of node) (#3107) + * unwrap.Nothing function for RPC clients (#3117) + * address and reverse hash display in opcode dumps (#3115) + +Bugs fixed: + * invalid peer port type returned by `getpeers` RPC response (#2914) + * invalid data source for `mempool_unsorted_tx` Prometheus metric (#2969) + * dBFT library upgrade fixing the ability of a single node to speed up the + process of new blocks creation for the whole network (#3018, + nspcc-dev/dbft#75) + * failing CALLT instructions in VM CLI for loaded NEF files (#3020) + * missing signers check for on-chain conflicting transactions (#3061) + * incorrect sequence point boundaries in debug data (#3074) + * compiler panic on encountering generic code (#3041) + * potential pooled fallback notary transaction changes (#3108) + * lost LastGasPerVote value on NEO state deserialization for non-voting + accounts (#3122) + +## 0.101.4 "Yarborough" (01 Aug 2023) + +Another one 3.5.0-compatible version that is aimed to fix T5 testnet state +difference that has happened at block 2336911 which leads to inability to process +new blocks since 2418703. The issue is fixed by allowing JSON numbers +unmarshalling from scientific notation to Integer stackitem. Maximum parsing +precision for such numbers is currently restricted by 53 bits. This is a +temporary C#-compatible solution that is likely to change in the future versions +when an appropriate C# node bug is fixed (neo-project/neo#2879). + +A set of minor bug fixes is included as well to flush some of the long-awaited +changes that were blocked by the 0.102.0 release delay (caused by v3.6.0 C# node +release delay). In particular, invalid headers returned by an RPC server for +error responses, invalid format of incremental dumps created by CLI and deadlock +on unhealthy RPC server shutdown. Long-awaited `--config-file` CLI option to +start the node providing a single configuration file is added. + +T5 testnet chain requires a complete resynchronization for this version. Mainnet +chain resynchronization is recommended, but not required. + +New features: + * `--config-file` CLI option allowing to start the node with a single configuration file (#3014) + +Improvements: + * blockchain Notary and Oracle services documentation improvement (#2972) + * BoltDB (`go.etcd.io/bbolt`) dependency upgrade that fixes a number of Windows-related issues (#3034) + +Bugs fixed: + * panic on node start with invalid configuration (#2968) + * deadlock on unhealthy RPC server shutdown (#2966) + * improper WSClient notification channels managing after disconnection (#2980) + * missing Prometheus metric initialisation on node start (#2992) + * invalid initialisation of native contracts cache (#2994) + * incorrect way of incremental DB dumps creation (#3047) + * Notary contract is allowed to be a sender of main Notary request transaction (#3065) + * discrepancy in signer's witness scope parsing on the RPC server side (#3060) + * Invoker calling API isn't allowed to accept nil parameter (#3067) + * contract RPC Client unwrapper helper can't handle missing contract case (#3072) + * JSON numbers can't be unmarshalled to stackitem from scientific notation (#3073) + * invalid content-type header returned by RPC server on error responses (#3075) + +## 0.101.3 "Yuckiness" (08 Jul 2023) + +Yet another 3.5.0-compatible emergency version that removes scrupulous +instructions check of smart contract's script on contract deployment or update. +Presence of this check leads to the known T5 testnet state incompatibility +(since 1670095) which causes inability to process new blocks (since 2272533). +It should be noted that the corresponding check was accidentally removed from +the reference C# node implementation way back in neo-project/neo#2266 and added +again in neo-project/neo#2849 which is planned to be a part of the upcoming +3.6.0 C# node release. Thus, changes made in the presented 0.101.3 release will +be reverted afterwards and strict contract script check will be present in the +next 3.6.0-compatible version of NeoGo node. + +T5 testnet chain requires a complete resynchronization for this version. Mainnet +chain resynchronization is recommended. + +Bugs fixed: + + * extra strict contract script check on contract deployment or update is + removed (#3052) + +## 0.101.2 "Excavation" (29 Jun 2023) + +One more (and unexpected one!) 3.5.0-compatible version that fixes a VM bug +leading to mainnet state incompatibility (since 3672783) which in turn leads +to inability to process new blocks (since 3682290). + +Mainnet chain needs to be resynchronized for this version. + +Improvements: + * documentation updates (#3029, #2990, #2981) + +Bugs fixed: + * incorrect handling of empty Any-type parameter for RPC invocations (#2959) + * incorrect state rollbacks in case of exception during cross-contract call + when the call is made from non-TRYing context (#3046) + +## 0.101.1 "Shallowness" (17 Mar 2023) + +Another 3.5.0-compatible version that delivers important bug fixes and +provides a new API to be used by NeoFS. An upgrade is recommended, the DB +doesn't need to be resynchronized. + +New features: + * internal RPC client for deeply integrated applications like NeoFS (#2916) + +Improvements: + * documentation updates (#2879, #2880, #2893, #2917, #2920, #2936) + * code style, spelling and updated linter fixes (#2884, #2922, #2933) + * NEP-2 import password can be provided via config file now (#2887) + * custom stack item deserialization limit is available via public APIs now (#2904) + * RPC client endpoint can be retrieved via public API (#2915) + * dependency updates (#2919, #2929) + * WSClient now copies filter parameters to Subscribe* and Receive* methods + improving code safety (#2937) + +Bugs fixed: + * name parameter ignored for wallet import command (#2887) + * incorrect RPC binding code generated for Any return type (#2918) + * memory leak on active peer disconnection (#2924) + * consensus process deadlock (#2930) + * dBFT deadlock in "committed at previous view" scenario (#2935) + * panic in RPC waiter code (#2938) + +## 0.101.0 "Shortness" (13 Jan 2023) + +This release delivers an important fix for block execution application logs +and requires a resynchronization, therefore it's 0.101.0 (even though it's +also 3.5.0-compatible). It fixes some other minor problems as well (the other +most notable change probably is in the compiler), so we recommend upgrading. + +Improvements: + * updated golang.org/x/* dependencies (#2854) + * CLI help and required flags handling fixes (#2852) + * transfer data storage optimization (#2865) + * network's magic number is stored (and checked against the config on + startup) in the DB now, reducing potential for node operator errors (#2867) + +Bugs fixed: + * in rare cases nodes could request an invalid number of blocks from peers + leading to disconnect (#2846) + * outdated documentation fixes (#2860, #2876) + * application logs for blocks that contained GAS spends for transaction fees + contained (and returned from getapplicationlog RPC) incorrect (off by one) + values for amount in Transfer events; transaction application logs were not + affected by this, but data returned to RPC event subscribers could + potentially be (#2865) + * findstates RPC returned an error instead of an empty data set for valid + contracts that have no data (unlike C# node, #2866) + * miscompiled shadowed range loop variable definition (#2871) + * missing (compared to C# node) explicit (even though null) 'exception' field + in the getapplicationlog RPC output from server (#2872) + +## 0.100.1 "Chaptalization" (28 Dec 2022) + +This is a tiny update that 99.99% of users can easily skip. The reason for +this release is the need to fix compatibility with the NeoFS mainnet +sidechain and have some stable version to be used there. In any other case it +can be ignored, but if you still decide to upgrade you don't need to +resynchronize. + +Behaviour changes: + * Aspidochelone fork is made to include ContractManagement native contract + deploy/update methods call flags change, initially it was an unconditional + part of 0.99.0 NeoGo release (or 3.2.0 C# version), but this behavior is + incompatible with the NeoFS mainnet sidechain; the change to the fork logic + does not affect any other public networks (mainnet/testnet) and any new + networks that have Aspidochelone enabled since block 0 (#2848) + +Improvements: + * more robust NEP-11 divisibility check in the RPC server code (#2841) + * microoptimization for some debug log messages (#2842) + * additional fuzz tests for integer serialization and script parsing code (#2851) + +## 0.100.0 "Centuplication" (08 Dec 2022) + +A 3.5.0-compatible version of NeoGo with all the appropriate protocol updates +and a number of other changes. The most notable ones are configuration +updates. New features and some long-standing inconsistencies required for some +changes and this release brings them with it. Old configurations are still +supported and will work the same way (except for one minor exception in +VerifyBlocks that is only supposed to be used for tests/development), but we'd +like to highlight that all of the old settings will be removed in ~6 months, +so please review these updates and update your configurations. For public +networks the best way to go is to take the new versions from the "config" +directory and then adjust for particular scenario if needed. + +This release requires a complete resynchronization due to native contract +changes, so please schedule your updates appropriately. + +New features: + * System.Runtime.LoadScript syscall (and appropriate smart contract interops) + allowing to load/run dynamic code (#2719) + * PUSHT/PUSHF VM instructions allowing for simpler/cheaper booleans (#2770) + * ContractManagement native contract was extended with ID->hash mappings and + `getContractById` and `getContractHashes` methods (#2702, #2837) + * LogLevel application configuration option that can be changed on SIGHUP + (#2831) + * additional type data generated by the compiler that then can be used by the + RPC bindings (SDK/contract wrapper) generator, this allows for complex + types (structures/arrays/maps) to be easily represented/handled in + contract-specific RPC code (#2828) + * multiaddress listeners for all services and P2P, this changes the old + Address/Port (and AnnouncedPort for P2P) configuration scheme to more + generic Addresses list, the old configuration is still supported, but is + deprecated and will be removed in future node versions (#2827, #2839) + +Behaviour changes: + * Aspidochelone fork block for NeoFS sidechain mainnet configuration is + rescheduled again (#2823, #2830) + * Blockchain's GetHeaderHash() method now accepts uint32 for parameter + (#2814) + * pre-0.97.3 and pre-0.99.0 deprecated compatibility fields and logic were + dropped from the result.Version (`getversion` RPC result) structure (#2786) + * SecondsPerBlock protocol configuration variable was replaced with + TimePerBlock allowing for sub-second precision, SecondsPerBlock is still + supported, but will eventually be removed (#2829) + * AttemptConnPeers, BroadcastFactor, DialTimeout, ExtensiblePoolSize, + MaxPeers, MinPeers, PingInterval, PingTimeout, ProtoTickInterval settings + were moved into the new P2P section, timing parameters now use Duration + type allowing for more human-friendly values in many cases, old parameters + are still supported, but will eventually be removed (#2827) + * consensus (dBFT) is configured as a separate service now that can be + enabled/disabled (and can work in "watch only" mode without a wallet), the + old direct "UnlockWallet" specification in "ApplicationConfiguration" is + still supported, but is deprecated and will be removed in future versions + (#2832) + * GarbageCollectionPeriod, KeepOnlyLatestState, RemoveUntraceableBlocks, + SaveStorageBatch and VerifyBlocks settings were moved from + ProtocolConfiguration to ApplicationConfiguration; old configurations are + still supported, except for VerifyBlocks which is replaced by + SkipBlockVerification with inverted meaning (and hence an inverted default) + for security reasons; this also affects NewBlockchain and neotest APIs + (#2833) + +Improvements: + * more user-friendly error and help messages in some cases (#2824, #2834) + * faster node startup time and smaller memory footprint for networks with + lots (1-2M+) of blocks (#2814) + * minor documentation fixes (#2834, #2838) + +Bugs fixed: + * transactions with system fee or more than MaxBlockSystemFee are no longer + accepted into the mempool, preventing a form a network-wide DoS (#2826) + * deprecated WSClient subscription methods not working correctly when filters + are being used (#2836) + +## 0.99.7 "Hyalinization" (23 Nov 2022) + +Bugs, terrestrial arthropods with at least six legs. Sometimes we don't notice +them, sometimes we ignore them, but they do exist and there are times when +they need to be dealt with in one way or another. Most of the bugs fixed in +this release have a little less legs than proper insects, they're far from +being as critical as the ones fixed in the previous release and yet we +consider them annoying enough to warrant this new version of NeoGo to be +published. We don't want to see our users fighting these pesky creatures in +the wild, better update and have a troublefree experience. + +If you're not affected by #2815 (requests for verbose transaction data and +application logs failed for some specific mainnet transaction(s)) you may +leave the DB as is with this upgrade, if you want it to be solved then please +resynchronize. + +Behaviour changes: + * "loadtx" VM CLI command now uses transaction system fee value as the + default GAS limit for the context, unless --gas is used (previously it + wasn't limited, #2816) + * SendRawTransaction RPC client method will now always return transaction + hash, even if error occured, this also affects Actor APIs and allows to + handle some specific errors (like transaction already present in the + mempool) in more appropriate way (#2817) + * Actor and NotaryActor Wait() wrapper methods now handle "already exists" + and "already on chain" errors as non-errors, waiting (if needed) and + returning proper results (#2817) + +Improvements: + * reworked network discoverer/connection manager that has a better picture of + the network (especially if it's a small one), doesn't try to make additional + connections (even when having less than MinPeers of them) when there are no + more real addresses to connect to, establishes stable connections faster in + containerized environments and does more connection attempts when MinPeers + setting is not satisfied, but there are potential candidates (#2811) + * VM invocation stack handling microoptimization (#2812) + * load* VM CLI commands now accept --gas parameter to set the amount of GAS + to be used in this context (#2816) + * better logging during state reset process (#2813) + * subscription-based transaction waiter now handles already accepted + transactions faster (~immediately vs waiting for block timeout previously, + #2817) + * WSClient can return more specific error on websocket handshake failure if + it's provided by server (#2821) + * new MaxWebSocketClients configuration option for the RPC server to control + the number of allowed websocket clients, missing or 0 value make the server + use the default of 64 which is compatible with the previous behavior (you + can use old configurations if you don't care about this option, #2821) + +Bugs fixed: + * occasional "loadgo" VM CLI test failures because of timeout (#2810) + * websocket waiter test instability (#2809) + * websocket event unsubscriptions could lead to node deadlock in rare + circumstances (#2809) + * exception handler could use improper evaluation stack in some cases (#2812) + * pointer stack items were not handled correctly in "protected" serialization + mode leading to inability to deserialize these items back (#2816) + * state reset functionality not working for big chains like mainnet (#2813) + +## 0.99.6 "Funambulation" (16 Nov 2022) + +An emergency release fixing mainnet incompatibility at block 2504320 leading +to inability to process block 2504813. A couple of other less severe bugs got +fixed as well. Unfortunately, this release requires complete resynchronization +for mainnet, other networks can use old DBs. + +Improvements: + * only transactions requested by the consensus process are forwarded to it + now slightly improving performance in some networked scenarios (#2788) + * a complete set of simple array unwrappers in the unwrap library (#2792) + * RPC binding generator can handle simple arrays now with configuration file + provided (#2792) + * more user-friendly error messages in some cases (#2797, #2802, #2804) + +Bugs fixed: + * DB was not properly closed after state reset (#2791) + * VM stack handling problem leading to lost items after return from call to + another contract (#2800) + * "istack" command panic in the VM CLI (#2800) + * failure to decode incorrect contract from ContractManagement state leading + to failure to start the node, it'll be ignored now similar to how C# node + treats this (#2802) + * subscription to execution events failed if no "state" filter was used along + with "container" (#2804) + * Actor using WSClient could deadlock in the transaction waiter during + unsubscription (#2804) + +## 0.99.5 "Underestimation" (11 Nov 2022) + +It wasn't long since the previous release until a juicy set of features and +optimisations came on board. So we've decided to change our release plan and +supply one more 3.4.0 compatible version. It comes up with a massive suite +of network P2P and broadcast improvements. We've carefully investigated message +sending timeouts on benchmarks, reworked the broadcast logic and significantly +reduced delays in message sending and the amount of useless traffic between our +nodes. For smart-contract developers and RPC client users several new features +were added. These are automatic RPC bindings generator, poll-based and +websocket-based transactions awaiting and an extended websocket client +subscriptions interface. For better debugging experience we've improved state +related functionality of VM CLI and added an ability to reset chain state to a +particular height. + +An important dBFT initialization bug that caused timestamp-related errors on +block addition was fixed. Also, those who use NeoGo node with +EnableCORSWorkaround setting may want to update the node to be able to handle +web-socket requests with Origin set in the header. The users of websocket RPC +client subscription mechanism are welcomed to move from deprecated subscriptions +API to the extended one. Finally, we are kindly asking our users to have a look +at our roadmap file where deprecated code removal schedule is attached. + +This version is fully compatible with C# node 3.4.0 and does not require +resynchronization on upgrade. This time for sure, the next release is planned +to be compatible with 3.5.0. + +New features: + * logarithmic gossip fan-out, new `BroadcastFactor` application setting and + adaptive peer requests are added to optimise broadcasting process (#608, + #2678, #2743) + * Prometheus histograms with handling time are added for P2P commands (#2750) + and RPC calls (#2784) + * transaction awaiting is added to RPC clients (#2740, #2749) + * RPC bindings generator (#2705) is added for both safe (#2766) and + state-changing (#2769, #2778) methods with initial iterator support (#2783) + * the ability to reset chain state to a particular height is added (#2576, + #2773) + +Behaviour changes: + * Aspidochelone fork block for NeoFS sidechain mainnet configuration is + rescheduled (#2754, #2776) + * RPC server will now handle any Origin in websocket connection when + EnableCORSWorkaround setting is on (#2772, #2724) + * WS RPC client subscriptions API is extended to accept custom subscriber + channels, old subscriptions API is marked as deprecated (#2756, #2764) + +Improvements: + * network code refactoring (#2742) + * network packets sending is now limited in time (#2738) + * broadcast messages will be sent concurrently to each peer (#2741) + * ping messages are broadcast using the common broadcast logic (#2742) + * transactions requested by dBFT will be accepted on demand only (#2746, #2748, + nspcc-dev/dbft#63) + * VM CLI's state-based functionality improvements (extended signers and + arguments parsing, enabled logging) and additional commands (`loadtx`, + `loaddeployed`, `jump`) (#2729, #2606, #2740) + * incoming P2P transactions are now processed concurrently and buffer for + inventory hashes is reusable (#2755) + * `GetData` and `GetBlocksByIndex` replies are now batched on a TCP-level + (#2757) + * optimisation of incoming P2P transactions and blocks filtering are introduced + (#2758, #2759) + * batch size of broadcast transactions hashes is adjusted (#2760) + * peer reconnections logic is improved, `GetAddr` messages are sent more + often (#2761, #2745) + * documentation for wallet configuration file is clarified (#2763) + * Koblitz curve implementation of btcd/btcec was replaced with decred/secp256k1 + due to GitHub security warning (#2781) +* `NOT` opcode for Bool emission is now used to reduce resulting script + cost (#2762) + +Bugs fixed: + * failing tests cleanup on Windows (#2736) + * race in CLI test (#2765) + * invalid conversion of PublicKey smart contract parameter to emitable (#2739) + * initialize dBFT's context with previous block's timestamp on view 0 (#2753, + #2752, nspcc-dev/dbft#64) + +## 0.99.4 "Transliteration" (07 Oct 2022) + +A small update mostly interesting for people building/testing smart contracts +with NeoGo. It contains long-awaited VM CLI update that allows to use +blockchain state and complete set of interops, additional helper functions for +smart contracts and notification checking code (for upcoming 3.5.0 protocol +changes). Node operators using EnableCORSWorkaround (that is still not +recommended, but available) also may want to update to be able to handle +pre-flight CORS requests. + +This version is compatible with C# node 3.4.0 and does not require +resynchronization on upgrade. The next release is planned to be compatible +with 3.5.0. + +New features: + * address conversion helpers for smart contracts (#2698) + * interop helper to call specific version of a contract (#2695) + * notifications are now checked for manifest compliance, a warning is logged + in case on detected inconsistencies (#2710) + * VM CLI can now use blockchain state DB (including historic states) to run + code with a complete set of interops and contracts available; additional + commands were added to it to inspect storage and generated events (#2723) + +Behavior changes: + * type assertion with two return values in Go contracts can't be compiled now + (it never worked properly, #2718) + * RPC server will now handle pre-flight CORS requests (via OPTIONS methods) + when EnableCORSWorkaround setting is on (#2724) + * all transaction-generating CLI commands were unified to accept + gas/sysgas/out/force parameters, ask for confirmation and be able to save + transaction into a file; this affects wallet claim and candidate-related + commands mostly (#2731) + +Improvements: + * smartcontract.Builder API was extended with Len method (#2691) + * NNS example contract adjustments for better DNS compatibility (#2679) + * documentation updates (#2708, #2722, #2726, #2728) + * compiler now emits code to explicitly cast runtime.Notify() parameters to + appropriate types (#2720) + * CLI tests repackaging (#2725) + * NeoFS sidechain configurations for mainnet and testnet (#2730) + +Bugs fixed: + * panic on node shutdown (#2689) + * panic on inlined append() compilation (#2716) + +## 0.99.3 "Simplification" (09 Sep 2022) + +Version 0.99.3 brings with it most of the planned refactoring changes to the +RPC client. dApp backend code can be greatly simplified in many cases by +using new APIs (old ones are still available for transition period of about +one-two releases more). We also have some updates for CLI and compiler and a +number of bug fixes. + +This version is compatible with C# node 3.4.0 and does not require +resynchronization on upgrade. The next release is planned to be compatible +with 3.5.0 (if it's to be released around currently planned dates). + +New features: + * native contract RPC wrappers (#2643, #2646, #2650, #2666, #2665) + * NEP-11 RPC wrappers (#2651) + * invoker interface extension with session-based iterators support (#2650) + * notary Actor greatly simplifying creation of notary-assisted transactions + via RPC client (#2665) + * historic smart contract calls can now be made via CLI (#2683) + +Behavior changes: + * calculatenetworkfee RPC can handle paid attributes (NeoGo extensions) and + invalid contract signatures now, it won't return an error for them (#2658) + * graceful node shutdown on SIGTERM (#2660) + * wallet balance commands now require at least 0.99.1 NeoGo version (or + compatible C# node) used by the RPC server (#2667) + +Improvements: + * build system corrections (#2641, #2684) + * additional types in `unwrap` (#2650, #2651) + * session iterator consistency check in `unwrap` (#2650) + * multitransfers in NEP-17 RPC wrapper (#2653) + * extended transaction validity time in CLI wallet commands (#2653) + * reference counter optimization in VM (#2659) + * RPC Actor API can have default (used for all transactions) attributes and + callbacks now (#2665) + * neptoken RPC package can now provide wallet.Token data (#2667) + * NEP-11 balance commands can now work without direct token hash + specification or previous token import (#2667) + * support for offline signing in CLI (#2668) + * compiler can optimize out unused global variables now (#2624) + * private keys are now cleaned up from memory when they're no longer needed, + additional Account APIs added to reduce direct interactions with private + keys (#2672) + * updated linter settings, some code cleanup (#2674) + * more documentation and examples for new RPC APIs (#2681) + * refactored state-changing methods of NEP-11/NEP-17 RPC wrappers into a + separate structure to simplify reusing them in higher-level code (#2681) + * simplified rpcclient historic API (#2686) + +Bugs fixed: + * compiler panic on empty package list (#2645) + * compiler not allowed to use unnamed parameters in exported methods (#2648) + * compiler allowed to export multireturn functions (#2648) + * compiler panic on nil method receiver in the compiled code (#2649) + * compiler panic on variable initialization from multireturn call (#2644) + * potential lockups or panics on node shutdown (#2652) + * contract manifest not checked for correctness in bindings generation CLI + command (#2656) + * SignTx wallet Account API could lead to inconsistent result in some cases + (#2665) + * wallet Account API allowed to sign with locked accounts (#2665) + * potential panic in keys.WIFDecode on some inputs (#2672) + +## 0.99.2 "Recalibration" (12 Aug 2022) + +This is a 3.4.0-compatible (tested for mainnet and testnet) update to NeoGo +that implements all of the required protocol changes, adds a lot of new RPC +client interfaces and provides additional service management functions. This +is also the first version to support the recently released Go 1.19. Support +for Go 1.16 is removed at the same time, so you need Go 1.17+ to build NeoGo +now. + +With this version you can turn on/off, restart or reconfigure internal node +services like RPC or Oracle without full node shutdown/startup. Node operators +can send an appropriate Unix signal and that'll do the job. At the same time +node operators must resynchronize their nodes when updating to 0.99.2 due to +native contract state change. + +A number of RPC client's methods are now deprecated (but still available to +simplify transition), please explore new APIs, try them and leave feedback in +#2597 if any. We expect new APIs to better fit the needs of various backend +RPC client use cases while allowing to remove some repetitive code from +applications at the same time. This release only contains generic NEP-17 call +wrapper, so it doesn't cover all of the things currently provided by the +old client API, but this will be fixed in future releases. + +New features: + * signal-based configuration and service reloads (#2612) + * new APIs in smartcontract package to convert Go types into NEP-14 + parameters (#2621, #2632) + * invoker package to deal with invocations via RPC in various contexts + (mostly useful for read-only and/or historic calls when used directly, + #2621) + * `hasMethod` method in the ContractManagement native contract (#2598, #2619, + #2640) + * actor package that helps in creating and sending transactions to the + network via RPC using the same set of signers (can be used directly, but + mostly intended to be used by higher-order contract wrappers, #2632, #2637) + * unwrap package in the RPC client that checks for execution results and + converts returned stack items to expected Go types (can be used directly, + but more useful when being used by upper-layer contract wrappers, #2638) + * nep17 RPC client package for simple remote access to NEP-17 contract + methods via RPC, both safe and state-changing (#2642) + +Behavior changes: + * smartcontract.Params type and (*Parameter).TryParse APIs were removed + completely as unused and not useful (#2621) + * compiler now rejects unnamed parameter for exported methods (but they + didn't work properly anyway, #2601) + * restored deploy-time out of bounds script checks (#2538, #2619) + * dynamic scripts (like entry scripts) no longer can emit notifications + (#2613) + * db dump and db restore CLI commands now reject `--debug` option (it was + ignored previously, #2625) + +Improvements: + * manifest correctness check during compilation (#2601) + * more robust CalledByEntry scope check (#2613) + * a check for excessive arguments in CLI (#2625) + * better help messages for CLI commands (#2625) + * Go 1.19 support (#2634) + * saved transactions created via CLI (`--out`) now use the maximum possible + ValidUntilBlock values (#2632) + * RPC server now returns more data about NeoGo-specific protocol extensions + in `getversion` reply (#2632) + +Bugs fixed: + * NEP-6 wallet version was wrong wrt the standard and C# implementation + (#2633) + * smart contract invocations from the CLI didn't compensate for interactive + prompt wait time which could lead to transaction expiration before it was + sent (#2632) + * Oracle native contract's `finish` method reentrancy problem (#2639) + +## 0.99.1 "Parametrization" (28 Jul 2022) + +We're updating NeoGo to push out a number of significant updates as well as +make it compatible with 3.3.1 version of C# node. The most prominent changes +are RPC sessions for iterators returned from invoke* calls, initial bits of +RPC client refactoring and support for darwin-arm64 and linux-arm64 +platforms. A number of internal changes are less visible from outside, but +are also important for future evolution of the code base. + +Node operators must resynchronize their nodes to get fully compatible state +(which is confirmed to be compatible with 3.3.1 for current mainnet up to +block 1932677 and T5 testnet up to block 475938). RPC client users, please +review the changes carefully and update your code wrt refactorings made as +well as iterator session support. + +Subsequent releases will also change RPC client and associated code, we want +to make it easier to use as well as extend its functionality (see #2597 for +details, comments and suggestions are welcome). 0.99.2 is expected to be +released somewhere in August with 3.4.0 C# node compatibility. + +New features: + * `getcandidates` RPC method to get full list of registered candidates and + their voting status (#2587) + * wallet configuration file support to simplify using CLI non-interactively, + use `--wallet-config` option instead of `--wallet` if needed (#2559) + * session-based JSON-RPC iterator API for both server and client with + `traverseiterator` and `terminatesession` calls; notice that the default + server behavior (`SessionEnabled: false`) is compatible with NeoGo 0.99.0, + iterators are expanded the way they were previously, only when sessions are + enabled they're returned by the server to the client (#2555) + * `interop` package now provides helper methods for proper `Hash160`, + `Hash256` and other type comparisons in smart contracts (#2591) + * `smartcontract.Builder` type to assist with entry script creation as well + as a set of CreateXXXScript functions for simple cases (#2610) + +Behavior changes: + * 3.3.1-compatible order of ABI methods for the native NeoToken contract + which affects LedgerContract state (#2539) + * `getnextvalidators` RPC was reworked to be compatible with C# node, if you + rely on Active flag from the old response or full candidate list please use + `getcandidates` method (#2587) + * `--account` parameter for `contract manifest add-group` CLI command was + changed to `--address` (short `-a` is still the same) for better + consistency with other commands (#2559) + * (*WSClient).GetError won't return an error in case the connection was + closed with (*WSClient).Close + * `getnepXXbalances` RPCs now return decimals, symbol and token name data + along with asset hash (#2581) + * Ledger.GetBlock will now return state root hash for chains that use + StateRootInHeader extension (#2583) + * `request.RawParams` type is gone (but it likely wasn't used anway), if + needed use `[]interface{}` directly (#2585) + * `RawParams` field of `request.Raw` was renamed to `Params` (#2585) + * `vm.State` type moved to a package of its own (`vm/vmstate`) to avoid + importing whole VM where only State type is needed (#2586) + * `MaxStorageKeyLen` and `MaxStorageKeyLen` definitions moved from + `core/storage` to `config/limits` package simplifying their usage (#2586) + * `vm.InvocationTree` type moved into `vm/invocations` (#2586) + * `storage.Operation` type moved into `storage/dboper` package (#2586) + * `rpc/server` package moved to `services/rpcsrv` (#2609) + * `rpc/client` package moved to `rpcclient` (#2609) + * `network/metrics` package moved to `services/metrics` (#2609) + * `rpc/request` and `rpc/response` packages were merged under `neorpc`, + `request.Raw` is `neorpc.Request` now, while `response.Raw` is + `neorpc.Response` (#2609) + * `rpc.Config` type was moved to `config.RPC` (#2609) + * `subscriptions.NotificationEvent` type moved to + `state.ContainedNotificationEvent` (#2609) + * `subscriptions.NotaryRequestEvent` type moved to + `result.NotaryRequestEvent` (#2609) + +Improvements: + * new `make` targets to build NeoGo binaries locally or using Docker + environment (#2537, #2541) + * more detailed client-side error on JSON-RPC WebSocket connection closure + (#2540) + * various node's micro-optimizations both for CPU usage and memory + allocations (#2543) + * new Wallet method that allows to save it in pretty format (#2549) + * massive test code refactoring along with some core packages restructuring + (#2548) + * transaction package fuzz-test (#2553) + * simplified client-side Error structure, more predefined errors for + comparisons, refactored server side of RPC error handling (#2544) + * more effective entry scripts for parameterless function invocations via RPC + (#2558) + * internal services now can start/stop properly independent of other node + functionality (#2566, #2580) + * all configurations now use 'localhost' instead of 127.0.0.1 (#2577) + * documented JSON behavior for enumerations which deviates from C# node + slightly (#2579) + * better error messages in the CLI for invocations (#2574) + * compiler can inline methods now (#2583) + * `interface{}()` conversions are supported by the compiler now (although + they don't change values, #2583) + * server-side code moved out of common RPC packages completely (#2585, #2586) + * metrics and DB configurations moved to config and dbconfig packages (#2586) + * calling methods on returned values is now possible in Go smart contracts + (#2593) + * NNS contract now cleans up old entries in case a domain is registered again + after expiration (#2599) + * GetMPTData P2P messages (from P2PStateExchangeExtensions) can now be + processed even if KeepOnlyLatestState is enabled (#2600) + * builds and regular tests for MacOS (#2602, #2608) + * internal `blockchainer.Blockchainer` is finally gone, simplifying some + dependencies (#2609) + * updated CLI `--version` output format to conform with NeoFS tooling style + (#2614) + +Bugs fixed: + * dBFT update: increase the number of nodes that respond to RecoveryRequest + (#2546, nspcc-dev/dbft#59) + * invalid block height estimation for historic calls (#2556) + * Signature parameter to RPC invocations was expected in hex format instead + of base64 (#2558) + * missing data in RPC server error logs in some cases (#2560) + * potential deadlock in consensus node before the dBFT process is started + (#2567) + * dBFT update: ChangeView messages were not correctly transmitted in + RecoveryResponse messages (#2569, nspcc-dev/dbft#60) + * dBFT update: ChangeView messages from newer views were not correctly + processed by outdated node (#2573, nspcc-dev/dbft#61) + * RPC server restarts via SIGHUP could lead to server not starting at all in + very rare case (#2582) + * inlined code couldn't have multiple `return` statements (#2594) + * empty list of transactions is now returned instead of `null` in `getblock` + RPC responses when block doesn't contain transactions, the same way C# node + does (#2607) + * candidate registration didn't invalidate committee cache leading to state + differences for T5 testnet (#2615) + +## 0.99.0 "Overextrapolation" (03 Jun 2022) + +A big NeoGo upgrade that is made to be compatible with C# node version +3.3.0. All of the protocol changes are implemented there with the main one +being the Aspidochelone hardfork that will happen on mainnet +soon. Compatibility is confirmed for current T5 testnet and mainnet, but this +version requires a resynchronization so schedule your updates accordingly. + +But it's not just about the protocol changes, this release introduces an +ability to perform historic invocations via RPC for nodes that store old MPT +data. Using `invokefunctionhistoric` you can perform some invocation with the +chain state at the given height, retreiving old balances, ownership data or +anything else you might be interested in. + +Please also pay attention to configuration files used by your node, mainnet +ones must have Aspidochelone hardfork enabled at height 1730000 to be +compatible and T5 testnet enables it at height 210000. T5 testnet is different +from T4, it uses different magic number, different seed nodes and different +protocol configuration settings. 0.99.0 won't work for T4 testnet, please +use 0.98.5 for it. + +New features: + * new methods in native contracts: + - getTransactionVMState and getTransactionSigners in LedgerContract (#2417, + #2447, #2511) + - murmur32 in CryptoLib (#2417) + - getAllCandidates and getCandidateVote in NeoToken (#2465) + * System.Runtime.GetAddressVersion syscall (#2443) + * StartWhenSynchronized option for RPC server (defaults to the old behaviour, + #2445) + * historic RPC invocations (invokefunctionhistoric, invokescripthistoric and + invokecontractverifyhistoric APIs, #2431) + * protocol hardforks, with Aspidochelone being the first supported (#2469, + #2519, #2530, #2535) + * MODMUL and MODPOW VM instructions (#2474) + * ability to get connection closure error from WSClient (#2510) + * isolated contract calls with state rollback on exception (#2508) + * vote and candidate state change events in the NEO contract (#2523) + * immutable compound types in VM (#2525) + +Behavior changes: + * ContractManagement deploy and update methods now require AllowCall flag + (#2402) + * GetStorageItems* APIs are no longer available in dao package, use Seek* + (#2414) + * committee candidates are now checked against the Policy contract block list + (#2453) + * getCandidates NEO method returns no more than 256 results now (#2465) + * EQUAL checks are limited to 64K of data in VM now irrespective of the + number of elements compared (#2467) + * Create[Standard/Multisig]Account prices were raised to avoid DoS attacks + (#2469) + * System.Runtime.GetNotifications syscall costs 16 times more GAS now (#2513) + * System.Runtime.GetRandom syscall now costs more and uses more secure seed + (#2519) + +Improvements: + * better messages for some CLI commands (#2411, #2405, #2455) + * fixes and extensions for example contracts (#2408) + * better neotest documentation (#2408) + * internal tests now use neotest framework more extensively (#2393) + * neotest can now be used for benchmark code and has more multi-validator + chain methods (#2393) + * big (>64 bit) integers can now be used for RPC calls (#2413) + * no error is logged now when notary-assisted transaction is already in the + mempool (it's not an error, #2426) + * T5 testnet with more aggressive protocol parameters (#2457) + * destroyed contracts are blocked now (#2462) + * JSONization errors for invoke* RPCs are now returned in exception field of + the answer (#2461) + * typos, grammar and spelling fixes in documentation, comments and messages + (#2441, #2442) + * faster RPC client initialization (#2468) + * RPC processing errors use ERROR log severity now only if there is a + server-side error occurred (2484) + * increased server-side websocket message limit to fit any request (#2507) + * invalid PrepareRequest now doesn't require other nodes to be alive to send + ChangeView (#2512) + * updated YAML library dependency (#2527) + * notary subsystem compatibility fixes, using new IDs and options (#2380) + * minor performance improvements (#2531) + +Bugs fixed: + * websocket-based RPCs were not counted in Prometheus metrics (#2404) + * input data escaping missing for RPC log messages (#2404) + * compiler panic in notification checking code (#2408) + * missing 'hash' field in the debug data (#2427) + * debug data used relative paths that are not compatible with neo-debugger + (#2427) + * getversion RPC method wasn't compatible with C# implementation (#2435, + #2448) + * old BaseExecFee and StoragePrice values could be used by transactions in + the same block with transactions that change any of them (#2432) + * context initialization race in dbft (#2439) + * some stateroot data functions used incorrect keys in the DB (#2446) + * voter reward data could not be deleted in NEO contract in some cases + (#2454) + * the maximum number of HTTPS oracle redirections is limited to 2 (and only + using HTTPS) for C# compatibility (#2456, #2389) + * maximum number of contract updates wasn't limited leading to overflow (#2462) + * incorrect interop signature for getCandidates NEO contract method (#2465) + * next instruction validitiy check is performed now before instruction + pointer move to be compatible with C# implementation (#2475) + * concurrent map access in Seek leading to panic (#2495, #2499) + * insecure password reads (#2480) + * minor VM reference counting fixes (#2498, #2502, #2525) + * panic during serialization of transaction with empty script (#2485) + * 'exception' field was missing in the invoke* RPC call output when there is + no exception which differed from C# node behaviour (#2514) + * interop interfaces used incompatible (wrt C# node) type string in JSON + (#2515) + * minor genesis block state differences wrt C# implementation (#2532) + * incompatible (wrt C#) method offset check (#2532) + +## 0.98.5 "Neutralization" (13 May 2022) + +An urgent update to fix the same security issue that was fixed in 3.1.0.1 C# +node release. Please upgrade as soon as possible, resynchronization is not +needed for mainnet. + +Bugs fixed: + * GAS emission now happens after NEO transfer is finished (#2488) + +## 0.98.4 "Mesmerization" (11 May 2022) + +An urgent release to fix incompatibility with mainnet at block 1528989. The +actual pair of problems leading to inability to process this block occurred +earilier than that, so to fix this you need to resynchronize your node. Fixed +node is confirmed to have identical state as 3.1.0 C# node up to block +1529810. + +Bugs fixed: + * StdLib itoa() implementation emitted uppercase letters instead of lowercase + (#2478) + * StdLib jsonDeserialize() implementation couldn't handle properly integers + larger than 64-bit signed (#2478) + +## 0.98.3 "Liquidation" (07 May 2022) + +This is a hotfix release to fix t4 testnet incompatibility at block +1589202. The actual problem was found and fixed during 0.99.0 development +cycle, but 0.99.0 is expected to be incompatible with t4 testnet. This release +allows to continue working with it as well as mainnet (and contains some other +fixes for known problems). It does not require resynchronizing a node. + +Improvements: + * double call to `WSClient.Close()` method won't cause a panic (#2420) + +Bugs fixed: + * Rules scope considered as invalid in binary representation (#2452) + * incorrect compressed P2P message could lead to panic (#2409) + * notary-assisted transaction could be in inconsistent state on the Notary + node (#2424) + * WSClient panics if request is made after connection breakage (#2450) + * Rules scope JSON representation wasn't compatible with C# implementation + (#2466) + * JSONized Rules scope could only contain 15 conditions instead of 16 (#2466) + +## 0.98.2 "Karstification" (21 Mar 2022) + +We've decided to release one more 3.1.0-compatible version bringing all of the +new features and bug fixes (along with complete support for Windows platform) +made before going into full 3.2.0 compatibility. It's important for us to give +you more stable and flexible environment for the ongoing hackathon. Contract +bindings generator might be very useful for anyone operating with already +deployed contracts written in other languages, while the node itself can now +be configured for lightweight operation, keeping only the data relevant for +the past MaxTraceableBlocks, no more and no less. Current public networks +don't benefit from it yet with their large MaxTraceableBlocks setting, but +they're growing every day and it's just a matter of time when this feature +will start making a difference. And sorry, but this release is again faster +than the previous one. + +We recommend updating, but if your node is not public and you don't +specifically want to play with new things brought by it you might as well wait +for the next one. It requires resynchronization and 0.99.0 will do too because +of planned protocol changes. + +New features: + * protocol extension allowing to change the number of validators in existing + network (#2334) + * MPT garbage collection mechanism allowing to store a set of latest MPTs, + RemoveUntraceableBlocks setting now activates it by default unless + KeepOnlyLatestState is used (#2354, #2360) + * NEP-11/NEP-17 transfer data garbage collection (#2363) + * Go bindings generator based on contract manifests (#2349, #2359) + +Behavior changes: + * some RPC client functions for divisible NEP-11 changed signatures using + slice of bytes now instead of strings for IDs (#2351) + * heavily refactored storage and dao package interfaces (#2364, #2366) + * support for Go 1.18 added, 1.15 dropped (#2369) + +Improvements: + * additional CLI tests (#2341) + * `defer` statement is now compiled more effectively (#2345) + * `defer` statement can be used in conditional branches now (#2348) + * new tests for divisible NEP-11 contract (#2351) + * all tests failing on Windows platform are fixed (#2353) + * RPC functions now accept integers larger than 2^64 from strings (#2356) + * 10-35%% improvement in bulk block processing speed (node synchronization) + depending on machine and configuration (#2364) + * reworked VM CLI fixing Windows incompatibility and UTF-8 bugs (#2365) + * incompletely signed transaction JSONs now also contain hash (#2368) + * RPC clients can now safely be used from multiple threads (#2367) + * additional tests for node startup sequences (#2370) + * customizable notary service fee for Notary extension (#2378) + * signers passed into RPC methods can now contain rules (#2384) + * compiler tests now take less time (#2382) + * some fuzzing tests added (#2399) + +Bugs fixed: + * test execution result wasn't checked for CLI invocations that saved + transaction into file (#2341) + * exception stack wasn't properly cleared during CALL processing in VM (#2348) + * incorrect contract used in RPC client's GetOraclePrice (#2378) + * oracle service could be tricked by redirects into going to local hosts when + this was disabled (#2383) + * `invoke*` RPC functions could be used to trigger OOM with specially crafted + scripts (#2386) + * some edge-cased integer conversions were not exactly matching the behavior + of C# node in VM (#2391) + * HASKEY instruction wasn't working for byte arrays (#2391) + * specially-crafterd JSONPath filters for oracle requests could trigger OOM + (#2372) + * ENDFINALLY opcode before ENDTRY could lead to VM panic (#2396) + * IsScriptCorrect function could panic on specially-crafted inputs (#2397) + +## 0.98.1 "Immunization" (31 Jan 2022) + +Bug fixes, interesting optimizations, divisible NEP-11 example and a big +compiler update --- everything you wanted to find in this NeoGo update. It +requires chain resynchronization, but this resynchronization will be faster +than ever. + +One thing should also be noted, even though this release is 3.1.0-compatible, +it is known to have a different state for testnet after block 975644, but it's +not a NeoGo fault, it'll be fixed in the next C# node release. The root cause +is well-known and is not considered to be critical compatibility-wise. + +New features: + * support for reading the wallet from stdin in CLI (where it's possible, #2304) + * new CLI command for wallet password change (#2327) + * `getstateroot` support in RPC client (#2328) + * divisible NEP-11 example (#2333) + * helper script to compare node states via RPC (#2339) + +Behavior changes: + * zero balance is explicitly printed now for token-specific NEP-17 balance + requests from CLI (#2315) + * pkg/interop (used by smart contracts) is a separate Go module now (#2292, + #2338) + * smart contracts must be proper Go packages now (#2292, #2326) + +Improvements: + * optimized application log storage (#2305) + * additional APIs in `neotest` framework (#2298, #2299) + * CALLT instruction is now used by the compiler where appropriate leading to + more optimized code (#2306) + * DB seek improvements increasing chain processing speed by ~27% (#2316, #2330) + * updated NeoFS dependencies (#2324) + * optimized emitting zero-length arrays in `emit` package used to construct + scripts (#2299) + * native contract tests refactored using generic contract testing framework (#2299) + * refactored internal Blockchainer interfaces, eliminating unnecessary + dependencies and standardizing internal service behavior (#2323) + * consensus process now always receives incoming transactions which might be + helpful for accepting conflicting (wrt local pool) transactions or when the + memory pool is full (#2323) + * eliminated queued block networked re-requests (#2329) + * better error reporting and parameter handling in FindStates RPC client + method (#2328) + * invoke* RPCs now also return notifications generated during execution (#2331) + * it's possible to get storage changes from the result of invoke* RPCs (#2331) + * better transfer data storage scheme resulting in faster getnep* RPC + processing (#2330) + * dropped deprecated `loader` package from compiler dependencies moving to + updated x/tools interface (#2292) + +Bugs fixed: + * incorrect handling of missing user response in CLI (#2308) + * improper primary node GAS distribution in notary-enabled networks (#2311) + * excessive trailing spaces in the VM CLI output (#2314) + * difference in JSON escaping wrt C# node leading to state difference for + testnet at block 864762 (#2321) + * potential panic during node shutdown in the middle of state jump (#2325) + * wrong parameters for `findstates` call in RPC client (#2328) + * improper call flags in Notary contract `withdraw` method (#2332) + * incorrect ownerOf signature for divisible NEP-11 contracts (#2333) + * missing safeness check for overloaded methods in the compiler (#2333) + +## 0.98.0 "Zincification" (03 Dec 2021) + +We've implemented all of Neo 3.1.0 protocol changes in this release (and +features like NEP-11 transfer tracker), but as usual it's not the only thing +it contains. If you're developing contracts in Go you might be interested in +contract testing framework this release brings with it, which allows you to +write automated tests in Go. You can also build a node for Windows now and +most of functionality works there (but some known problems still exist, so +this port is considered to be experimental for now). As usual there were some +optimizations made in various components as well as bug fixes. + +There are also some things removed in this release, that is BadgerDB and +RedisDB support, this will allow us to optimize better for LevelDB and BoltDB +in future. Notice also that this release requires full node resynchronization. + +New features: + * optional signer scope parameter for deploy command (#2213) + * interop packages extended with Abort() function and useful constants (#2228) + * notary subsystem now allows to have multiple multisignature signers as well + as combining simple notary-completed accounts with multisignature ones (#2225) + * configurable method overloading for the compiler (#2217) + * initial support for the Windows platform (#2239, #2265, #2267, #2264, + #2283, #2288) + * contract testing framework (#2229, #2262) + * `Rules` witness scope (#2251) + * PACKMAP/PACKSTRUCT opcodes and UNPACK extension (#2252) + * NEP-11 transfer tracking with RPC API (#2266) + * support for structure field slicing in the compiler (#2280) + * invoked contract tracing for invoke* RPCs (#2270, #2291) + +Behavior changes: + * BadgerDB and RedisDB are no longer available for DB backend (#2196) + * GetNEP17Transfers() RPC client method now works with Uint160 parameter + instead of address (#2266) + +Improvements: + * compiler optimizations (#2205, #2230, #2232, #2252, #2256) + * output fees as proper decimals in the CLI (#2214) + * private network created from Makefile now uses dynamic IP addresses (#2224) + * various minor optimizations across whole codebase (#2193, #2243, #2287, #2290) + * `util convert` command now also handles base64 representations of script + hashes (#2237) + * unified "unknown transaction" RPC responses (#2247) + * faster state switch when using P2P state synchronization extension (#2201) + * better logging for oracle errors (#2275) + * updated NeoFS client library dependency (#2276) + +Bugs fixed: + * getproof RPC API didn't handle destroyed contracts correctly (#2215) + * invokefunction RPC returned error if no parameters were passed (#2220) + * incorrect handling of empty MPT batch (#2235) + * incoming block queue using more memory than it should, up to OOM condition + on stuck node (#2238) + * panic on peer disconnection in rare cases (#2240) + * panic on password read failure (#2244) + * miscompiled functions using `defer` and returning no values (#2255) + * oracle responses processed not using request witnesses (#2261) + * CreateTxFromScript() RPC client method incorrectly handled CustomContracts + and CreateTxFromScript sender scopes (#2272) + * potential OOM during synchronization on systems with slow disks (#2271) + * incorrect oracle peer configuration in the default private network (#2278) + * compiler error processing functions using defer and inlined calls (#2282) + * node that is simultaneously a CN and Oracle node could create invalid + oracle response transactions (#2279) + * incorrect GetInvocationCounter() result in some cases (#2270) + +## 0.97.3 "Exception" (13 Oct 2021) + +This updated version of NeoGo is made to be compatible with Neo 3.0.3 bringing +with it all corresponding protocol changes and bug fixes so that you can use +it on public testnet and mainnet. It also brings with it a complete +experimental P2P state exchange extension for private networks as well as +additional optimizations. This is the first version requiring at least Go 1.15 +to compile, so make sure your Go is up to date if you're not using pre-built +binaries. Protocol updates and bug fixes made in this release require a +resynchronization on upgrade. + +We also make a final call on Badger and Redis DB issue #2130, if there are no +active users of them, support for both will be removed in the next release. + +New features: + * MaxConnsPerHost RPC client option (#2151) + * P2P state exchange extension (#2019) + * transaction-related CLI commands now show calculated fees before + sending transaction to RPC node and ask for confirmation (#2180) + * test invocations are now possible for partially-signed transactions stored + in JSON format (used for manual signing by multiple parties, #2179) + * `getstate` and `findstates` RPC to retrieve historic state data (#2207) + +Behavior changes: + * added support for Go 1.17, dropped Go 1.14 (#2135, #2147) + * blocking native contracts is no longer possible in Policy contract (#2155) + * `getversion` RPC call now also returns protocol configuration data (#2161, + #2202) + * NEF files now have Source field that can be specified in contract's + metadata (#2191) + * notification events from contracts in subscription service now also contain + container (transaction usually) hash (#2192) + * out of bounds exceptions can be catched in contracts now for PICKITEM and + SETITEM VM instructions (#2209) + +Improvements: + * reduced number of memory allocations in some places (#2136, #2141) + * VM optimizations (#2140, #2148) + * notary subsystem documentation (#2139) + * documentation fixes (#2162) + * VM CLI now allows to dump slots (#2164) + * better IPv6 checks in example NNS contract and `getAllRecords` method (#2166) + * updated linters (#2177) + * Migrate methods renamed into Update in examples (#2183) + * old (no longer available) interop function names removed (#2185) + * optimized processing of voting NEO transfers (#2186) + * configuration parameters specified in seconds no longer use (improper) + time.Duration type (#2194) + * better error message for conflicting transactions (#2199) + * open wallet in read-only mode if not changing it (#2184) + * optimized header requests in P2P communication (#2200) + * compiler now checks for method existence if it's specified as safe in + metadata (#2206) + +Bugs fixed: + * incorrect CustomGroups witness scope check (#2142) + * empty leaf values were ignored for MPT calculcations (#2143) + * lower-case hexadecimal in block header JSON output (differing from C# node, + #2165) + * `getblockheader` RPC result missing `Nonce` and `Primary` fields (#2165) + * return empty list of unverified transactions for `getrawmempool` RPC + instead of null (C# node never returns null, #2165) + * return empty list of NEF tokens in JSON format instead of null which C# + node never returns (#2165) + * races in some tests (#2173) + * parameter context JSON serialization/deserialization incompatiblity with C# + node leading to interoperability problems (#2171) + * transfers of 0 GAS or NEO were not possible (#2169) + * incorrect ContentTypeNotSupported oracle response transaction handling (#2178) + * JSON escaping differences with C# implementation (#2174) + * NEO balance update didn't save LastUpdatedBlock before GAS distribution + leading to problems in transactions with recursive NEO transfers (#2181) + * panic in CLI transfer command when missing destination address (#2211) + * multiple blank identifiers in function/method were misinterpreted by the + compiler (#2204) + * `getnep17transfers` used uint32 for internal time processing which is not + enough for ms-precision N3 timestamps (#2212) + * PICKITEM instruction could fail on attempt to get an item with long key + from map (#2209) + +## 0.97.2 "Dissipation" (18 Aug 2021) + +We're rolling out an update for NeoGo nodes that mostly concentrates on +performance. We've tweaked and tuned a lot of code while staying compatible +with N3 mainnet and testnet. At the same time we're gradually introducing +changes required for our P2P state exchange extension and this affected DB +format, so you'll need to resynchronize on update. This update is not +mandatory though, 0.97.1 is still perfectly valid for N3 networks. + +Note also that we're discussing removal of Badger and Redis databases support +in future releases, so if you're interested in them take a look at #2130. + +Behavior changes: + * address blocking in Policy contract now also blocks calls to contracts with + blocked addresses (#2132) + +Improvements: + * numerous memory and CPU optimizations across whole codebase (#2108, #2112, + #2117, #2118, #2122, #2123, #2128, #2114, #2133) + * preliminary work for P2P state exchange extension (#2119) + * `util convert` command now also detects public keys and converts them to + script hash/address (#2125) + +Bugs fixed: + * key decoding functions could accept some additional data even though only + key is expected to be present (#2125) + * conflicting dummy transactions could stay in the DB even with block removal + enabled (#2134) + +## 0.97.1 "Gasification" (06 Aug 2021) + +We're updating NeoGo to make it compatible with the latest protocol changes +made in 3.0.2 version of C# node. But that's not the only thing we do, this +release also fixes one important bug, improves node's performance and adds CLI +support to add group signatures to manifests. + +It requires resynchronization on upgrade. + +New features: + * `contract manifest add-group` command to add group signatures to contract + manifest (#2100) + +Behavior changes: + * GAS contract no longer has Refuel method, it could be used as DOS + amplification tool for attacks on network and there is no way to securely + fix it (#2111) + +Improvements: + * memory store optimizations leading to substantial single-node TPS + improvements (#2102) + * various micro-optimizations across the board both for CPU usage and memory + allocations (#2106, #2113) + * optimized transaction decoding (#2110) + +Bugs fixed: + * ping messages created with wrong value used for node's height (#2115) + +## 0.97.0 "Ventilation" (02 Aug 2021) + +This is an official 3.0.0-compatible release that is ready to be used both for +mainnet and testnet. Technically, 0.96.0 and 0.96.1 are compatible too, but +they need an updated configuration to work on mainnet while this version has +it covered. + +We keep improving our node and this release is not just a repackage of +something older, so DB format changes require a resynchronization if you're +upgrading from 0.96.X. + +Behavior changes: + * updated configuration for mainnet (#2103) + +Improvements: + * documentation for contract configuration file (#2097) + * significant change to NEP-17 tracking code, it shouldn't affect valid + NEP-17 tokens, but now we store a little less data in the DB and get more + from token contracts when needed (for `getnep17balances` RPC for + example); this change is required for our future state exchange protocol + extension (#2093) + * improved block processing speed on multicore systems (#2101) + * JSON deserialization now has the same limits as binary, but this doesn't + affect any valid code (#2105) + +Bugs fixed: + * potential deadlocks in notary-enabled nodes (#2064) + * wallet files not truncated properly on key removal (#2099) + +## 0.96.1 "Brecciation" (23 Jul 2021) + +New CLI commands, updated dependencies and some bugs fixed --- you can find +all of this in the new NeoGo release. It's compatible with 0.96.0 (except for +multisignature contexts, but you're not likely to be using them) and +confirmed to have proper RC4 testnet state up to 15K blocks (but 0.96.0 is +fine wrt this too). At the same time we recommend to resynchronize the chain +if you're using LevelDB or BoltDB, both databases were updated. + +New features: + * `query candidates`, `query committee` and `query height` CLI commands + (#2090) + * `GetStateHeight` RPC client call support (#2090) + +Behavior changes: + * `wallet candidate getstate` command was renamed to `query voter`, now it + also prints the key voted for and handles addresses with 0 balance without + spitting errors (#2090) + +Improvements: + * `query tx` now outputs signer address along with script hash (#2082) + * updated many of node's dependencies including BoltDB and LevelDB, there are + no radical changes there, mostly just some fixes improving stability (#2087) + * better error messages in some places (#2089, #2091) + +Bugs fixed: + * `query tx` command wasn't providing correct results if used with C# RPC + node (#2082) + * watch-only node (with a key, but not elected to participating in consensus) + could panic on receiving a number of Commit messages (#2083) + * `getstateheight` RPC call produced result in different format from C# node + (#2090) + * JSON representation of multisignature signing context wasn't compatible + with C# implementation (#2092) + +## 0.96.0 "Aspiration" (21 Jul 2021) + +We're updating NeoGo to support RC4 changes, there are some incompatible ones +so this version can't be used with RC3 networks/chains (RC3 testnet is +still available, please use 0.95.4 for it). This release was checked for RC4 +testnet compatibility and has the same state as C# for the first 5K blocks. + +No significant protocol changes are expected now as we're moving to final N3 +release, but we'll keep updating the node fixing bugs (if any), improving +performance and introducing NeoGo-specific features. + +New features: + * "System.Runtime.GetNetwork" system call (#2043) + * ContentTypeNotSupported oracle return code and content type configuration + for Oracle service (#2042) + * block header have "Nonce" field again which is used for + "System.Runtime.GetRandom" system call (#2066) + * import from incremental chain dumps (#2061) + * configurable initial (from the genesis block) GAS supply (#2078) + * "query tx" CLI command to check for transaction status (#2070) + +Behavior changes: + * verification GAS limits were increased (#2055) + * SQRT price reduced (#2071) + * binary deserialization is now limited to 2048 (MaxStackSize) items (#2071) + * notary deposit is stored now as serialized stack item (#2071) + * testnet and mainnet configuration updates (#2077, #2079, #2081) + +Improvements: + * faster stack item binary and JSON serialization (#2053) + * faster Storage.Find operation (#2057) + * additional documentation for public network CNs (#2068) + * better code reuse for native contract's data serialization (#2071, see new + stackitem.Convertible interface) + * some types from state package renamed to remove "State" word (#2075) + * util.ArrayReverse helper moved to package of its own with additional + helpers (#2075) + +Bugs fixed: + * nested structure cloning could lead to OOM (#2054, #2071) + * addresses were not accepted for witness accounts in RPC calls (#2072) + * POW instruction could take unknown amount of time to complete for big + parameters (#2060) + * oracle contract could accept invalid user data (#2071) + * EQUAL for deeply nested structures could never complete (#2071) + * arrays and structures were limited to 1024 items (#2071) + * oracle fallback transactions could try using outdated ValidUntilBlock value + (#2074) + * oracle node starting from genesis block tried to process all requests from + the chain instead of only working with current ones (#2074) + * some tests used non-unique/wrong temporary files and directories (#2076) + +## 0.95.4 "Yatter" (09 Jul 2021) + +Making a fully compliant Neo node is not easy, there are lots of minuscule +details that could be done in a little different way where both ways are +technically correct but at the same time just different which is enough to +create some interoperability problems. We've seen that with Legacy node +implementation where we were functionally complete with 0.74.0 release but +some fixes kept coming up to the most recent 0.78.3 version. It was a bit +easier with Legacy because we already had some years-long chains to test the +node against while N3 is a completely new territory. + +So we'd like to thank all RC3 hackathon participants as well as all other +people playing with N3 RC3 network, your joint efforts gave enough material +to keep us busy fixing things for some time. The chain moving forward hit more +and more edge cases with every block and this is actually very helpful, we saw +a lot of things that could be improved in our node and we improved them. + +Now we're releasing 0.95.4 with all of these fixes which is still +RC3-compatible and it's stateroot-compatible with C# implementation up to 281K +blocks. Please resynchronize to get identic testnet state. And most likely +that's the last RC3-compatible release as we're moving forward to the official +3.0.0 release of C# version NeoGo will be updated with appropriate changes +(some of which are not compatible with RC3). + +New features: + * 'sysgas' parameter for invocation and transfer CLI commands to manually add + some system fee GAS (#2033) + +Behavior changes: + * contract calls are now checked against specified permissions by the + compiler (#2025) + * stack items with nesting levels of 10 and more won't be serialized to JSON + for purposes like including them into RPC call reply (#2045) + +Improvements: + * AddHeaders() with multiple headers added at the same time now works with + StateRootInHeader option (#2028) + * cached GAS per vote value leads to substantially improved block processing + speed (#2032) + * failed native NEP-17 transfers now still re-save the old value to the + storage making state dumps compatible with C# (#2034) + * refactored and renamed some stackitem package functions, added proper error + values in all cases (#2045) + * RPC calls (and application logs) can now return real serialization errors + if there are any (previously all errors were reported as "recursive + reference", #2045) + * better tests (#2039, #2050) + +Bugs fixed: + * wrong onNEP11Payment name in contract configuration examples (#2023) + * missing permission sections in contract configuration examples (#2023) + * buffer stack item deserialization created byte string (#2026) + * Extra contract manifest field serialization was reworked to match C# + implementation more closely (#2021) + * wildcard permission in manifest allowed any methods, while it shouldn't + (#2030) + * group permission in manifest worked incorrectly for a set of groups (#2030) + * call flags were JSONized in incompatible way (#2041) + * MPT update left branch nodes with 1 child in some cases (#2034, #2047) + * JSON marshalling code for stack items detected item recursion in some cases + where no actual recursion was present (#2045) + * binary serialization code for stack items could overwrite old errors in + some cases (#2045) + * binary serialization code for stack items wasn't ensuring maximum size + limit during serialization which could lead to OOM (#2045) + * batched MPT update create excessive extension nodes for the last child of + branch node in some cases (#2047) + * in some cases "TCP accept error" was logged during node shutdown (#2050) + * contract updating code was updating caches before they should've been + updated (#2050) + * INVERT/ABS/NEGATE unary operations were not copying their arguments (#2052) + +## 0.95.3 "Yuppification" (17 Jun 2021) + +One more N3 RC3-compatible release that fixes testnet state difference at +block 151376. Please resynchronize to get proper testnet state. + +Behavior changes: + * NEP2-related functions in `crypto/keys` package changed a bit to allow + Scrypt parameters overriding, standard parameters are available via + `NEP2ScryptParams` function (#2001) + +Improvements: + * better unit test stability (#2011, #2001) + * updated neofs-api-go dependency (with support for TLS-enabled NeoFS node + connections, #2003) + * removed annoying token matching warning (#2018) + +Bugs fixed: + * state mismatch resulting from different committee candidate sorting (#2017) + +## 0.95.2 "Echolocation" (10 Jun 2021) + +This is another N3 RC3-compatible release and it's better in its RC3 +compatiblity than the previous one because we've fixed some state mismatches +wrt C# implementation that were found on testnet. It is confirmed to have the +same state up to 126K height (which is current), but to get proper state you +need to resynchronize your node from the genesis. + +New features: + * RPC notification subsystem was extended to support notary pool events, so + clients can now react on request additions (#1984) + * compiler now checks notification event name length to fit into the limit + (#1989) + * compiler now also checks for runtime.Notify() calls from Verify method + (which won't work anyway, #1995) + +Improvements: + * codegeneration improvements removing some unnecessary store/loads and type + conversions (#1881, #1879) + * additional MPT tests added ensuring compatibility with C# implementation + (#1993) + * additional consistency check added to prevent node running with native + contracts differing from the ones saved in DB (#2010) + +Bugs fixed: + * `calculatenetworkfee` RPC result used format different from C# node (#1998) + * `CALLT` opcode was missing any price leading to wrong GAS calculations and + different state wrt C# node (#2004) + * '+' character was emitted directly by `jsonSerialize` method which is fine + wrt JSON itself, but differs from C# node behavior leading to node state + difference (#2006) + * NEO self-transfers were not checking the amount transferred (they didn't + change balance, but they succeeded) leading to state difference wrt C# + implementation (#2007) + +## 0.95.1 "Shiftiness" (31 May 2021) + +Bringing NeoGo up to date with N3 RC3 changes this release also improves +compiler and CLI a bit. + +This release is mostly compatible with 0.95.0, but you need to resynchronize +your chains to have proper stateroot data and to make new methods available to +contracts. At the same time there won't be long-term support provided for it, +just as with all previous previews and RC. + +New features: + * base58CheckEncode/base58CheckDecode methods in StdLib native contract + (#1977, #1979) + * getAccountState method in NeoToken native contract (#1978, #1986) + * custom contracts and groups can now be specified for witness scopes in + invocations from CLI (#1973) + +Improvements: + * local variable handling was refactored in the compiler, removing some + duplicate code and improving robustness (#1921) + * CLI help now describes how "unvote" can be done (#1985) + +Bugs fixed: + * boolean parameters to function invocations via RPC were not processed + correctly (#1976) + * VM CLI used too restrictive default call flags for loaded scripts (#1981) + * IPv6 check in NNS contract was out of date wrt C# implementation (#1969) + +## 0.95.0 "Sharpness" (17 May 2021) + +This version mostly implements N3 RC2 protocol changes (and is fully +RC2-compatible), but also brings NEP-11 CLI support and small improvements for +node operators. + +Please note that this release is incompatible with 0.94.1 and there won't be +long-term support provided for it. + +New features: + * CLI command for NEP-17 transfers now accepts `data` parameter for the + transfer (#1906) + * contract deployment CLI comand now also accepts `data` parameter for + `_deploy` method (#1907) + * NEP-11 and NEP-17 transfers from CLI can now have multiple signers (#1914) + * `System.Runtime.BurnGas` interop was added to burn some GAS as well as + `refuel` GAS contract method to add GAS to current execution environment + (#1937) + * port number announced via P2P can now differ from actual port node is bound + to if new option is used (#1942) + * CLI now supports full set of NEP-11 commands, including balance and + transfers (#1918) + * string split, memory search and compare functions added to stdlib (#1943) + * MaxValidUntilBlockIncrement can now be configured (#1963) + +Behavior changes: + * `data` parameter is now passed in a different way to NEP-17 RPC client + methods (#1906) + * default (used if nothing else specified) signer scope is now + `CalledByEntry` in CLI and RPC (#1909) + * `SignAndPushInvocationTx` RPC client method now adds as many signatures as + it can with the wallet given which in some cases allows CLI to create + complete transaction without going through multisignature procedure (#1912) + * `getversion` RPC call now returns network magic number in `network` field + (#1927) + * RoleManagement native contract now emits `Designation` event in + `designateAsRole` method (#1938) + * `System.Storage.Find` syscall now strips full prefix given when + `FindRemovePrefix` option is used (#1941) + * LT, LE, GT, GE VM opcodes now accept Null parameters (#1939) + * `features` field was re-added to contract manifests, though it's not + currently used (#1944) + * node will reread TLS certificates (if any configured) on SIGHUP (#1945) + * contract trusts are now expressed with permission descriptors in manifest + (#1946) + * NEP-11 transfers now also support `data` parameter (#1950) + * N3 RC2 testnet magic differs from N2 RC1 testnet (#1951, #1954) + * stdlib encoding/decoding methods now only accept inputs no longer than 1024 + bytes (#1943) + * `System.Iterator.Create` interop was removed with all associated logic (#1947) + * `Neo.Crypto.CheckSig` and `Neo.Crypto.CheckMultisig` interops were renamed + to `System.Crypto.CheckSig` and `System.Crypto.CheckMultisig` (#1956) + * oracle requests now use Neo-specific JSONPath implementation (#1916) + * native NNS contract was removed and replaced by non-native version (#1965) + +Improvements: + * RPC errors reported by server are now more verbose for `submitblock`, + `sendrawtransaction` calls (#1899) + * all CLI commands that accept addresses now also accept hashes and vice + versa (#1905) + * code cleanup based on a number of static analysis tools (#1908, #1958) + * CLI implementation refactoring (#1905, #1911, #1914, #1915, #1918) + * only one state validator now sends complete stateroot message normally + (#1953) + * oracle HTTPS requests are now sent with User-Agent header (#1955) + * stdlib `itoa` and `atoi` methods can now be called with one parameter + (#1943) + * oracle nodes are no longer on extensible payload whitelist (#1948) + * extensible message pool is now per-sender with configurable size (#1948) + * `static-variables` field support for debugger as well as debug data for + `init` and `_deploy` functions (#1957) + * user documentation for configuration options (#1968) + +Bugs fixed: + * `getproof` RPC request returned successful results in some cases where it + should fail + * `Transfer` events with invalid numbers were not rejected by NEP-17 tracking + code (#1902) + * boolean function parameters were not accepted by `invokefunction` RPC call + implementation (#1920) + * potential races in state validation service (#1953) + * single state validator couldn't ever complete stateroot signature (#1953) + * SV vote resending was missing (#1953) + * SV vote messages used invalid (too big) ValidBlockEnd increment (#1953) + * memory leak in state validation service (#1953) + * NEP-6 wallets have `isDefault` field, not `isdefault` (#1961) + +## 0.94.1 "Channelization" (08 Apr 2021) + +This is the second and much improved N3 RC1-compatible release. We've mostly +focused on documentation and examples with this release, so there is a number +of updates in this area including oracle contract example and NEP-11 NFT +contract example. At the same time proper testnet network revealed some +implementation inconsistencies between NeoGo and C# especially in oracle and +state validation services, so there are fixes for them also. + +Protocol-wise this release is compatible with 0.94.0, but MPT structures have +changed and there are known state change differences for N3 RC1 testnet, so +you need to do full node resynchronization on update from 0.94.0. Some SDK +APIs have changed also improving developer experience, but they may affect +your code. We don't plan to make more of RC1-compatible releases (the protocol +has slightly changed already since the release). + +New features: + * RPC (*Client).GetDesignatedByRole method to easily get node lists from + RoleManagement contract (#1855) + * `calculatenetworkfee` RPC method support (#1858) + * RPC client now has additional methods for NEP-11 contracts and specifically + for NNS native contract (#1857) + * contract deployment from multisig addresses (#1886) + +Behavior changes: + * node roles for RoleManagement contract were moved into separate package + (#1855) + * NotaryVerificationPrice constant was moved into package of its own (#1855) + * testnet configuration now has proper N3 RC1 testnet magic (#1856) + * crypto.Verifiable interface was removed, now any hash.Hashable thing can be + verified (and signed, #1859) + * Network field was dropped from transaction.Transaction, block.Header, + payload.Extensible, state.MPTRoot, payload.P2PNotaryRequest and + network.Message, it was needed for proper hash calculations before recent + protocol changes (even though this field is not a part of serialized + representation of any of these elements), but now it's only used to + sign/verify data and doesn't affect hashing which allowed to simplify + interface here (#1859) + * RPC server now returns `StateRootInHeader` option in its `getversion` + request answers if it's used by the network (#1862) + * NNS record types were moved to separate package (#1857) + * MaxStorageKeyLen and MaxStorageValueLen constants were moved to storage + package from core (#1871) + * oracle service now accepts complete URL for other nodes RPC services (#1877) + +Improvements: + * RPC (*Client).AddNetworkFee method is now more informative in some error + case (#1852) + * proper NEP-17 support in unit test contract (#1852) + * documentation updates for examples and services (#1854, #1890) + * syscall number is now printed for failed syscalls (#1874) + * better logging (#1883, #1889) + * Oracle native contract interop documentation extended (#1884) + * Oracle native contract interop extended with return codes and constants (#1884) + * oracle smart contract example (#1884) + * NEP-11 NFT smart contract example (#1891) + +Bugs fixed: + * node could simultaneously try to connect to the same peer multiple times in + some cases (#1861) + * uniqueness is enforced now for other node addresses provided by peers (#1861) + * stateroot message hash wasn't calculated the same way as in C# (#1859) + * state validation service P2P messages were not signed properly (#1859) + * state.MPTRoot structure JSON representation was different from C# one (#1859) + * stateroot messages generated by state validation service were not setting + proper message category (#1866) + * block persistence caches were flushed too early by MPT-managing code (#1866) + * validated stateroot data overwrote local one which lead to node not + functioning after restart if states were different (#1866) + * peers delivering P2P message with validated stateroot differing from local + one were disconnected (#1866) + * state dump comparing script was using old Ledger native contract ID (#1869) + * candidate registration check was made in different way from C# in native + NEO contract leading to different execution results for erroneous voting + transactions (#1869) + * state change dumps were different from C# node for erroneous voting + transactions even though contract's state was the same (#1869) + * arguments were not completely removed from stack in erroneous + Runtime.Notify calls leading to different stack state with C# + implementation (#1874) + * in case contract failed execution with THROW the node didn't print the + message from the contract properly (#1874) + * `getproof` RPC call output was not strictly compliant with C# + implementation (#1871) + * MPT node serialization and some constants were out of date wrt C# + implementation (#1871) + * native NEP-17 contracts could use stale balance data in some cases (#1876) + * state validation service could lock the system during shutdown, preventing + proper node exit (#1877) + * state validation and oracle services were started before node reaching full + synchronization which could lead to excessive useless traffic (#1877) + * some native contract calls in function arguments could be miscompiled + (#1880) + * oracle service was accepting http URLs instead of https (#1883) + * neofs URIs were subject to host validation in oracle service even though + there is no host there (#1883) + * neofs URI scheme was differing from C# implementation (#1883) + * no default value was used for NeoFS request timeout if it's not specified + in the configuration (#1883) + * oracle response code was marshalled as integer into JSON (#1884) + * MPT could grow in memory unbounded (#1885) + * Storage.Find results could differ from C# in some cases (#1888) + +## 0.94.0 "Tsessebe" (19 Mar 2021) + +N3 RC1-compatible release is here. We've implemented all Neo protocol changes +(including state validation service and NeoFS support for oracles) and are +ready for testnet launch now, so that you could play with new native +contracts, VM instructions and other goodies RC1 brings with it. Some +usability improvements and documentation updates also went into this release +as well as a number of fixes stabilizing Notary subsystem (which is +NeoGo-specific protocol extension). + +Please note that this release is incompatible with 0.93.0. We do plan to make +an update soon (with more examples and documentation), but there won't be +long-term support provided, Neo N3 is still on its way to mainnet (although +RC1 and testnet are major milestones on this route). + +New features: + * Compiler: + - ellipsis support for append() to non-byte slices (#1750) + - NEP-11 standard conformance check (#1722) + - onNEP*Payable compliance checks (#1722) + * you can pass files as contract arguments now with `filebytes` CLI parameter + (#1762) + * CLI now supports specifying addresses everywhere script hashes are accepted + (#1758) + * System.Contract.CreateMultisigAccount interop function (#1763) + * SQRT and POW VM instructions (#1789, #1796) + * new NeoFSAlphabet node role added to RoleManagement contract (#1812) + * node can serve as state validator node (#1701) + * oracles now support NeoFS (#1484, #1830) + * CLI can be used to dump wallet's public keys (#1811) + * storage fee, candidate register price and oracle request price can now be + adjusted by committee (#1818, #1850) + * native contracts can now be versioned (#1827) + * RPC client was extended with price getters for native contracts (#1838) + +Behavior changes: + * NFTs no longer have "description" field (#1751) + * P2P Notary service configuration moved to ApplicationConfiguration section + (#1747) + * native contract methods requiring write permission in call flags now also + require read permission (#1777, #1837) + * System.Contract.Call interop function now requires state read permission + (#1777) + * NeoGo no longer supports Go 1.13 (#1791) + * native contract calls are now contract version aware (#1744) + * interop wrappers for smart contracts now use `int` type for all integers + (#1796) + * MaxTransactionsPerBlock, MaxBlockSize, MaxBlockSystemFee settings are now a + part of node configuration and no longer are available in Policy contract + (#1759, #1832) + * storage items can no longer be constant (#1819) + * state root handling is now conformant with C# implementation (with state + validators and vote/stateroot messages, #1701) + * blocks no longer have ConsensusData section, primary index is now a part of + the header (#1792, #1840, #1841) + * `wallet multisign sign` command was renamed to `wallet sign`, it now works + not just for multisignature contract signing, but also for multiple regular + signers as well as contract verification signing + * conversion interops were moved to StdLib native contract (#1824) + * crypto interops (except basic `CheckSig` and `CheckMultiSig`) were moved to + CryptoLib native contract (#1824) + * PACK, UNPACK and CONVERT instructions now cost more (#1826) + * some native contract types were adjusted (#1820) + * native Neo's `getCommittee` and `getNextBlockValidators` methods now cost + less (#1828) + * block/transaction/payload hashing and signing scheme has changed (#1829, + #1839) + * signing context is now serialized to JSON using base64 for data (#1829) + * System.Contract.IsStandard interop was removed (#1834) + * notifications are no longer allowed for safe contract methods (#1837) + +Improvements: + * verification script are now allowed to make contract calls (#1751) + * extensible payloads now have the same size limit as other P2P messages + (#1751) + * error logging is more helpful now in some cases (#1747, #1757) + * function inlining support in compiler for internal intrinsics, interop + refactoring (#1711, #1720, #1774, #1785, #1804, #1805, #1806, #1796, #1809) + * documentation updates (#1778, #1843) + * `sendrawtransaction` and `submitblock` RPC calls now return more detailed + information about errors for failed submissions (#1773) + * NeoGo now supports Go 1.16 (#1771, #1795) + * NEP-17 transfer tracking was optimized to avoid some DB accesses (#1782) + * interop wrappers added for SIGN/ABS/MAX/MIN/WITHIN instructions (#1796) + +Bugs fixed: + * `designateAsRole` method returned value while it shouldn't (#1746) + * deleting non-existent key could lead to node panic during MPT calculation + (#1754) + * some invalid IPv4 were accepted by name service contract (#1741) + * some legitimate IPv6 addresses were rejected by name service (#1741) + * compiler: append() to byte array produced wrong results for bytes with + >0x80 values (#1750) + * wrong notary-signed transaction size in notifications leading to client + disconnects (#1766) + * CALLT wasn't checking permission to read states and make calls in call + flags (#1777) + * proper escaping wasn't done in some cases on VM stack item conversion to + JSON (#1794) + * improper network fee calculation for complex multi-signed transactions in + some cases (#1797) + * importing package with the same name as compiled one could lead to + incorrect compiler behavior (#1808) + * boolean values were emitted as integers by compiler leading to some + comparison failures (#1822) + * `invokecontractverify` wasn't calculating proper GAS price of verification + using contract (#1825) + * ContractManagement contract wasn't returning NULL value from `getContract` + method if contract is missing (#1851) + +## 0.93.0 "Hyperproduction" (12 Feb 2021) + +This is a 3.0.0-preview5 compatible release with important protocol changes, +improved smart contract interop interface for native contracts (it's much +easier now to use them) and complete Notary subsystem which is a NeoGo +experimental protocol extension for P2P signature collection. + +Please note that this release is incompatible with 0.92.0 and there are no +plans for long-term support of it, Neo 3 is still changing and improving. + +The release is tested with preview5 testnet data (and one of testnet CNs is +already a neo-go node) up to 48K blocks and it has exactly the same storage +data except for the Ledger contract that technically can differ between nodes +(it's not a part of state proper), and in case of neo-go it's intentionally +different (but this doesn't affect contract's functionality and state roots +compatibility). + +New features: + * "ProtocolNotSupported" oracle response code (#1630) + * POPITEM VM opcode (#1670) + * CALLT VM opcode and contract tokens support (#1673) + * extensible P2P payloads (#1667, #1674, #1706, #1709) + * contract method overloading (#1689) + * oracle module (#1427, #1703) + * NNS native contract (#1678, #1712, #1721) + * complete P2P Notary subsytem (experimental protocol extension, use only on + private networks, #1658, #1726) + * Ledger native contract (#1696) + * `getblockheadercount` RPC call (#1718) + * native contract wrappers for Go smart contracts (#1702) + * `getnativecontracts` RPC call (#1724) + +Behavior changes: + * VM CLI now supports and requires manifests to properly work with NEF files + (#1642) + * NEP-17-related CLI commands now output GAS balance as floating point numbers + (#1654) + * `--from` parameter can be omitted now for NEP-17 CLI transfers, the default + wallet address will be used in this case (#1655) + * native contracts now use more specific types for methods arguments (#1657) + * some native contract names and IDs have changed (#1622, #1660) + * NEF file is stored now in contract's state instead of script only which + also affects RPC calls and ContractManagement's interface + * call flag definitions were moved to a package of their own (#1647) + * callback syscalls were removed (#1647) + * calling a contract now requires specifying allowed flags (and `CallEx` + syscall was removed, #1647) + * iterator/enumerator `Concat` calls were removed (#1656) + * `System.Enumerator.*` syscalls were removed (#1656) + * `System.Storage.Find` interface was reworked (#1656) + * NEF file format was changed merging compiler and version fields, adding + reserved fields and tokens (#1672) + * registering as committee/validator candidate now costs 1000 GAS (#1684) + * dbft now uses extensible P2P payloads (#1667) + * contract hashing scheme was changed now including names in the mix (#1686) + * GAS fees JSON marshalling was changed to plain integers (#1687, #1723) + * native methods requiring committee signature now fail the script instead of + returning `false` (#1695) + * native ContractManagement contract now has two `deploy` methods, with + additional data and without it (#1693) + * updated contract manifest now can't change contract's name (#1693) + * default values of native contracts were moved to storage from code (#1703) + * blocked accounts now store empty string instead of `01` byte (#1703) + * testnet magic number was changed for preview5 (#1709, #1715) + * `onPayment` method was renamed to `onNEP17Payment` (#1712) + * manifests are stored and accessed as stack items instead of JSON (#1704) + * zero-length "Headers" packet is a protocol error now (#1716) + * `getstorage` RPC call now uses base64 instead of hex for input/output + (#1717) + * state dumper and comparer now use base64 instead of hex (#1717) + * deployed contracts and manifests are now checked for correctness (#1729) + * transaction scripts are now checked for correctness before execution + (#1729) + +Improvements: + * default VM interop prices were adjusted (#1643) + * batched MPT updates (#1641) + * you can use `-m` now for manifest parameter of contract deploying command + (#1690) + * transaction cosigners can be specified with addresses now (#1690) + * compiler documentation was updated (#1690) + +Bugs fixed: + * oracle response transaction can't be correctly evicted from the mempool + (#1668) + * the node left with zero peers didn't initiate reconnections to seeds in rare + cases (#1671) + * native contracts supposed to check for committee witness in fact checked + for validators witness (#1679) + * it was allowed for contracts to have non-bool `verify` methods (#1689) + * previous proposal reuse could lead to empty blocks accepted even if there + are transactions in the mempool (#1707) + * NEO contract's `getCommittee` method name was misspelled (#1708) + * CLI wasn't correctly handling escape sequences (#1727) + * init and deploy methods could be misoptimized leading to execution failures + (#1730) + * NEP-17 contract example was missing `onNEP17Payment` invocation (#1732) + * missing state read privilege could lead to transaction failures for + transfers from CLI or when using native contract wrappers (#1734, #1735) + * some native contract methods had different parameter names and return + output types (#1736) + +## 0.92.0 "Fermentation" (28 Dec 2020) + +NeoGo project is closing year 2020 with 3.0.0-preview4 compatible release that +also has much improved performance, a lot of updates to compiler and SDK and +some experimental protocol extensions. This release also is the most tested +release of NeoGo ever, we've reached 83% code coverage (Neo 2.x only has +66%). + +Please note that this release is incompatible with 0.91.0 and there are no +plans for long-term support of it, Neo 3 is still changing and improving. + +Protocol-wise this release is tested with preview4 testnet (including working +in consensus with C# nodes) and it is compatible with it even though there are +some known storage change mismatches between NeoGo and C# (functionally the +contents is the same, this mismatch is caused by JSON handling differences and +needs to be addressed at the protocol level). + +New features: + * "high priority" transaction attribute (#1341) + * "oracle response" transaction attribute (#1407, #1520, #1573) + * new GAS distribution and committee update scheme (#1364, #1529, #1518, + #1542, #1549, #1636, #1639) + * oracle native contract (#1409, #1432, #1474, #1476, #1521, #1528, #1548, + #1556, #1625) + * designation native contract (#1451, #1504, #1519) + * support for `_deploy` method (#1452, #1466) + * `MaxTraceableBlocks` parameter can now be configured (#1520) + * `KeepOnlyLatestState` configuration option to drop old MPT data (#1553) + * `StateRootInHeader` configuration option to include state root data in the + header (it's a protocol extension, so use with care, #1500) + * NEP-5 was replaced by NEP-17 (#1558, #1574, #1617, #1636) + * `RemoveUntraceableBlocks` configuration option to perform old (unreachable) + block and transaction purging (#1561) + * stable deployed contract hashes (#1555, #1616, #1628) + * `Safe` method flag for contract manifests (#1597, #1607) + * native Management contract to deploy/update/destroy contracts (#1610, + #1624, #1636) + * Compiler/SDK: + - base58 encoding/decoding interops for smart contracts (#1350, #1375) + - you can use aliases now when importing interop packages (#397) + - `+=` operator is now supported for strings (#1351) + - `switch` without statement support (#1355) + - `make()` support for maps and slices using basic types (#1352) + - `copy()` support for byte slices (#1352, #1383) + - `iota` support (#1361) + - support for basic function literals (#1343) + - new variable initialization is now supported in `if` statements (#1343) + - `defer` is now supported (#1343) + - `recover()` support for exception handling (#1343, #1383) + - `delete()` support (#1391) + - `util.Remove()` function is added for smart contracts to remove elements + from slice (#1401) + - `interop` package now provides specific type aliases like Hash160/Hash256 + for smart contracts to use (it affects generated manifests, #1375, #1603) + - variables and function calls support in struct and slice literals (#1425) + - atoi/itoa encoding/decoding interops for smart contracts (#1530, #1533) + - contracts specifying NEP-17 as supported standard are now checked for + interface compliance (#1373) + - `contract.CallEx` function is now available to contracts that allows + specifying flags for contract called (#1598) + * CLI: + - support for array parameters in invocations (#1363, #1367) + - contract addresses can now be imported into wallet (#1362) + - deploying/invoking from multisignature accounts (#1461) + * RPC: + - `getcommittee` RPC call (#1416) + - limits and paging are now supported for `get*transfers` calls (#1419) + - `getunclaimedgas` RPC call now also supports passing address as a + parameter (#1418) + - `getcontractstate` now also accepts addresses, IDs and names (for native + contracts, #1426) + - batched JSON-RPC requests support (#1514) + - `invokecontractverify` RPC call to run verification scripts (#1524) + * P2P notaries subsystem (configurable `P2PSigExtensions` protocol extension, + use with care): + - optional `NotValidBefore` transaction attribute (#1490) + - optional `Conflicts` transaction attribute (#1507) + - native Notary contract (#1557, #1593) + - notary request P2P payload (#1582) + +Behavior changes: + * converting items to boolean now fail for strings of bytes longer than 32 + (#1346) + * consensus messages now use uint8 field for validator indexes (#1350) + * maximum possible try-catch nesting is now limited to 16 levels (#1347) + * maximum manifest size increased to 64K (#1365, #1555) + * required flags changed for many interop functions (#1362) + * compiler no longer generates code to log `panic()` message (#1343) + * `GetBlock`, `GetTransaction` and similar interop functions now return + pointers to structures (#1375) + * calling script hash now also is accounted for by CheckWitness interop + (#1435, #1439) + * CLI is using `--address` parameter everywhere it's needed (some commands + used `--addr` previously (#1434) + * VM now restricts comparable byte array size to 64K (#1433) + * `FeeOnly` witness scope was renamed to `None` (#1450) + * `getvalidators` RPC call was renamed to `getnextblockvalidators` (#1449) + * `emit.Opcode` is now `emit.Opcodes`, allowing for variadic specification of + opcodes (#1452) + * `CalculateNetworkFee` was moved to `fee.Calculate` (#1467) + * fault exception string is now returned for failed invocations (#1462) + * `runtime.GetInvocationCounter` no longer errors (#1455) + * `invoke*` RPC calls now also return `transaction` field (#1418) + * `getversion` RPC call now also returns network magic number (#1489) + * RPC calls now return data in base64 instead of hex (#1489, #1525, #1537) + * blocked accounts interface and storage was changed in Policy contract (#1504) + * voting fee is lower now (#1510) + * blocks are now processed with two execution triggers, `OnPersist` and + `PostPersist`, `getapplicationlog` RPC call was updated to support passing + trigger type (#1515, #1531, #1619) + * storage fee formula has changed (#1517) + * `MaxValidUntilBlockIncrement` is now 5760 instead of 2102400 (#1520) + * Policy contract no longer saves default values during initialization + (#1535) + * opcode pricing was changed and now it's adjustable (#1538, #1615) + * contracts no longer have `IsPayable` (see NEP-17) and `HasStorage` (they + all have it by default now) features (#1544) + * notification size is restricted now (#1551) + * unsolicited `addr` messages are now treated as errors (#1562) + * native contracts no longer have `name()` methods, it is now a part of + manifest (#1557) + * transaction fees, invocation GAS counters and unclaimed GAS values are now + returned as strings with floating point values from RPC calls (#1566, + #1572, #1604) + * NEF format was changed (#1555) + * `engine.AppCall()` interop was renamed to `contract.Call()` (#1598) + * call flags were renamed (#1608) + * deploying contract now costs at minimum 10 GAS and MaxGasInvoke + configuration was adjusted to account for that (the fee is configurable by + the committee, #1624, #1635) + +Improvements: + * a lot of new tests added (#1335, #1339, #1341, #1336, #1378, #1452, #1508, + #1564, #1567, #1583, #1584, #1587, #1590, #1591, #1595, #1602, #1633, + #1638) + * a number of optimizations across all node's components applied (#1342, + #1347, #1337, #1396, #1398, #1403, #1405, #1408, #1428, #1421, #1463, + #1471, #1492, #1493, #1526, #1561, #1618) + * smartcontract package now provides function to create simple majority + multisignature contract (in addition to BFT majority, #1341) + * `AddNetworkFee()` now supports contract addresses (#1362) + * error handling and help texts for CLI wallet commands (#1434) + * compiler emitting short jump instructions if possible (#805, #1488) + * compiler emitting jump instructions with embedded conditions where possible + (#1351) + * transaction retransmission mechanism added to mempool (#1536) + * parallel block fetching (#1568) + * Binary and Runtime interops refactored to separate packages (#1587) + * message broadcasts now finish successfully if the message is sent to 2/3 of + peers (#1637) + +Bugs fixed: + * TRY opcode implementation was not allowing for 0 offsets (#1347) + * compiler wasn't dropping unused elements returned from calls (#683) + * MEMCPY with non-zero destination index was not working correctly (#1352) + * asset transfer from CLI didn't work (#1354) + * specifying unknown DB type in configuration file induced node crash (#1356) + * address specifications in configuration file didn't work correctly (#1356) + * RPC server wasn't processing hashes with "0x" prefix in parameters (#1368) + * incorrect context unloding on exception handling (#1343) + * attempt to get committee only returned validators if there was no voting on + chain (#1370) + * block queue could be attacked with wrong blocks to cause OOM (#1374) + * token sale example wasn't checking witness correctly (#1379) + * structure methods were added to generated manifests (#1381) + * type conversions in `switch` and `range` statements were compiled as + function calls (#1383) + * RPC server result format wasn't conforming to C# implementation for + `getapplicationlog` and `invoke*` (#1371, #1387) + * subslicing non-byte slices was miscompiled (it's forbidden now, #1401) + * potential deadlock in consensus subsystem (#1410) + * race in peer connection closing method (#1378) + * race in MPT calculation functions (#1378) + * possible panic on shutdown (#1411) + * block-level `getapplicationlog` result had block hash in it (#1413) + * fail CN execution if wrong password is provided in the configuration + (#1419) + * tx witness check didn't account for GAS properly when several witnesses are + used (#1439) + * map keys longer than 64 bytes were allowed (#1433) + * unregistered candidate with zero votes wasn't removed (#1445) + * standard contract's network fee wasn't calculated correctly (#1446) + * witness checking wasn't taking into account transaction size fee (#1446) + * sending invalid transaction from the CLI wasn't prevented in some cases + (#1448, #1479, #1604) + * `System.Storage.Put` wasn't accounting for new key length in its GAS price + (#1458) + * blocks can't have more than 64K transactions (#1454) + * Policy native contract wasn't limiting some values (#1442) + * MerkleBlock payload wasn't serialized/deserialized properly (#1454, #1591) + * partial contract updates were not always possible (#1460) + * potential panic on verification with incorrect signature size (#1467) + * CLI attempted to save transaction when it wasn't requested (#1467) + * VM allowed to create bigger integers than it should support (#1464) + * some protocol limits were not enforced (#1469) + * iterating over storage items could produce incorrect KV pairs when using + LevelDB or BadgerDB (#1478) + * stale transaction were not deleted from the mempool (#1480) + * node panic during block processing with BoltDB (#1482) + * node that failed to connect to seeds on startup never attempted to + reconnect to them again (#1486, #1592) + * compiler produced incorrect code for if statements using function calls + (#1479) + * invocation stack size check wasn't performed in some cases (#1492) + * incorrect code produced by the compiler when assigning slices returned from + functions to new variables (#1495) + * websocket client closing connection on new events (#1494) + * minor `getrawtransaction`/`gettransactionheight` and NEP5-related RPC + implementation incompatibilities (#1543, #1550) + * VM CLI breakpoint wasn't stopping before the instruction (#1584) + * VM CLI was incorrectly processing missing parameter error (#1584) + * wallet conversion wasn't performed correctly (#1590) + * node didn't return all requested blocks in response to `getblocks` P2P + requests (#1595) + * CN didn't request transactions properly from its peers in some cases + (#1595) + * incorrect manifests generated for some parameter types (#1599) + * incorrect code generated when no global variables are present, but there + are some global constants (#1598) + * native contract invocations now set proper calling script hash (#1600) + * byte string and buffer VM stack items conversion to JSON differed from C# + (#1609) + * when mempool is full new transaction's hash could still be added into it + even if it is to be rejected afterwards (#1621) + * CN wasn't always performing timestamp validation correctly (#1620) + * incorrect stack contents after execution could stop block processing + (#1631) + * `getapplicationlog` RPC call handler wasn't validating its parameters + properly, potentially leading to node crash (#1636) + * a peer could be connected twice in rare circumstances (#1637) + * missing write timeout could lead to broadcasting stalls (#1637) + +## 0.91.0 "Ululation" (18 August 2020) + +We've updated NeoGo for 3.0.0-preview3 compatibility implementing all the +appropriate protocol changes as well as improving NeoGo-specific +components. This release brings with it significant changes for smart +contracts, both in terms of Neo protocol changes (no more there is a single +entry point! execution environment has also changed in lots of ways) and Go +smart contract compiler updates and fixes. + +Please note that this release is incompatible with 0.90.0 and there will be no +long-term support provided for it, Neo 3 is still changing and +improving. If you have any wallets used with 0.90.0 release you'll need to +regenerate them from private keys because of changes to verification scripts +that also changed hashes and addresses. + +But nonetheless it is tested to be compatible with preview3 testnet for up to +68K of blocks in terms of storage changes, with neo-debugger for debug data +produced by the compiler and with consensus process for heterogeneous setups +(like 2 neo-go CNs with 2 C# CNs). + +New features: + * secp256k1 signature checks added to interop functions + (Neo.Crypto.VerifyWithECDsaSecp256k1 and + Neo.Crypto.CheckMultisigWithECDsaSecp256k1 syscalls, + crypto.ECDsaSecp256k1Verify and crypto.ECDSASecp256k1CheckMultisig interop + functions, #918) + * RIPEMD160 hash added to interop functions (Neo.Crypto.RIPEMD160 syscall, + crypto.RIPEMD160 interop function, #918, #1193) + * "NotFound" P2P message (#1135, #1333) + * base64 encoding/decoding interop functions (binary.Base64Encode and + binary.Base64Decode, #1187) + * new contract.GetCallFlags interop (System.Contract.GetCallFlags syscall) + implemented (#1187) + * it is possible now to create iterators and enumerators over primitive VM + types in smart contracts (#1218) + * runtime.Platform interop (System.Runtime.Platform syscall) is now available + to smart contracts in Go (#1218) + * storage.PutEx interop (System.Storage.PutEx syscall) is now available to + smart contracts in Go (#1221) + * exceptions support in VM (#885) + * CLI conversion utility functions for addresses/hashes/etc (#1207, #1258) + * multitransfer transactions now can be generated with RPC client or CLI + (#940, #1260) + * System.Callback.* syscalls for callback creation and execution (callback.* + interop functions, #1197) + * MPT implementation was added (#1235) + * Policy native contract now also contains MaxBlockSystemFee setting (#1195) + * getting blocks by indexes via P2P is now supported (#1192) + * limited pointer support was added to the compiler (#1247) + * voting support in CLI (#1206, #1286) + +Behavior changes: + * crypto.ECDsaVerify interop function was renamed to + crypto.ECDsaSecp256r1Verify now that we have support for secp256k1 curve + (#918) + * many RPC requests/responses changed names used for data fields (#1169) + * runtime.Notify interop function now requires a mandatory UTF8 name + parameter (#1052) and this name can be used to filter notifications (#1266) + * sendrawtransaction and submitblock RPC calls now return a hash instead of + boolean value in case of success (#1216) + * System.Runtime.Log syscall now only accepts valid UTF8 strings no longer + than 1024 bytes (#1218) + * System.Storage.Put syscall now limits keys to 64 bytes and values to 1024 + bytes (#1221) + * PUSHA instruction now works with relative code offset (#1226) + * EQUAL instruction no longer does type conversions, so that values of + different types are always unequal (#1225) + * verification scripts now can't use more than 0.5 GAS (#1202) + * contracts no longer have single entry point, rather they export a set of + methods with specific offsets. Go smart contract compiler has been changed + accordingly to add all exported (as in Go) methods to the manifest + (but with the first letter being lowercased to match NEP-5 expectations, + #1228). Please also refer to examples changes to better see how it affects + contracts, manifests and configuration files (#1296) + * native contracts are now called via Neo.Native.Call syscall (#1191) + * compressed P2P payloads now also contain their uncompressed size (#1212, + #1255) + * NEF files now use double SHA256 for checksums (#1203) + * VM's map keys and contract methods now can only contain valid UTF-8 strings + (#1198) + * stack items now can be converted to/from JSON natively (without + smartcontract.ContractParameters intermediate) which is now used for + invoke* RPC calls and application execution logs (#1242, #1317) + * invoking Policy native contracts now requires AllowsStates (to get + settings) or AllowModifyStates (to change setting) flags (#1254) + * Transaction now has Signers field unifying Sender (the first Signer) and + Cosigners, a Signer can have FeeOnly or any other regular witness scope + (#1184) + * verification scripts no longer have access to blockchain's state (#1277) + * governance scheme was changed to delegated committee-based one. The number + of validators is now specified with ValidatorsCount configuration option, + standby validators are no longer being registered by default (#867, #1300) + * Go 1.13+ is now required to build neo-go (#1281) + * public contract methods now always return some value and this is being + checked by the VM (#1196, #1331, #1332) + * runtime interop package now exports triggers as proper constants rather + than functions (#1299) + * RPC client no longer has SetWIF/WIF methods that didn't do anything useful + anyway (#1328) + +Improvements: + * Neo.Crypto.CheckMultisigWithECDsaSecp256r1 syscall is now available via + crypto.ECDSASecp256r1CheckMultisig interop function (#1175) + * System.Contract.IsStandard syscall now also checks script's container (#1187) + * syscalls no longer have allowed triggers limitations (#1205) + * better testing coverage (#1232, #1318, #1328) + * getrawmempool RPC call now also supports verbose parameter (#1182) + * VMState is no longer being stored as a string for application execution + results (#1236) + * manifest now contains a list of supported standards (#1204) + * notifications can't be changed now by a contract after emitting them + (#1199) + * it is possible to call other contracts from native contracts now (#1271) + * getnep5transfers now supports timing parameters (#1289) + * smartcontract package now has CreateDefaultMultiSigRedeemScript that should + be used for BFT-compliant "m out of n" multisignature script generation + (#1300) + * validators are always sorted now (standby validators were not previously, + #1300) + * debug information now contains all file names (#1295) + * compiler now accepts directory to compile a package, only one file could be + passed previously (#1295) + * some old no longer used functions and structures were removed (#1303) + * contract inspection output was improved for new Neo 3 VM instructions (#1231) + * ping P2P message handling was changed to trigger block requests (#1326) + +Bugs fixed: + * inability to transfer NEO/GAS from deployed contract's address (#1180) + * System.Blockchain.GetTransactionFromBlock syscall didn't pick all of its + arguments from the stack in some error cases (#1187) + * System.Contract.CallEx syscall didn't properly check call flags (#1187) + * System.Blockchain.GetContract and System.Contract.Create syscalls returned + an interop interface instead of plain well-defined structure (#1187) + * System.Contract.Update syscall's manifest checks were improved, return + value was fixed (#1187) + * getnep5balances and getnep5transfers RPC calls now support addresses in + their parameters (#1188) + * rare panic during node's shutdown (#1188) + * System.Runtime.CheckWitness, System.Runtime.GetTime syscalls are only allowed to be called with + AllowStates flag (#1218) + * System.Runtime.GasLeft syscall result for test VM mode was wrong (#1218) + * getrawtransaction RPC call now also returns its VM state after execution + (#1183) + * getnep5balances and getnep5transfers RPC calls now correctly work for + migrated contracts (#1188, #1239) + * compiler now generates correct code for global variables from multiple + files (#1240) + * compiler now correctly supports exported contracts and variables in + packages (#1154) + * compiler no longer confuses functions with the same name from different + packages (#1150) + * MaxBlockSize policy setting was not enforced (#1254) + * missing scope check for signers (#1244) + * compiler now properly supports init() functions (#1253, #1295) + * getvalidators RPC call now returns zero-length array of validators when + there are no registered candidates instead of null value (#1300) + * events were not added to the debug data (#1311, #1315) + * RPC client's BalanceOf method was lacking account parameter (#1323) + * VM CLI debugging commands didn't really allow to step through the contract + (#1328) + * recovery message decoding created incorrect PrepareRequest payload that + lead to consensus failures (#1334) + +## 0.90.0 "Tantalization" (14 July 2020) + +The first Neo 3 compatible release of neo-go! We've targeted to make it +compatible with preview2 release of Neo 3, so it only contains features +available there, but at the same time this makes the node more useful until we +have some more up to date reference version. It's a completely different +network, so almost everything has changed and it's hard to describe it with +the same level of details we usually do (but we'll provide them for subsequent +releases where the changeset is going to be lower in size). Please note that +this is a preview-level release and there won't be long-term support provided +for it, Neo 3 is evolving and the next release won't be compatible with this +one. + +Main Neo 3 features in this release: + * no UTXO + * native contracts + * new VM + * scoped witnesses for transaction + * updated interop/syscalls set + * contract manifests + * more efficient P2P protocol + +Things that have also changed: + * transaction format + * block format + * address format + * wallets + * RPC protocol + * notification subsystem + * executable format output for compiler + +Compatibility level of this neo-go release: + * identical storage changes compared to C# node for 378K blocks of preview2 + testnet + * debugging info produced is compatible with preview2-compatible neo-debugger + * running consensus nodes in heterogeneous setup is possible (2 neo-go CNs + with 2 C# CNs, for example) + +Changes specific to neo-go: + * some CLI parameters like wallet path or RPC endpoint URL have been unified + across all commands and thus have changed in some of them (refer to CLI + help for details) + * as an extension we support post-preview2 cosigners parameter for + invokefunction RPC calls (see neo-project/neo-modules#260) + * Go compiler now supports comparisons with nil properly + * we no longer provide bootstrapping 6k block dump for private networks, you + have 30000000 GAS right in the genesis block and it's not hard to make use + of it (see + [neo-go-sc-wrkshp](https://github.com/nspcc-dev/neo-go-sc-wrkshp) for an + example of how to use it) + * we have a conversion tool for your old Neo 2 wallets (`wallet convert` + command), so you can reuse keys on Neo 3 networks + * util.Equals interop function may not function the way you expect it to due + to Neo VM changes, it still is an EQUAL opcode though. This interop may be + removed in the future. + +## Older versions + +Please refer to the [master-2.x branch +CHANGELOG](https://github.com/nspcc-dev/neo-go/tree/master-2.x/CHANGELOG.md) +for versions prior to 0.90.0 (that are Neo 2 compatible, unlike 0.90.0+ that +are Neo 3 compatible). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ec598ba --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,25 @@ +# Contributing + +First, thank you for contributing! We love and encourage pull requests from everyone. Please +follow the guidelines: + +1. Check open [issues](https://github.com/nspcc-dev/neo-go/issues) and +[pull requests](https://github.com/nspcc-dev/neo-go/pulls) for existing discussions. +1. Open an issue first to discuss a new feature or enhancement. +1. Write tests and make sure the test suite passes locally and on CI. +1. When optimizing something, write benchmarks and attach the results: + ``` + go test -run - -bench BenchmarkYourFeature -count=10 ./... >old // on master + go test -run - -bench BenchmarkYourFeature -count=10 ./... >new // on your branch + benchstat old new + ``` + `benchstat` is described here https://godocs.io/golang.org/x/perf/cmd/benchstat. + +1. Open a pull request and reference the relevant issue(s). +1. Make sure your commits are logically separated and have good comments + explaining the details of your change. Add a package/file prefix to your + commit if that's applicable (like 'vm: fix ADD miscalculation on full + moon'). +1. After receiving a feedback, amend your commits or add new ones as + appropriate. +1. **Have fun!** diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1165fbe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# Builder image +# Keep go version in sync with Build GA job. +FROM golang:1.25-alpine AS builder + +# Display go version for information purposes. +RUN go version + +RUN set -x \ + && apk add --no-cache git make \ + && mkdir -p /tmp + +COPY . /neo-go + +WORKDIR /neo-go + +ARG REPO=repository +ARG VERSION=dev + +RUN VERSION=$VERSION REPO=$REPO make build + +# Executable image +FROM alpine + +ARG VERSION=dev +LABEL version=$VERSION + +WORKDIR / + +COPY --from=builder /neo-go/config /config +COPY --from=builder /neo-go/.docker/privnet-entrypoint.sh /usr/bin/privnet-entrypoint.sh +COPY --from=builder /neo-go/bin/neo-go /usr/bin/neo-go +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ + +ENTRYPOINT ["/usr/bin/privnet-entrypoint.sh"] + +CMD ["node", "--config-path", "/config", "--privnet"] diff --git a/Dockerfile.wsc b/Dockerfile.wsc new file mode 100644 index 0000000..22e4e84 --- /dev/null +++ b/Dockerfile.wsc @@ -0,0 +1,37 @@ +# Builder image +# Keep go version in sync with Build GA job. +FROM golang:1.25.0-windowsservercore-ltsc2022 AS builder + +COPY . /neo-go + +WORKDIR /neo-go + +ARG REPO=repository +ARG VERSION=dev + +SHELL ["cmd", "/S", "/C"] +RUN go env -w CGO_ENABLED=0 +ENV GOGC=off + +RUN go build -trimpath -v -o ./bin/neo-go.exe -ldflags="-X %REPO%/pkg/config.Version=%VERSION%" ./cli/main.go + +# Executable image +FROM mcr.microsoft.com/windows/servercore:ltsc2022 + +ARG VERSION +LABEL version=%VERSION% + +WORKDIR / + +COPY --from=builder /neo-go/config /config +COPY --from=builder /neo-go/.docker/privnet-entrypoint.ps1 /usr/bin/privnet-entrypoint.ps1 +COPY --from=builder /neo-go/bin/neo-go.exe /usr/bin/neo-go.exe + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';", "$ProgressPreference = 'SilentlyContinue';"] + +# Check executable version. +RUN /usr/bin/neo-go.exe --version + +ENTRYPOINT ["powershell", "-File", "/usr/bin/privnet-entrypoint.ps1"] + +CMD ["node", "--config-path", "/config", "--privnet"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..bcc0de6 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-2023 NeoSPCC (@nspcc-dev), Anthony De Meulemeester (@anthdm), City of Zion community (@CityOfZion) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9ac0ed --- /dev/null +++ b/Makefile @@ -0,0 +1,159 @@ +BRANCH = "master" +REPONAME = "tutus-chain" +NETMODE ?= "tutus-testnet" +BINARY=tutus +BINARY_PATH=./bin/$(BINARY)$(shell go env GOEXE) +GO_VERSION ?= 1.25 +DESTDIR = "" +SYSCONFIGDIR = "/etc" +BINDIR = "/usr/bin" +SYSTEMDUNIT_DIR = "/lib/systemd/system" +UNITWORKDIR = "/var/lib/tutus" + +IMAGE_SUFFIX="$(shell if [ "$(OS)" = Windows_NT ]; then echo "_WindowsServerCore"; fi)" +D_FILE ?= "$(shell if [ "$(OS)" = Windows_NT ]; then echo "Dockerfile.wsc"; else echo "Dockerfile"; fi)" +DC_FILE ?= ".docker/docker-compose.yml" # Single docker-compose for Ubuntu/WSC, should be kept in sync with ENV_IMAGE_TAG. +ENV_IMAGE_TAG="env_neo_go_image" + +REPO ?= "$(shell go list -m)" +VERSION ?= "$(shell git describe --tags --match "v*" --abbrev=8 2>/dev/null | sed -r 's,^v([0-9]+\.[0-9]+)\.([0-9]+)(-.*)?$$,\1 \2 \3,' | while read mm patch suffix; do if [ -z "$$suffix" ]; then echo $$mm.$$patch; else patch=`expr $$patch + 1`; echo $$mm.$${patch}-pre$$suffix; fi; done)" +MODVERSION ?= "$(shell cat go.mod | cat go.mod | sed -r -n -e 's|.*pkg/interop (.*)|\1|p')" +BUILD_FLAGS = "-X '$(REPO)/pkg/config.Version=$(VERSION)' -X '$(REPO)/cli/smartcontract.ModVersion=$(MODVERSION)'" + +IMAGE_REPO=tutus-one/tutus-chain + +DISABLE_NEOTEST_COVER=1 + +ROOT_DIR:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +GOMODDIRS=$(dir $(shell find $(ROOT_DIR) -name go.mod)) + +# All of the targets are phony here because we don't really use make dependency +# tracking for files +.PHONY: build $(BINARY) deps image docker/$(BINARY) image-latest image-push image-push-latest clean-cluster \ + test vet lint fmt cover version gh-docker-vars + +build: deps + @echo "=> Building binary" + @set -x \ + && export GOGC=off \ + && export CGO_ENABLED=0 \ + && go build -trimpath -v -ldflags $(BUILD_FLAGS) -o ${BINARY_PATH} ./cli/main.go + +$(BINARY): build + +docker/$(BINARY): + @echo "=> Building binary using clean Docker environment" + @docker run --rm -t \ + -v `pwd`:/src \ + -w /src \ + -u "$$(id -u):$$(id -g)" \ + --env HOME=/src \ + golang:$(GO_VERSION) make $(BINARY) + +tutus.service: tutus.service.template + @sed -r -e 's_BINDIR_$(BINDIR)_' -e 's_UNITWORKDIR_$(UNITWORKDIR)_' -e 's_SYSCONFIGDIR_$(SYSCONFIGDIR)_' $< >$@ + +install: build tutus.service + @echo "=> Installing systemd service" + @mkdir -p $(DESTDIR)$(SYSCONFIGDIR)/tutus \ + && mkdir -p $(SYSTEMDUNIT_DIR) \ + && cp ./tutus.service $(SYSTEMDUNIT_DIR) \ + && cp ./config/protocol.tutus.yml $(DESTDIR)$(SYSCONFIGDIR)/tutus \ + && cp ./config/protocol.tutus.testnet.yml $(DESTDIR)$(SYSCONFIGDIR)/tutus \ + && install -m 0755 -t $(BINDIR) $(BINARY_PATH) \ + +postinst: install + @echo "=> Preparing directories and configs" + @id tutus || useradd -s /usr/sbin/nologin -d $(UNITWORKDIR) tutus \ + && mkdir -p $(UNITWORKDIR) \ + && chown -R tutus:tutus $(UNITWORKDIR) $(BINDIR)/tutus \ + && systemctl enable tutus.service + +image: deps + @echo "=> Building image" + @echo " Dockerfile: $(D_FILE)" + @echo " Tag: $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)" + @docker build -f $(D_FILE) -t $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX) --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) . + +image-latest: deps + @echo "=> Building image with 'latest' tag" + @echo " Dockerfile: Dockerfile" # Always use default Dockerfile for Ubuntu as `latest`. + @echo " Tag: $(IMAGE_REPO):latest" + @docker build -t $(IMAGE_REPO):latest --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) . + +image-push: + @echo "=> Publish image" + @echo " Tag: $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)" + @docker push $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX) + +image-push-latest: + @echo "=> Publish image for Ubuntu with 'latest' tag" + @docker push $(IMAGE_REPO):latest + +deps: + @CGO_ENABLED=0 \ + go mod download + @CGO_ENABLED=0 \ + go mod tidy -v + +version: + @echo $(VERSION) + +gh-docker-vars: + @echo "file=$(D_FILE)" + @echo "version=$(VERSION)" + @echo "repo=$(IMAGE_REPO)" + @echo "suffix=$(IMAGE_SUFFIX)" + +test: + @go test ./... -cover + +vet: + @go vet ./... + +.golangci.yml: + curl -L -o $@ https://github.com/nspcc-dev/.github/raw/master/.golangci.yml + +lint: .golangci.yml + @for dir in $(GOMODDIRS); do \ + (cd "$$dir" && golangci-lint run --config $(ROOT_DIR)/$< | sed -r "s,^,$$dir," | sed -r "s,^$(ROOT_DIR),,") \ + done + +fmt: + @gofmt -l -w -s $$(find . -type f -name '*.go'| grep -v "/vendor/") + +cover: + @go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./pkg/...,./cli/... + @go tool cover -html=coverage.txt -o coverage.html + +# --- Ubuntu/Windows environment --- +env_image: + @echo "=> Building env image" + @echo " Dockerfile: $(D_FILE)" + @echo " Tag: $(ENV_IMAGE_TAG)" + @docker build \ + -f $(D_FILE) \ + -t $(ENV_IMAGE_TAG) \ + --build-arg REPO=$(REPO) \ + --build-arg VERSION=$(VERSION) . + +env_up: + @echo "=> Bootup environment" + @echo " Docker-compose file: $(DC_FILE)" + @docker compose -f $(DC_FILE) up -d node_one node_two node_three node_four + +env_single: + @echo "=> Bootup environment" + @docker compose -f $(DC_FILE) up -d node_single + +env_down: + @echo "=> Stop environment" + @docker compose -f $(DC_FILE) down + +env_restart: + @echo "=> Stop and start environment" + @docker compose -f $(DC_FILE) restart + +env_clean: env_down + @echo "=> Cleanup environment" + @docker volume rm docker_volume_chain diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e390d8 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# Tutus Chain + +Sovereign blockchain platform for government deployments. Forked from [NeoGo](https://github.com/nspcc-dev/neo-go). + +**Website:** tutus.one +**License:** Apache 2.0 + +## Overview + +Tutus is an independent blockchain designed for government-grade deployments with: + +- **Sovereign instances** - Each government runs their own chain +- **1-second blocks** - Fast finality with dBFT consensus +- **7 validators** - Government-controlled consensus nodes +- **Zero gas for citizens** - Native contracts subsidize operations +- **Inter-government bridge** - Optional cross-border interoperability + +## Building + +Requires Go 1.24+ and `make`: + +```bash +make build +``` + +The resulting binary is `bin/tutus`. + +## Running a Node + +Start a Tutus node: + +```bash +./bin/tutus node --config-file ./config/protocol.tutus.yml +``` + +For testnet: + +```bash +./bin/tutus node --config-file ./config/protocol.tutus.testnet.yml +``` + +## Network Flags + +- `--tutus` - Tutus mainnet (government deployment) +- `--tutus-testnet` - Tutus testnet + +## Configuration + +Network configurations are in `./config/`: + +| File | Purpose | +|------|---------| +| `protocol.tutus.yml` | Production government deployment | +| `protocol.tutus.testnet.yml` | Development testnet | + +## Docker + +```bash +docker build -t tutus-chain . +docker run -d --name tutus -p 10333:10333 -p 10332:10332 tutus-chain +``` + +## Native Contracts (Planned) + +The following will be built into the Tutus protocol: + +| Contract | Purpose | +|----------|---------| +| PersonToken | Soul-bound identity | +| Scire | Universal education | +| Salus | Universal healthcare | +| Sese | Life planning | +| Tribute | Anti-hoarding economics | +| VTS | Government stablecoin | +| Eligere | Democratic voting | + +## Validator Setup + +See [docs/validator.md](docs/validator.md) for government validator deployment. + +## Origin + +Tutus is forked from NeoGo, the Go implementation of the Neo N3 blockchain. +We maintain compatibility with the Neo VM while extending the protocol for government use cases. + +## License + +Apache 2.0 - See [LICENSE.md](LICENSE.md) diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..39e36a7 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,36 @@ +# Roadmap for neo-go + +This defines approximate plan of neo-go releases and key features planned for +them. Things can change if there is a need to push a bugfix or some critical +functionality. + +## Versions 0.7X.Y (as needed) +* Neo 2.0 support (bug fixes, minor functionality additions) + +## Version 0.115.0 (~Dec 2025) + * protocol updates + * bug fixes + +## Version 1.0 (2026, TBD) + * stable version + +# Deprecated functionality + +As the node and the protocol evolve some external APIs can change. Usually we +try keeping backwards compatibility for some time (like half a year) unless +it's impossible to do for some reason. But eventually old +APIs/commands/configurations will be removed and here is a list of scheduled +breaking changes. Consider changing your code/scripts/configurations if you're +using anything mentioned here. + +## Candidate registration via `registerCandidate` method of native NeoToken contract + +The original way of Neo candidate registration via `wallet candidate register` CLI +command using `registerCandidate` method of native NeoToken contract is deprecated +and has been superseded by the GAS transfer approach. Deprecated candidate +registration way via `registerCandidate` method call is supported via +`--useRegisterCall` flag. + +Removal of `registerCandidate`–based support of candidate registration will be +done once `registerCandidate` method is officially deprecated and removed from +the NeoToken manifest with the subsequent hardfork. diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..573c0c4 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1 @@ +cli diff --git a/cli/app/app.go b/cli/app/app.go new file mode 100644 index 0000000..aaee0d0 --- /dev/null +++ b/cli/app/app.go @@ -0,0 +1,41 @@ +package app + +import ( + "fmt" + "os" + "runtime" + + "github.com/nspcc-dev/neo-go/cli/query" + "github.com/nspcc-dev/neo-go/cli/server" + "github.com/nspcc-dev/neo-go/cli/smartcontract" + "github.com/nspcc-dev/neo-go/cli/util" + "github.com/nspcc-dev/neo-go/cli/vm" + "github.com/nspcc-dev/neo-go/cli/wallet" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/urfave/cli/v2" +) + +func versionPrinter(c *cli.Context) { + _, _ = fmt.Fprintf(c.App.Writer, "NeoGo\nVersion: %s\nGoVersion: %s\n", + config.Version, + runtime.Version(), + ) +} + +// New creates a NeoGo instance of [cli.App] with all commands included. +func New() *cli.App { + cli.VersionPrinter = versionPrinter + ctl := cli.NewApp() + ctl.Name = "neo-go" + ctl.Version = config.Version + ctl.Usage = "Official Go client for Neo" + ctl.ErrWriter = os.Stdout + + ctl.Commands = append(ctl.Commands, server.NewCommands()...) + ctl.Commands = append(ctl.Commands, smartcontract.NewCommands()...) + ctl.Commands = append(ctl.Commands, wallet.NewCommands()...) + ctl.Commands = append(ctl.Commands, vm.NewCommands()...) + ctl.Commands = append(ctl.Commands, util.NewCommands()...) + ctl.Commands = append(ctl.Commands, query.NewCommands()...) + return ctl +} diff --git a/cli/app/main_test.go b/cli/app/main_test.go new file mode 100644 index 0000000..a8521f3 --- /dev/null +++ b/cli/app/main_test.go @@ -0,0 +1,19 @@ +package app_test + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/internal/versionutil" + "github.com/nspcc-dev/neo-go/pkg/config" +) + +func TestCLIVersion(t *testing.T) { + config.Version = versionutil.TestVersion // Zero-length version string disables '--version' completely. + e := testcli.NewExecutor(t, false) + e.Run(t, "neo-go", "--version") + e.CheckNextLine(t, "^NeoGo") + e.CheckNextLine(t, "^Version:") + e.CheckNextLine(t, "^GoVersion:") + e.CheckEOF(t) +} diff --git a/cli/cmdargs/parser.go b/cli/cmdargs/parser.go new file mode 100644 index 0000000..713cc5d --- /dev/null +++ b/cli/cmdargs/parser.go @@ -0,0 +1,360 @@ +package cmdargs + +import ( + "errors" + "fmt" + "strings" + + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/urfave/cli/v2" +) + +const ( + // CosignersSeparator marks the start of cosigners cli args. + CosignersSeparator = "--" + // ArrayStartSeparator marks the start of array cli arg. + ArrayStartSeparator = "[" + // ArrayEndSeparator marks the end of array cli arg. + ArrayEndSeparator = "]" +) + +const ( + // ParamsParsingDoc is a documentation for parameters parsing. + ParamsParsingDoc = ` Arguments always do have regular Neo smart contract parameter types, either + specified explicitly or being inferred from the value. To specify the type + manually use "type:value" syntax where the type is one of the following: + 'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'. + Array types are also supported: use special space-separated '[' and ']' + symbols around array values to denote array bounds. Nested arrays are also + supported. Null parameter is supported via 'nil' keyword without additional + type specification. + + There is ability to provide an argument of 'bytearray' type via file. Use a + special 'filebytes' argument type for this with a filepath specified after + the colon, e.g. 'filebytes:my_file.txt'. + + Given values are type-checked against given types with the following + restrictions applied: + * 'signature' type values should be hex-encoded and have a (decoded) + length of 64 bytes. + * 'bool' type values are 'true' and 'false'. + * 'int' values are decimal integers that can be successfully converted + from the string. + * 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after + decoding) strings. + * 'hash256' type values should be hex-encoded and have a (decoded) + length of 32 bytes. + * 'bytes' type values are any hex-encoded things. + * 'filebytes' type values are filenames with the argument value inside. + * 'key' type values are hex-encoded marshalled public keys. + * 'string' type values are any valid UTF-8 strings. In the value's part of + the string the colon looses it's special meaning as a separator between + type and value and is taken literally. + + If no type is explicitly specified, it is inferred from the value using the + following logic: + - anything that can be interpreted as a decimal integer gets + an 'int' type + - 'nil' string gets 'Any' NEP-14 parameter type and nil value which corresponds + to Null stackitem + - 'true' and 'false' strings get 'bool' type + - valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160' + type + - valid hex-encoded public keys get 'key' type + - 32 bytes long hex-encoded values get 'hash256' type + - 64 bytes long hex-encoded values get 'signature' type + - any other valid hex-encoded values get 'bytes' type + - anything else is a 'string' + + Backslash character is used as an escape character and allows to use colon in + an implicitly typed string. For any other characters it has no special + meaning, to get a literal backslash in the string use the '\\' sequence. + + Examples: + * 'int:42' is an integer with a value of 42 + * '42' is an integer with a value of 42 + * 'nil' is a parameter with Any NEP-14 type and nil value (corresponds to Null stackitem) + * 'bad' is a string with a value of 'bad' + * 'dead' is a byte array with a value of 'dead' + * 'string:dead' is a string with a value of 'dead' + * 'filebytes:my_data.txt' is bytes decoded from a content of my_data.txt + * 'NSiVJYZej4XsxG5CUpdwn7VRQk8iiiDMPM' is a hash160 with a value + of '682cca3ebdc66210e5847d7f8115846586079d4a' + * '\4\2' is an integer with a value of 42 + * '\\4\2' is a string with a value of '\42' + * 'string:string' is a string with a value of 'string' + * 'string\:string' is a string with a value of 'string:string' + * '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a + key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' + * '[ a b c ]' is an array with strings values 'a', 'b' and 'c' + * '[ a b [ c d ] e ]' is an array with 4 values: string 'a', string 'b', + array of two strings 'c' and 'd', string 'e' + * '[ ]' is an empty array` + + // SignersParsingDoc is a documentation for signers parsing. + SignersParsingDoc = ` Signers represent a set of Uint160 hashes with witness scopes and are used + to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated + as a sender. To specify signers use signer[:scope] syntax where + * 'signer' is a signer's address (as Neo address or hex-encoded 160 bit (20 byte) + LE value with or without '0x' prefix). + * 'scope' is a comma-separated set of cosigner's scopes, which could be: + - 'None' - default witness scope which may be used for the sender + to only pay fee for the transaction. + - 'Global' - allows this witness in all contexts. This cannot be combined + with other flags. + - 'CalledByEntry' - means that this condition must hold: EntryScriptHash + == CallingScriptHash. The witness/permission/signature + given on first invocation will automatically expire if + entering deeper internal invokes. This can be default + safe choice for native NEO/GAS. + - 'CustomContracts' - define valid custom contract hashes for witness check. + Hashes are be provided as hex-encoded LE value string. + At lest one hash must be provided. Multiple hashes + are separated by ':'. + - 'CustomGroups' - define custom public keys for group members. Public keys are + provided as short-form (1-byte prefix + 32 bytes) hex-encoded + values. At least one key must be provided. Multiple keys + are separated by ':'. + + If no scopes were specified, 'CalledByEntry' used as default. If no signers were + specified, no array is passed. Note that scopes are properly handled by + neo-go RPC server only. C# implementation does not support scopes capability. + + Examples: + * 'NNQk4QXsxvsrr3GSozoWBUxEmfag7B6hz5' + * 'NVquyZHoPirw6zAEPvY1ZezxM493zMWQqs:Global' + * '0x0000000009070e030d0f0e020d0c06050e030c02' + * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` + + `CustomGroups:0206d7495ceb34c197093b5fc1cccf1996ada05e69ef67e765462a7f5d88ee14d0' + * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` + + `CustomContracts:1011120009070e030d0f0e020d0c06050e030c02:0x1211100009070e030d0f0e020d0c06050e030c02'` +) + +// GetSignersFromContext returns signers parsed from context args starting +// from the specified offset. +func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, cli.ExitCoder) { + args := ctx.Args() + var ( + signers []transaction.Signer + err error + ) + if args.Present() && args.Len() > offset { + signers, err = ParseSigners(args.Slice()[offset:]) + if err != nil { + return nil, cli.Exit(err, 1) + } + } + return signers, nil +} + +// ParseSigners returns array of signers parsed from their string representation. +func ParseSigners(args []string) ([]transaction.Signer, error) { + var signers []transaction.Signer + for i, c := range args { + cosigner, err := parseCosigner(c) + if err != nil { + return nil, fmt.Errorf("failed to parse signer #%d: %w", i, err) + } + signers = append(signers, cosigner) + } + return signers, nil +} + +func parseCosigner(c string) (transaction.Signer, error) { + var ( + err error + res = transaction.Signer{ + Scopes: transaction.CalledByEntry, + } + ) + address, scopes, _ := strings.Cut(c, ":") + res.Account, err = flags.ParseAddress(address) + if err != nil { + return res, err + } + + if scopes == "" { + return res, nil + } + + res.Scopes = 0 + for s := range strings.SplitSeq(scopes, ",") { + sub := strings.Split(s, ":") + scope, err := transaction.ScopesFromString(sub[0]) + if err != nil { + return transaction.Signer{}, err + } + if scope == transaction.Global && res.Scopes&^transaction.Global != 0 || + scope != transaction.Global && res.Scopes&transaction.Global != 0 { + return transaction.Signer{}, errors.New("'Global' scope can not be combined with other scopes") + } + + res.Scopes |= scope + + switch scope { + case transaction.CustomContracts: + if len(sub) == 1 { + return transaction.Signer{}, errors.New("CustomContracts scope must refer to at least one contract") + } + for _, s := range sub[1:] { + addr, err := flags.ParseAddress(s) + if err != nil { + return transaction.Signer{}, err + } + + res.AllowedContracts = append(res.AllowedContracts, addr) + } + case transaction.CustomGroups: + if len(sub) == 1 { + return transaction.Signer{}, errors.New("CustomGroups scope must refer to at least one group") + } + for _, s := range sub[1:] { + pub, err := keys.NewPublicKeyFromString(s) + if err != nil { + return transaction.Signer{}, err + } + + res.AllowedGroups = append(res.AllowedGroups, pub) + } + default: + } + } + return res, nil +} + +// GetDataFromContext returns data parameter from context args. +func GetDataFromContext(ctx *cli.Context) (int, any, cli.ExitCoder) { + var ( + data any + offset int + params []smartcontract.Parameter + err error + ) + args := ctx.Args() + if args.Present() { + offset, params, err = ParseParams(args.Slice(), true) + if err != nil { + return offset, nil, cli.Exit(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1) + } + if len(params) > 1 { + return offset, nil, cli.Exit("'data' should be represented as a single parameter", 1) + } + if len(params) != 0 { + data, err = smartcontract.ExpandParameterToEmitable(params[0]) + if err != nil { + return offset, nil, cli.Exit(fmt.Sprintf("failed to convert 'data' to emitable type: %s", err.Error()), 1) + } + } + } + return offset, data, nil +} + +// EnsureNone returns an error if there are any positional arguments present. +// It can be used to check for them in commands that don't accept arguments. +func EnsureNone(ctx *cli.Context) cli.ExitCoder { + if ctx.Args().Present() { + return cli.Exit(fmt.Errorf("additional arguments given while this command expects none"), 1) + } + return nil +} + +// ParseParams extracts array of smartcontract.Parameter from the given args and +// returns the number of handled words, the array itself and an error. +// `calledFromMain` denotes whether the method was called from the outside or +// recursively and used to check if CosignersSeparator and ArrayEndSeparator are +// allowed to be in `args` sequence. +func ParseParams(args []string, calledFromMain bool) (int, []smartcontract.Parameter, error) { + res := []smartcontract.Parameter{} + for k := 0; k < len(args); { + s := args[k] + switch s { + case CosignersSeparator: + if calledFromMain { + return k + 1, res, nil // `1` to convert index to numWordsRead + } + return 0, []smartcontract.Parameter{}, errors.New("invalid array syntax: missing closing bracket") + case ArrayStartSeparator: + numWordsRead, array, err := ParseParams(args[k+1:], false) + if err != nil { + return 0, nil, fmt.Errorf("failed to parse array: %w", err) + } + res = append(res, smartcontract.Parameter{ + Type: smartcontract.ArrayType, + Value: array, + }) + k += 1 + numWordsRead // `1` for opening bracket + case ArrayEndSeparator: + if calledFromMain { + return 0, nil, errors.New("invalid array syntax: missing opening bracket") + } + return k + 1, res, nil // `1`to convert index to numWordsRead + default: + param, err := smartcontract.NewParameterFromString(s) + if err != nil { + // '--' argument is skipped by urfave/cli library, which leads + // to [--, addr:scope] being transformed to [addr:scope] and + // interpreted as a parameter if other positional arguments are not present. + // Here we fallback to parsing cosigners in this specific case to + // create a better user experience ('-- addr:scope' vs '-- -- addr:scope'). + if k == 0 { + if _, err := parseCosigner(s); err == nil { + return 0, nil, nil + } + } + return 0, nil, fmt.Errorf("failed to parse argument #%d: %w", k+1, err) + } + res = append(res, *param) + k++ + } + } + if calledFromMain { + return len(args), res, nil + } + return 0, []smartcontract.Parameter{}, errors.New("invalid array syntax: missing closing bracket") +} + +// GetSignersAccounts returns the list of signers combined with the corresponding +// accounts from the provided wallet. +func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers []transaction.Signer, accScope transaction.WitnessScope) ([]actor.SignerAccount, error) { + signersAccounts := make([]actor.SignerAccount, 0, len(signers)+1) + sender := senderAcc.ScriptHash() + signersAccounts = append(signersAccounts, actor.SignerAccount{ + Signer: transaction.Signer{ + Account: sender, + Scopes: accScope, + }, + Account: senderAcc, + }) + for i, s := range signers { + if s.Account == sender { + signersAccounts[0].Signer = s + continue + } + signerAcc := wall.GetAccount(s.Account) + if signerAcc == nil { + return nil, fmt.Errorf("no account was found in the wallet for signer #%d (%s)", i, address.Uint160ToString(s.Account)) + } + signersAccounts = append(signersAccounts, actor.SignerAccount{ + Signer: s, + Account: signerAcc, + }) + } + return signersAccounts, nil +} + +// EnsureNotEmpty returns a function that checks if the flag with the given name +// is not empty. +func EnsureNotEmpty(flagName string) func(*cli.Context, string) error { + return func(ctx *cli.Context, name string) error { + if ctx.String(flagName) == "" { + return cli.Exit(fmt.Errorf("required flag --%s is empty", flagName), 1) + } + return nil + } +} diff --git a/cli/cmdargs/parser_test.go b/cli/cmdargs/parser_test.go new file mode 100644 index 0000000..f3d8521 --- /dev/null +++ b/cli/cmdargs/parser_test.go @@ -0,0 +1,379 @@ +package cmdargs + +import ( + "strings" + "testing" + + "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestParseCosigner(t *testing.T) { + acc := util.Uint160{1, 3, 5, 7} + c1, c2 := random.Uint160(), random.Uint160() + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + testCases := map[string]transaction.Signer{ + acc.StringLE(): { + Account: acc, + Scopes: transaction.CalledByEntry, + }, + "0x" + acc.StringLE(): { + Account: acc, + Scopes: transaction.CalledByEntry, + }, + acc.StringLE() + ":Global": { + Account: acc, + Scopes: transaction.Global, + }, + acc.StringLE() + ":CalledByEntry": { + Account: acc, + Scopes: transaction.CalledByEntry, + }, + acc.StringLE() + ":None": { + Account: acc, + Scopes: transaction.None, + }, + acc.StringLE() + ":CalledByEntry,CustomContracts:" + c1.StringLE() + ":0x" + c2.StringLE(): { + Account: acc, + Scopes: transaction.CalledByEntry | transaction.CustomContracts, + AllowedContracts: []util.Uint160{c1, c2}, + }, + acc.StringLE() + ":CustomGroups:" + priv.PublicKey().StringCompressed(): { + Account: acc, + Scopes: transaction.CustomGroups, + AllowedGroups: keys.PublicKeys{priv.PublicKey()}, + }, + } + for s, expected := range testCases { + actual, err := parseCosigner(s) + require.NoError(t, err) + require.Equal(t, expected, actual, s) + } + errorCases := []string{ + acc.StringLE() + "0", + acc.StringLE() + ":Unknown", + acc.StringLE() + ":Global,CustomContracts", + acc.StringLE() + ":Global,None", + acc.StringLE() + ":CustomContracts:" + acc.StringLE() + ",Global", + acc.StringLE() + ":CustomContracts", + acc.StringLE() + ":CustomContracts:xxx", + acc.StringLE() + ":CustomGroups", + acc.StringLE() + ":CustomGroups:xxx", + } + for _, s := range errorCases { + _, err := parseCosigner(s) + require.Error(t, err, s) + } +} + +func TestParseParams_CalledFromItself(t *testing.T) { + testCases := map[string]struct { + WordsRead int + Value []smartcontract.Parameter + }{ + "]": { + WordsRead: 1, + Value: []smartcontract.Parameter{}, + }, + "[ [ ] ] ]": { + WordsRead: 5, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{}, + }, + }, + }, + }, + }, + "a b c ]": { + WordsRead: 4, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.StringType, + Value: "b", + }, + { + Type: smartcontract.StringType, + Value: "c", + }, + }, + }, + "a [ b [ [ c d ] e ] ] f ] extra items": { + WordsRead: 13, // the method should return right after the last bracket, as calledFromMain == false + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "b", + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "c", + }, + { + Type: smartcontract.StringType, + Value: "d", + }, + }, + }, + { + Type: smartcontract.StringType, + Value: "e", + }, + }, + }, + }, + }, + { + Type: smartcontract.StringType, + Value: "f", + }, + }, + }, + } + + for str, expected := range testCases { + input := strings.Split(str, " ") + offset, actual, err := ParseParams(input, false) + require.NoError(t, err) + require.Equal(t, expected.WordsRead, offset) + require.Equal(t, expected.Value, actual) + } + + errorCases := []string{ + "[ ]", + "[ a b [ c ] d ]", + "[ ] --", + "--", + "not-int:integer ]", + } + + for _, str := range errorCases { + input := strings.Split(str, " ") + _, _, err := ParseParams(input, false) + require.Error(t, err) + } +} + +func TestParseParams_CalledFromOutside(t *testing.T) { + testCases := map[string]struct { + WordsRead int + Parameters []smartcontract.Parameter + }{ + "-- cosigner1": { + WordsRead: 1, // the `--` only + Parameters: []smartcontract.Parameter{}, + }, + "a b c": { + WordsRead: 3, + Parameters: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.StringType, + Value: "b", + }, + { + Type: smartcontract.StringType, + Value: "c", + }, + }, + }, + "a b c -- cosigner1": { + WordsRead: 4, + Parameters: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.StringType, + Value: "b", + }, + { + Type: smartcontract.StringType, + Value: "c", + }, + }, + }, + "a [ b [ [ c d ] e ] ] f": { + WordsRead: 12, + Parameters: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "b", + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "c", + }, + { + Type: smartcontract.StringType, + Value: "d", + }, + }, + }, + { + Type: smartcontract.StringType, + Value: "e", + }, + }, + }, + }, + }, + { + Type: smartcontract.StringType, + Value: "f", + }, + }, + }, + "a [ b ] -- cosigner1 cosigner2": { + WordsRead: 5, + Parameters: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "b", + }, + }, + }, + }, + }, + "a [ b ]": { + WordsRead: 4, + Parameters: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "b", + }, + }, + }, + }, + }, + "a [ b ] [ [ c ] ] [ [ [ d ] ] ]": { + WordsRead: 16, + Parameters: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "a", + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "b", + }, + }, + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "c", + }, + }, + }, + }, + }, + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + Value: []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: "d", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + for str, expected := range testCases { + input := strings.Split(str, " ") + offset, arr, err := ParseParams(input, true) + require.NoError(t, err) + require.Equal(t, expected.WordsRead, offset) + require.Equal(t, expected.Parameters, arr) + } + + errorCases := []string{ + "[", + "]", + "[ [ ]", + "[ [ ] --", + "[ -- ]", + } + for _, str := range errorCases { + input := strings.Split(str, " ") + _, _, err := ParseParams(input, true) + require.Error(t, err) + } +} diff --git a/cli/flags/address.go b/cli/flags/address.go new file mode 100644 index 0000000..dc62cf0 --- /dev/null +++ b/cli/flags/address.go @@ -0,0 +1,146 @@ +package flags + +import ( + "flag" + "fmt" + "strings" + + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/urfave/cli/v2" +) + +// Address is a wrapper for a Uint160 with flag.Value methods. +type Address struct { + IsSet bool + Value util.Uint160 +} + +// AddressFlag is a flag with type Uint160. +type AddressFlag struct { + Name string + Usage string + Value Address + Aliases []string + Required bool + Hidden bool + Action func(*cli.Context, string) error +} + +var ( + _ flag.Value = (*Address)(nil) + _ cli.Flag = AddressFlag{} +) + +// String implements the fmt.Stringer interface. +func (a Address) String() string { + return address.Uint160ToString(a.Value) +} + +// Set implements the flag.Value interface. +func (a *Address) Set(s string) error { + addr, err := ParseAddress(s) + if err != nil { + return cli.Exit(err, 1) + } + a.IsSet = true + a.Value = addr + return nil +} + +// Uint160 casts an address to Uint160. +func (a *Address) Uint160() (u util.Uint160) { + if !a.IsSet { + // It is a programmer error to call this method without + // checking if the value was provided. + panic("address was not set") + } + return a.Value +} + +// IsSet checks if flag was set to a non-default value. +func (f AddressFlag) IsSet() bool { + return f.Value.IsSet +} + +// String returns a readable representation of this value +// (for usage defaults). +func (f AddressFlag) String() string { + var names []string + for _, name := range f.Names() { + names = append(names, getNameHelp(name)) + } + + return strings.Join(names, ", ") + "\t" + f.Usage +} + +func getNameHelp(name string) string { + if len(name) == 1 { + return fmt.Sprintf("-%s value", name) + } + return fmt.Sprintf("--%s value", name) +} + +// Names returns the names of the flag. +func (f AddressFlag) Names() []string { + return cli.FlagNames(f.Name, f.Aliases) +} + +// IsRequired returns whether the flag is required. +func (f AddressFlag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false. +func (f AddressFlag) IsVisible() bool { + return !f.Hidden +} + +// TakesValue returns true of the flag takes a value, otherwise false. +func (f AddressFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag. +func (f AddressFlag) GetUsage() string { + return f.Usage +} + +// Apply populates the flag given the flag set and environment. +// Ignores errors. +func (f AddressFlag) Apply(set *flag.FlagSet) error { + for _, name := range f.Names() { + set.Var(&f.Value, name, f.Usage) + } + return nil +} + +// RunAction executes flag action if set. +func (f AddressFlag) RunAction(c *cli.Context) error { + if f.Action != nil { + return f.Action(c, address.Uint160ToString(f.Value.Value)) + } + return nil +} + +// GetValue returns the flags value as string representation. +func (f AddressFlag) GetValue() string { + return address.Uint160ToString(f.Value.Value) +} + +// Get returns the flag’s value in the given Context. +func (f AddressFlag) Get(ctx *cli.Context) Address { + adr := ctx.Generic(f.Name).(*Address) + return *adr +} + +// ParseAddress parses a Uint160 from either an LE string or an address. +func ParseAddress(s string) (util.Uint160, error) { + const uint160size = 2 * util.Uint160Size + switch len(s) { + case uint160size, uint160size + 2: + return util.Uint160DecodeStringLE(strings.TrimPrefix(s, "0x")) + default: + return address.StringToUint160(s) + } +} diff --git a/cli/flags/address_test.go b/cli/flags/address_test.go new file mode 100644 index 0000000..349c74d --- /dev/null +++ b/cli/flags/address_test.go @@ -0,0 +1,211 @@ +package flags + +import ( + "flag" + "io" + "testing" + + "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" +) + +func TestParseAddress(t *testing.T) { + expected := random.Uint160() + t.Run("simple LE", func(t *testing.T) { + u, err := ParseAddress(expected.StringLE()) + require.NoError(t, err) + require.Equal(t, expected, u) + }) + t.Run("with prefix", func(t *testing.T) { + u, err := ParseAddress("0x" + expected.StringLE()) + require.NoError(t, err) + require.Equal(t, expected, u) + + t.Run("bad", func(t *testing.T) { + _, err := ParseAddress("0s" + expected.StringLE()) + require.Error(t, err) + }) + }) + t.Run("address", func(t *testing.T) { + addr := address.Uint160ToString(expected) + u, err := ParseAddress(addr) + require.NoError(t, err) + require.Equal(t, expected, u) + + t.Run("bad", func(t *testing.T) { + _, err := ParseAddress(addr[1:]) + require.Error(t, err) + }) + }) +} + +func TestAddress_String(t *testing.T) { + value := util.Uint160{1, 2, 3} + addr := Address{ + IsSet: true, + Value: value, + } + + require.Equal(t, address.Uint160ToString(value), addr.String()) +} + +func TestAddress_Set(t *testing.T) { + value := util.Uint160{1, 2, 3} + addr := Address{} + + t.Run("bad address", func(t *testing.T) { + require.Error(t, addr.Set("not an address")) + }) + + t.Run("positive", func(t *testing.T) { + require.NoError(t, addr.Set(address.Uint160ToString(value))) + require.Equal(t, true, addr.IsSet) + require.Equal(t, value, addr.Value) + }) +} + +func TestAddress_Uint160(t *testing.T) { + value := util.Uint160{4, 5, 6} + addr := Address{} + + t.Run("not set", func(t *testing.T) { + require.Panics(t, func() { addr.Uint160() }) + }) + + t.Run("success", func(t *testing.T) { + addr.IsSet = true + addr.Value = value + require.Equal(t, value, addr.Uint160()) + }) +} + +func TestAddressFlag_IsSet(t *testing.T) { + flag := AddressFlag{} + + t.Run("not set", func(t *testing.T) { + require.False(t, flag.IsSet()) + }) + + t.Run("set", func(t *testing.T) { + flag.Value.IsSet = true + require.True(t, flag.IsSet()) + }) +} + +func TestAddressFlag_String(t *testing.T) { + flag := AddressFlag{ + Name: "myFlag", + Usage: "Address to pass", + Value: Address{}, + } + + require.Equal(t, "--myFlag value\tAddress to pass", flag.String()) +} + +func TestAddress_getNameHelp(t *testing.T) { + require.Equal(t, "-f value", getNameHelp("f")) + require.Equal(t, "--flag value", getNameHelp("flag")) +} + +func TestAddressFlag_Names(t *testing.T) { + flag := AddressFlag{ + Name: "flag", + Aliases: []string{"my"}, + } + + require.Equal(t, []string{"flag", "my"}, flag.Names()) +} + +func TestAddress(t *testing.T) { + f := flag.NewFlagSet("", flag.ContinueOnError) + f.SetOutput(io.Discard) // don't pollute test output + addr := AddressFlag{Name: "addr", Aliases: []string{"a"}} + err := addr.Apply(f) + require.NoError(t, err) + require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"})) + require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String()) + require.NoError(t, f.Parse([]string{"-a", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"})) + require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String()) + require.Error(t, f.Parse([]string{"--addr", "kek"})) +} + +func TestAddressFlag_IsRequired(t *testing.T) { + flag := AddressFlag{Required: true} + require.True(t, flag.IsRequired()) + + flag.Required = false + require.False(t, flag.IsRequired()) +} + +func TestAddressFlag_IsVisible(t *testing.T) { + flag := AddressFlag{Hidden: false} + require.True(t, flag.IsVisible()) + + flag.Hidden = true + require.False(t, flag.IsVisible()) +} + +func TestAddressFlag_TakesValue(t *testing.T) { + flag := AddressFlag{} + require.True(t, flag.TakesValue()) +} + +func TestAddressFlag_GetUsage(t *testing.T) { + flag := AddressFlag{Usage: "Specify the address"} + require.Equal(t, "Specify the address", flag.GetUsage()) +} + +func TestAddressFlag_GetValue(t *testing.T) { + addrValue := util.Uint160{1, 2, 3} + flag := AddressFlag{Value: Address{IsSet: true, Value: addrValue}} + expectedStr := address.Uint160ToString(addrValue) + require.Equal(t, expectedStr, flag.GetValue()) +} + +func TestAddressFlag_Get(t *testing.T) { + app := cli.NewApp() + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := cli.NewContext(app, set, nil) + + flag := AddressFlag{ + Name: "testAddress", + Value: Address{Value: util.Uint160{1, 2, 3}, IsSet: false}, + } + + set.Var(&flag.Value, "testAddress", "test usage") + require.NoError(t, set.Set("testAddress", address.Uint160ToString(util.Uint160{3, 2, 1}))) + + expected := flag.Get(ctx) + require.True(t, expected.IsSet) + require.Equal(t, util.Uint160{3, 2, 1}, expected.Value) +} + +func TestAddressFlag_RunAction(t *testing.T) { + called := false + action := func(ctx *cli.Context, s string) error { + called = true + require.Equal(t, address.Uint160ToString(util.Uint160{1, 2, 3}), s) + return nil + } + + app := cli.NewApp() + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := cli.NewContext(app, set, nil) + + flag := AddressFlag{ + Action: action, + Value: Address{IsSet: true, Value: util.Uint160{4, 5, 6}}, + } + + expected := address.Uint160ToString(util.Uint160{1, 2, 3}) + set.Var(&flag.Value, "testAddress", "test usage") + require.NoError(t, set.Set("testAddress", expected)) + require.Equal(t, expected, flag.GetValue()) + + err := flag.RunAction(ctx) + require.NoError(t, err) + require.True(t, called) +} diff --git a/cli/flags/fixed8.go b/cli/flags/fixed8.go new file mode 100644 index 0000000..7b7fd38 --- /dev/null +++ b/cli/flags/fixed8.go @@ -0,0 +1,123 @@ +package flags + +import ( + "flag" + "strings" + + "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "github.com/urfave/cli/v2" +) + +// Fixed8 is a wrapper for a Uint160 with flag.Value methods. +type Fixed8 struct { + Value fixedn.Fixed8 +} + +// Fixed8Flag is a flag with type string. +type Fixed8Flag struct { + Name string + Usage string + Value Fixed8 + Aliases []string + Required bool + Hidden bool + Action func(*cli.Context, string) error +} + +var ( + _ flag.Value = (*Fixed8)(nil) + _ cli.Flag = Fixed8Flag{} +) + +// String implements the fmt.Stringer interface. +func (a Fixed8) String() string { + return a.Value.String() +} + +// Set implements the flag.Value interface. +func (a *Fixed8) Set(s string) error { + f, err := fixedn.Fixed8FromString(s) + if err != nil { + return cli.Exit(err, 1) + } + a.Value = f + return nil +} + +// Fixed8 casts the address to util.Fixed8. +func (a *Fixed8) Fixed8() fixedn.Fixed8 { + return a.Value +} + +// IsSet checks if flag was set to a non-default value. +func (f Fixed8Flag) IsSet() bool { + return f.Value.Value != 0 +} + +// String returns a readable representation of this value +// (for usage defaults). +func (f Fixed8Flag) String() string { + var names []string + for _, name := range f.Names() { + names = append(names, getNameHelp(name)) + } + return strings.Join(names, ", ") + "\t" + f.Usage +} + +// Names returns the names of the flag. +func (f Fixed8Flag) Names() []string { + return cli.FlagNames(f.Name, f.Aliases) +} + +// IsRequired returns whether the flag is required. +func (f Fixed8Flag) IsRequired() bool { + return f.Required +} + +// IsVisible returns true if the flag is not hidden, otherwise false. +func (f Fixed8Flag) IsVisible() bool { + return !f.Hidden +} + +// TakesValue returns true if the flag takes a value, otherwise false. +func (f Fixed8Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag. +func (f Fixed8Flag) GetUsage() string { + return f.Usage +} + +// Apply populates the flag given the flag set and environment. +// Ignores errors. +func (f Fixed8Flag) Apply(set *flag.FlagSet) error { + for _, name := range f.Names() { + set.Var(&f.Value, name, f.Usage) + } + return nil +} + +// Fixed8FromContext returns a parsed util.Fixed8 value provided flag name. +func Fixed8FromContext(ctx *cli.Context, name string) fixedn.Fixed8 { + return ctx.Generic(name).(*Fixed8).Value +} + +// RunAction executes flag action if set. +func (f Fixed8Flag) RunAction(c *cli.Context) error { + if f.Action != nil { + return f.Action(c, f.Value.Value.String()) + } + return nil +} + +// GetValue returns the flags value as string representation. +func (f Fixed8Flag) GetValue() string { + return f.Value.Value.String() +} + +// Get returns the flag’s value in the given Context. +func (f Fixed8Flag) Get(ctx *cli.Context) Fixed8 { + adr := ctx.Generic(f.Name).(*Fixed8) + return *adr +} diff --git a/cli/flags/fixed8_test.go b/cli/flags/fixed8_test.go new file mode 100644 index 0000000..d42a82b --- /dev/null +++ b/cli/flags/fixed8_test.go @@ -0,0 +1,128 @@ +package flags + +import ( + "flag" + "io" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" +) + +func TestFixed8_String(t *testing.T) { + value := fixedn.Fixed8(123) + f := Fixed8{ + Value: value, + } + + require.Equal(t, "0.00000123", f.String()) +} + +func TestFixed8_Set(t *testing.T) { + value := fixedn.Fixed8(123) + f := Fixed8{} + + require.Error(t, f.Set("not-a-fixed8")) + + require.NoError(t, f.Set("0.00000123")) + require.Equal(t, value, f.Value) +} + +func TestFixed8_Fixed8(t *testing.T) { + f := Fixed8{ + Value: fixedn.Fixed8(123), + } + + require.Equal(t, fixedn.Fixed8(123), f.Fixed8()) +} + +func TestFixed8Flag_String(t *testing.T) { + flag := Fixed8Flag{ + Name: "myFlag", + Usage: "Gas amount", + } + + require.Equal(t, "--myFlag value\tGas amount", flag.String()) +} + +func TestFixed8Flag_Names(t *testing.T) { + flag := Fixed8Flag{ + Name: "myFlag", + } + + require.Equal(t, []string{"myFlag"}, flag.Names()) +} + +func TestFixed8(t *testing.T) { + f := flag.NewFlagSet("", flag.ContinueOnError) + f.SetOutput(io.Discard) // don't pollute test output + gas := Fixed8Flag{Name: "gas", Aliases: []string{"g"}, Usage: "Gas amount", Value: Fixed8{Value: 0}, Required: true, Hidden: false, Action: nil} + err := gas.Apply(f) + require.NoError(t, err) + require.NoError(t, f.Parse([]string{"--gas", "0.123"})) + require.Equal(t, "0.123", f.Lookup("g").Value.String()) + require.NoError(t, f.Parse([]string{"-g", "0.456"})) + require.Equal(t, "0.456", f.Lookup("g").Value.String()) + require.Error(t, f.Parse([]string{"--gas", "kek"})) +} + +func TestFixed8Flag_Get(t *testing.T) { + app := cli.NewApp() + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := cli.NewContext(app, set, nil) + flag := Fixed8Flag{ + Name: "testFlag", + } + fixedFlag := Fixed8{Value: fixedn.Fixed8(123)} + set.Var(&fixedFlag, "testFlag", "test usage") + require.NoError(t, set.Set("testFlag", "0.00000321")) + expected := flag.Get(ctx) + require.Equal(t, fixedn.Fixed8(321), expected.Value) +} + +func TestFixed8Flag_GetValue(t *testing.T) { + f := Fixed8Flag{Value: Fixed8{Value: fixedn.Fixed8(123)}} + require.Equal(t, "0.00000123", f.GetValue()) + require.True(t, f.TakesValue()) +} + +func TestFixed8Flag_RunAction(t *testing.T) { + called := false + action := func(ctx *cli.Context, s string) error { + called = true + require.Equal(t, "0.00000123", s) + return nil + } + app := cli.NewApp() + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := cli.NewContext(app, set, nil) + f := Fixed8Flag{ + Action: action, + Value: Fixed8{Value: fixedn.Fixed8(123)}, + } + err := f.RunAction(ctx) + require.NoError(t, err) + require.True(t, called) +} + +func TestFixed8Flag_GetUsage(t *testing.T) { + f := Fixed8Flag{Usage: "Use this flag to specify gas amount"} + require.Equal(t, "Use this flag to specify gas amount", f.GetUsage()) +} + +func TestFixed8Flag_IsVisible(t *testing.T) { + f := Fixed8Flag{Hidden: false} + require.True(t, f.IsVisible()) + + f.Hidden = true + require.False(t, f.IsVisible()) +} + +func TestFixed8Flag_IsRequired(t *testing.T) { + f := Fixed8Flag{Required: false} + require.False(t, f.IsRequired()) + + f.Required = true + require.True(t, f.IsRequired()) +} diff --git a/cli/input/input.go b/cli/input/input.go new file mode 100644 index 0000000..43c6364 --- /dev/null +++ b/cli/input/input.go @@ -0,0 +1,71 @@ +package input + +import ( + "errors" + "fmt" + "io" + "os" + "syscall" + + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "golang.org/x/term" +) + +// Terminal is a terminal used for input. If `nil`, stdin is used. +var Terminal *term.Terminal + +// ReadWriter combiner reader and writer. +type ReadWriter struct { + io.Reader + io.Writer +} + +// ReadLine reads a line from the input without trailing '\n'. +func ReadLine(prompt string) (string, error) { + trm := Terminal + if trm == nil { + s, err := term.MakeRaw(int(syscall.Stdin)) + if err != nil { + return "", err + } + defer func() { _ = term.Restore(int(syscall.Stdin), s) }() + trm = term.NewTerminal(ReadWriter{ + Reader: os.Stdin, + Writer: os.Stdout, + }, "") + } + return readLine(trm, prompt) +} + +func readLine(trm *term.Terminal, prompt string) (string, error) { + _, err := trm.Write([]byte(prompt)) + if err != nil { + return "", err + } + return trm.ReadLine() +} + +// ReadPassword reads the user's password with prompt. +func ReadPassword(prompt string) (string, error) { + trm := Terminal + if trm != nil { + return trm.ReadPassword(prompt) + } + return readSecurePassword(prompt) +} + +// ConfirmTx asks for a confirmation to send the tx. +func ConfirmTx(w io.Writer, tx *transaction.Transaction) error { + fmt.Fprintf(w, "Network fee: %s\n", fixedn.Fixed8(tx.NetworkFee)) + fmt.Fprintf(w, "System fee: %s\n", fixedn.Fixed8(tx.SystemFee)) + fmt.Fprintf(w, "Total fee: %s\n", fixedn.Fixed8(tx.NetworkFee+tx.SystemFee)) + ln, err := ReadLine("Relay transaction (y|N)> ") + if err != nil { + return err + } + if 0 < len(ln) && (ln[0] == 'y' || ln[0] == 'Y') { + return nil + } + return errors.New("cancelled") +} diff --git a/cli/input/readpass_unix.go b/cli/input/readpass_unix.go new file mode 100644 index 0000000..c6f5f0d --- /dev/null +++ b/cli/input/readpass_unix.go @@ -0,0 +1,29 @@ +//go:build !windows + +package input + +import ( + "fmt" + "os" + + "golang.org/x/term" +) + +// readSecurePassword reads the user's password with prompt directly from /dev/tty. +func readSecurePassword(prompt string) (string, error) { + f, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) + if err != nil { + return "", err + } + defer f.Close() + _, err = f.WriteString(prompt) + if err != nil { + return "", err + } + pass, err := term.ReadPassword(int(f.Fd())) + if err != nil { + return "", fmt.Errorf("failed to read password: %w", err) + } + _, err = f.WriteString("\n") + return string(pass), err +} diff --git a/cli/input/readpass_windows.go b/cli/input/readpass_windows.go new file mode 100644 index 0000000..1d3ed20 --- /dev/null +++ b/cli/input/readpass_windows.go @@ -0,0 +1,21 @@ +//go:build windows + +package input + +import ( + "os" + "syscall" + + "golang.org/x/term" +) + +// readSecurePassword reads the user's password with prompt. +func readSecurePassword(prompt string) (string, error) { + s, err := term.MakeRaw(int(syscall.Stdin)) + if err != nil { + return "", err + } + defer func() { _ = term.Restore(int(syscall.Stdin), s) }() + trm := term.NewTerminal(ReadWriter{os.Stdin, os.Stdout}, prompt) + return trm.ReadPassword(prompt) +} diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 0000000..bc07e14 --- /dev/null +++ b/cli/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "os" + + "github.com/nspcc-dev/neo-go/cli/app" +) + +func main() { + ctl := app.New() + + if err := ctl.Run(os.Args); err != nil { + fmt.Fprintln(ctl.ErrWriter, err) + os.Exit(1) + } +} diff --git a/cli/nep_test/nep11_test.go b/cli/nep_test/nep11_test.go new file mode 100644 index 0000000..8f65583 --- /dev/null +++ b/cli/nep_test/nep11_test.go @@ -0,0 +1,682 @@ +package nep_test + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/big" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/internal/versionutil" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +const ( + // nftOwnerAddr is the owner of NFT-ND HASHY token (../examples/nft-nd/nft.go). + nftOwnerAddr = "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB" + nftOwnerWallet = "../../examples/my_wallet.json" + nftOwnerPass = "qwerty" + + // Keep contract NEFs consistent between runs. + _ = versionutil.TestVersion +) + +func TestNEP11Import(t *testing.T) { + e := testcli.NewExecutor(t, true) + + tmpDir := t.TempDir() + walletPath := filepath.Join(tmpDir, "walletForImport.json") + + // deploy NFT NeoNameService contract + nnsContractHash := deployNNSContract(t, e) + // deploy NFT-D NeoFS Object contract + nfsContractHash := deployNFSContract(t, e) + neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo) + require.NoError(t, err) + e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath) + + args := []string{ + "neo-go", "wallet", "nep11", "import", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", walletPath, + } + // missing token hash + e.RunWithErrorCheck(t, `Required flag "token" not set`, args...) + + // excessive parameters + e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE(), "something")...) + + // empty token hash + e.RunWithErrorCheck(t, `invalid value "" for flag -token: zero length string`, append(args, "--token", "")...) + + // good: non-divisible + e.Run(t, append(args, "--token", nnsContractHash.StringLE())...) + + // good: divisible + e.Run(t, append(args, "--token", nfsContractHash.StringLE())...) + + // already exists + e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE())...) + + // not a NEP-11 token + e.RunWithError(t, append(args, "--token", neoContractHash.StringLE())...) + + checkInfo := func(t *testing.T, h util.Uint160, name string, symbol string, decimals int) { + e.CheckNextLine(t, "^Name:\\s*"+name) + e.CheckNextLine(t, "^Symbol:\\s*"+symbol) + e.CheckNextLine(t, "^Hash:\\s*"+h.StringLE()) + e.CheckNextLine(t, "^Decimals:\\s*"+strconv.Itoa(decimals)) + e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(h)) + e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName)) + } + t.Run("Info", func(t *testing.T) { + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, "neo-go", "wallet", "nep11", "info", + "--wallet", walletPath, "--token", nnsContractHash.StringLE(), "qwerty") + }) + t.Run("WithToken", func(t *testing.T) { + e.Run(t, "neo-go", "wallet", "nep11", "info", + "--wallet", walletPath, "--token", nnsContractHash.StringLE()) + checkInfo(t, nnsContractHash, "NameService", "NNS", 0) + }) + t.Run("NoToken", func(t *testing.T) { + e.Run(t, "neo-go", "wallet", "nep11", "info", + "--wallet", walletPath) + checkInfo(t, nnsContractHash, "NameService", "NNS", 0) + e.CheckNextLine(t, "") + checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2) + }) + }) + + t.Run("Remove", func(t *testing.T) { + e.RunWithError(t, "neo-go", "wallet", "nep11", "remove", + "--wallet", walletPath, "--token", nnsContractHash.StringLE(), "parameter") + e.In.WriteString("y\r") + e.Run(t, "neo-go", "wallet", "nep11", "remove", + "--wallet", walletPath, "--token", nnsContractHash.StringLE()) + e.Run(t, "neo-go", "wallet", "nep11", "info", + "--wallet", walletPath) + checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2) + _, err := e.Out.ReadString('\n') + require.Equal(t, err, io.EOF) + }) +} + +func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + + // copy wallet to temp dir in order not to overwrite the original file + bytesRead, err := os.ReadFile(nftOwnerWallet) + require.NoError(t, err) + wall := filepath.Join(tmpDir, "my_wallet.json") + err = os.WriteFile(wall, bytesRead, 0755) + require.NoError(t, err) + + // transfer funds to contract owner + e.In.WriteString("one\r") + e.Run(t, "neo-go", "wallet", "nep17", "transfer", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--to", nftOwnerAddr, + "--token", "GAS", + "--amount", "10000", + "--force", + "--from", testcli.ValidatorAddr) + e.CheckTxPersisted(t) + + // deploy NFT HASHY contract + h := deployNFTContract(t, e) + + mint := func(t *testing.T) []byte { + // mint 1 HASHY token by transferring 10 GAS to HASHY contract + e.In.WriteString(nftOwnerPass + "\r") + e.Run(t, "neo-go", "wallet", "nep17", "transfer", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", wall, + "--to", h.StringLE(), + "--token", "GAS", + "--amount", "10", + "--force", + "--from", nftOwnerAddr) + txMint, _ := e.CheckTxPersisted(t) + + // get NFT ID from AER + aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application) + require.NoError(t, err) + require.Equal(t, 1, len(aer)) + require.Equal(t, 2, len(aer[0].Events)) + hashyMintEvent := aer[0].Events[1] + require.Equal(t, "Transfer", hashyMintEvent.Name) + tokenID, err := hashyMintEvent.Item.Value().([]stackitem.Item)[3].TryBytes() + require.NoError(t, err) + require.NotNil(t, tokenID) + return tokenID + } + + tokenID := mint(t) + var hashBeforeTransfer = e.Chain.CurrentHeaderHash() + + // check the balance + cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", wall, + "--address", nftOwnerAddr} + checkBalanceResult := func(t *testing.T, acc string, ids ...[]byte) { + e.CheckNextLine(t, "^\\s*Account\\s+"+acc) + e.CheckNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)") + + // Hashes can be ordered in any way, so make a regexp for them. + var tokstring strings.Builder + tokstring.WriteString("(") + for i, id := range ids { + if i > 0 { + tokstring.WriteString("|") + } + tokstring.WriteString(hex.EncodeToString(id)) + } + tokstring.WriteString(")") + + for range ids { + e.CheckNextLine(t, "^\\s*Token: "+tokstring.String()+"\\s*$") + e.CheckNextLine(t, "^\\s*Amount: 1\\s*$") + e.CheckNextLine(t, "^\\s*Updated: [0-9]+\\s*$") + } + e.CheckEOF(t) + } + // balance check: by symbol, token is not imported + e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...) + checkBalanceResult(t, nftOwnerAddr, tokenID) + // balance check: excessive parameters + e.RunWithError(t, append(cmdCheckBalance, "--token", h.StringLE(), "neo-go")...) + // balance check: by hash, ok + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, nftOwnerAddr, tokenID) + + // import token + e.Run(t, "neo-go", "wallet", "nep11", "import", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", wall, + "--token", h.StringLE()) + + // balance check: by symbol, ok + e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...) + checkBalanceResult(t, nftOwnerAddr, tokenID) + + // balance check: all accounts + e.Run(t, "neo-go", "wallet", "nep11", "balance", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", wall, + "--token", h.StringLE()) + checkBalanceResult(t, nftOwnerAddr, tokenID) + + // remove token from wallet + e.In.WriteString("y\r") + e.Run(t, "neo-go", "wallet", "nep11", "remove", + "--wallet", wall, "--token", h.StringLE()) + + // ownerOf: missing contract hash + cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdOwnerOf...) + cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE()) + + // ownerOf: missing token ID + e.RunWithError(t, cmdOwnerOf...) + cmdOwnerOf = append(cmdOwnerOf, "--id", hex.EncodeToString(tokenID)) + + // ownerOf: good + e.Run(t, cmdOwnerOf...) + e.CheckNextLine(t, nftOwnerAddr) + + // tokensOf: missing contract hash + cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flags "token, address" not set`, cmdTokensOf...) + cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE()) + + // tokensOf: missing owner address + e.RunWithErrorCheck(t, `Required flag "address" not set`, cmdTokensOf...) + cmdTokensOf = append(cmdTokensOf, "--address", nftOwnerAddr) + + // tokensOf: good + e.Run(t, cmdTokensOf...) + require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t)) + + // properties: no contract + cmdProperties := []string{ + "neo-go", "wallet", "nep11", "properties", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdProperties...) + cmdProperties = append(cmdProperties, "--token", h.StringLE()) + + // properties: no token ID + e.RunWithError(t, cmdProperties...) + cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(tokenID)) + + // properties: ok + e.Run(t, cmdProperties...) + require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.GetNextLine(t)) + + // tokensOf: good, several tokens + tokenID1 := mint(t) + e.Run(t, cmdTokensOf...) + fst, snd := tokenID, tokenID1 + if bytes.Compare(tokenID, tokenID1) == 1 { + fst, snd = snd, fst + } + + require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) + require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) + + // tokens: missing contract hash + cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdTokens...) + cmdTokens = append(cmdTokens, "--token", h.StringLE()) + + // tokens: excessive parameters + e.RunWithError(t, append(cmdTokens, "additional")...) + // tokens: good, several tokens + e.Run(t, cmdTokens...) + require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) + require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) + + // balance check: several tokens, ok + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, nftOwnerAddr, tokenID, tokenID1) + + cmdTransfer := []string{ + "neo-go", "wallet", "nep11", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", wall, + "--to", testcli.ValidatorAddr, + "--from", nftOwnerAddr, + "--force", + } + + // transfer: unimported token with symbol id specified + e.In.WriteString(nftOwnerPass + "\r") + e.RunWithError(t, append(cmdTransfer, + "--token", "HASHY")...) + cmdTransfer = append(cmdTransfer, "--token", h.StringLE()) + + // transfer: no id specified + e.In.WriteString(nftOwnerPass + "\r") + e.RunWithError(t, cmdTransfer...) + + // transfer: good + e.In.WriteString(nftOwnerPass + "\r") + e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(tokenID))...) + e.CheckTxPersisted(t) + + // check balance after transfer + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, nftOwnerAddr, tokenID1) + + // check --await flag + tokenID2 := mint(t) + e.In.WriteString(nftOwnerPass + "\r") + e.Run(t, append(cmdTransfer, "--await", "--id", hex.EncodeToString(tokenID2))...) + e.CheckAwaitableTxPersisted(t) + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, nftOwnerAddr, tokenID1) + + // transfer: good, to NEP-11-Payable contract, with data + verifyH := deployVerifyContract(t, e) + cmdTransfer = []string{ + "neo-go", "wallet", "nep11", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", wall, + "--to", verifyH.StringLE(), + "--from", nftOwnerAddr, + "--token", h.StringLE(), + "--id", hex.EncodeToString(tokenID1), + "--force", + "string:some_data", + } + e.In.WriteString(nftOwnerPass + "\r") + e.Run(t, cmdTransfer...) + tx, _ := e.CheckTxPersisted(t) + // check OnNEP11Payment event + aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application) + require.NoError(t, err) + require.Equal(t, 2, len(aer[0].Events)) + nftOwnerHash, err := address.StringToUint160(nftOwnerAddr) + require.NoError(t, err) + require.Equal(t, state.NotificationEvent{ + ScriptHash: verifyH, + Name: "OnNEP11Payment", + Item: stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(nftOwnerHash.BytesBE()), + stackitem.NewBigInteger(big.NewInt(1)), + stackitem.NewByteArray(tokenID1), + stackitem.NewByteArray([]byte("some_data")), + }), + }, aer[0].Events[1]) + + // check balance after transfer + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, nftOwnerAddr) + + // historic calls still remember the good old days. + cmdOwnerOf = append(cmdOwnerOf, "--historic", hashBeforeTransfer.StringLE()) + e.Run(t, cmdOwnerOf...) + e.CheckNextLine(t, nftOwnerAddr) + + cmdTokensOf = append(cmdTokensOf, "--historic", hashBeforeTransfer.StringLE()) + e.Run(t, cmdTokensOf...) + require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t)) + + cmdTokens = append(cmdTokens, "--historic", hashBeforeTransfer.StringLE()) + e.Run(t, cmdTokens...) + require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t)) + + // this one is not affected by transfer, but anyway + cmdProperties = append(cmdProperties, "--historic", hashBeforeTransfer.StringLE()) + e.Run(t, cmdProperties...) + require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.GetNextLine(t)) +} + +func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + + // copy wallet to temp dir in order not to overwrite the original file + bytesRead, err := os.ReadFile(testcli.ValidatorWallet) + require.NoError(t, err) + wall := filepath.Join(tmpDir, "my_wallet.json") + err = os.WriteFile(wall, bytesRead, 0755) + require.NoError(t, err) + + // deploy NeoFS Object contract + h := deployNFSContract(t, e) + + mint := func(t *testing.T, containerID, objectID util.Uint256) []byte { + // mint 1.00 NFSO token by transferring 10 GAS to NFSO contract + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "wallet", "nep17", "transfer", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", wall, + "--to", h.StringLE(), + "--token", "GAS", + "--amount", "10", + "--force", + "--from", testcli.ValidatorAddr, + "--", "[", "hash256:"+containerID.StringLE(), "hash256:"+objectID.StringLE(), "]", + ) + txMint, _ := e.CheckTxPersisted(t) + + // get NFT ID from AER + aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application) + require.NoError(t, err) + require.Equal(t, 1, len(aer)) + require.Equal(t, 2, len(aer[0].Events)) + nfsoMintEvent := aer[0].Events[1] + require.Equal(t, "Transfer", nfsoMintEvent.Name) + tokenID, err := nfsoMintEvent.Item.Value().([]stackitem.Item)[3].TryBytes() + require.NoError(t, err) + require.NotNil(t, tokenID) + return tokenID + } + + container1ID := util.Uint256{1, 2, 3} + object1ID := util.Uint256{4, 5, 6} + token1ID := mint(t, container1ID, object1ID) + + container2ID := util.Uint256{7, 8, 9} + object2ID := util.Uint256{10, 11, 12} + token2ID := mint(t, container2ID, object2ID) + + // check properties + e.Run(t, "neo-go", "wallet", "nep11", "properties", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--token", h.StringLE(), + "--id", hex.EncodeToString(token1ID)) + jProps := e.GetNextLine(t) + props := make(map[string]string) + require.NoError(t, json.Unmarshal([]byte(jProps), &props)) + require.Equal(t, base64.StdEncoding.EncodeToString(container1ID.BytesBE()), props["containerID"]) + require.Equal(t, base64.StdEncoding.EncodeToString(object1ID.BytesBE()), props["objectID"]) + e.CheckEOF(t) + + type idAmount struct { + id string + amount string + } + + // check the balance + cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", wall, + "--address", testcli.ValidatorAddr} + checkBalanceResult := func(t *testing.T, acc string, objs ...idAmount) { + e.CheckNextLine(t, "^\\s*Account\\s+"+acc) + e.CheckNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)") + + for _, o := range objs { + e.CheckNextLine(t, "^\\s*Token: "+o.id+"\\s*$") + e.CheckNextLine(t, "^\\s*Amount: "+o.amount+"\\s*$") + e.CheckNextLine(t, "^\\s*Updated: [0-9]+\\s*$") + } + e.CheckEOF(t) + } + tokz := []idAmount{ + {hex.EncodeToString(token1ID), "1"}, + {hex.EncodeToString(token2ID), "1"}, + } + // balance check: by symbol, token is not imported + e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...) + checkBalanceResult(t, testcli.ValidatorAddr, tokz...) + + // overall NFSO balance check: by hash, ok + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, testcli.ValidatorAddr, tokz...) + + // particular NFSO balance check: by hash, ok + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE(), "--id", hex.EncodeToString(token2ID))...) + checkBalanceResult(t, testcli.ValidatorAddr, tokz[1]) + + // import token + e.Run(t, "neo-go", "wallet", "nep11", "import", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", wall, + "--token", h.StringLE()) + + // overall balance check: by symbol, ok + e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...) + checkBalanceResult(t, testcli.ValidatorAddr, tokz...) + + // particular balance check: by symbol, ok + e.Run(t, append(cmdCheckBalance, "--token", "NFSO", "--id", hex.EncodeToString(token1ID))...) + checkBalanceResult(t, testcli.ValidatorAddr, tokz[0]) + + // remove token from wallet + e.In.WriteString("y\r") + e.Run(t, "neo-go", "wallet", "nep11", "remove", + "--wallet", wall, "--token", h.StringLE()) + + // ownerOfD: missing contract hash + cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdOwnerOf...) + cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE()) + + // ownerOfD: missing token ID + e.RunWithError(t, cmdOwnerOf...) + cmdOwnerOf = append(cmdOwnerOf, "--id", hex.EncodeToString(token1ID)) + + // ownerOfD: good + e.Run(t, cmdOwnerOf...) + e.CheckNextLine(t, testcli.ValidatorAddr) + + // tokensOf: missing contract hash + cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flags "token, address" not set`, cmdTokensOf...) + cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE()) + + // tokensOf: missing owner address + e.RunWithErrorCheck(t, `Required flag "address" not set`, cmdTokensOf...) + cmdTokensOf = append(cmdTokensOf, "--address", testcli.ValidatorAddr) + + // tokensOf: good + e.Run(t, cmdTokensOf...) + require.Equal(t, hex.EncodeToString(token1ID), e.GetNextLine(t)) + require.Equal(t, hex.EncodeToString(token2ID), e.GetNextLine(t)) + e.CheckEOF(t) + + // properties: no contract + cmdProperties := []string{ + "neo-go", "wallet", "nep11", "properties", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdProperties...) + cmdProperties = append(cmdProperties, "--token", h.StringLE()) + + // properties: no token ID + e.RunWithError(t, cmdProperties...) + cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(token2ID)) + + // properties: additional parameter + e.RunWithError(t, append(cmdProperties, "additiona")...) + + // properties: ok + e.Run(t, cmdProperties...) + jProps = e.GetNextLine(t) + props = make(map[string]string) + require.NoError(t, json.Unmarshal([]byte(jProps), &props)) + require.Equal(t, base64.StdEncoding.EncodeToString(container2ID.BytesBE()), props["containerID"]) + require.Equal(t, base64.StdEncoding.EncodeToString(object2ID.BytesBE()), props["objectID"]) + e.CheckEOF(t) + + // tokensOf: good, several tokens + e.Run(t, cmdTokensOf...) + fst, snd := token1ID, token2ID + if bytes.Compare(token1ID, token2ID) == 1 { + fst, snd = snd, fst + } + + require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) + require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) + + // tokens: missing contract hash + cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + } + e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdTokens...) + cmdTokens = append(cmdTokens, "--token", h.StringLE()) + + // tokens: good, several tokens + e.Run(t, cmdTokens...) + require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t)) + require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t)) + + // balance check: several tokens, ok + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, testcli.ValidatorAddr, tokz...) + + cmdTransfer := []string{ + "neo-go", "wallet", "nep11", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", wall, + "--to", nftOwnerAddr, + "--from", testcli.ValidatorAddr, + "--force", + } + + // transfer: unimported token with symbol id specified + e.In.WriteString(testcli.ValidatorPass + "\r") + e.RunWithError(t, append(cmdTransfer, + "--token", "NFSO")...) + cmdTransfer = append(cmdTransfer, "--token", h.StringLE()) + + // transfer: no id specified + e.In.WriteString(testcli.ValidatorPass + "\r") + e.RunWithError(t, cmdTransfer...) + + // transfer: good + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(token1ID))...) + e.CheckTxPersisted(t) + + // check balance after transfer + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + checkBalanceResult(t, testcli.ValidatorAddr, tokz[1]) // only token2ID expected to be on the balance + + // transfer: good, 1/4 of the balance, to NEP-11-Payable contract, with data + verifyH := deployVerifyContract(t, e) + cmdTransfer = []string{ + "neo-go", "wallet", "nep11", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", wall, + "--to", verifyH.StringLE(), + "--from", testcli.ValidatorAddr, + "--token", h.StringLE(), + "--id", hex.EncodeToString(token2ID), + "--amount", "0.25", + "--force", + "string:some_data", + } + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, cmdTransfer...) + tx, _ := e.CheckTxPersisted(t) + // check OnNEP11Payment event + aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application) + require.NoError(t, err) + require.Equal(t, 2, len(aer[0].Events)) + validatorHash, err := address.StringToUint160(testcli.ValidatorAddr) + require.NoError(t, err) + require.Equal(t, state.NotificationEvent{ + ScriptHash: verifyH, + Name: "OnNEP11Payment", + Item: stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(validatorHash.BytesBE()), + stackitem.NewBigInteger(big.NewInt(25)), + stackitem.NewByteArray(token2ID), + stackitem.NewByteArray([]byte("some_data")), + }), + }, aer[0].Events[1]) + + // check balance after transfer + e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...) + tokz[1].amount = "0.75" + checkBalanceResult(t, testcli.ValidatorAddr, tokz[1]) +} + +func deployNFSContract(t *testing.T, e *testcli.Executor) util.Uint160 { + return testcli.DeployContract(t, e, "../../examples/nft-d/nft.go", "../../examples/nft-d/nft.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) +} + +func deployNFTContract(t *testing.T, e *testcli.Executor) util.Uint160 { + return testcli.DeployContract(t, e, "../../examples/nft-nd/nft.go", "../../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass) +} + +func deployNNSContract(t *testing.T, e *testcli.Executor) util.Uint160 { + return testcli.DeployContract(t, e, "../../examples/nft-nd-nns/", "../../examples/nft-nd-nns/nns.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) +} + +func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 { + return testcli.DeployContract(t, e, "../smartcontract/testdata/verify.go", "../smartcontract/testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) +} diff --git a/cli/nep_test/nep17_test.go b/cli/nep_test/nep17_test.go new file mode 100644 index 0000000..a9340b3 --- /dev/null +++ b/cli/nep_test/nep17_test.go @@ -0,0 +1,435 @@ +package nep_test + +import ( + "io" + "math/big" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + "testing" + + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/stretchr/testify/require" +) + +func TestNEP17Balance(t *testing.T) { + e := testcli.NewExecutor(t, true) + + args := []string{ + "neo-go", "wallet", "nep17", "multitransfer", "--force", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--from", testcli.ValidatorAddr, + "GAS:" + testcli.TestWalletMultiAccount1 + ":1", + "NEO:" + testcli.TestWalletMultiAccount1 + ":10", + "GAS:" + testcli.TestWalletMultiAccount3 + ":3", + } + e.In.WriteString("one\r") + e.Run(t, args...) + e.CheckTxPersisted(t) + + var checkAcc1NEO = func(t *testing.T, e *testcli.Executor, line string) { + if line == "" { + line = e.GetNextLine(t) + } + balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash) + e.CheckLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)") + e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$") + e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10)) + } + var checkAcc1GAS = func(t *testing.T, e *testcli.Executor, line string) { + if line == "" { + line = e.GetNextLine(t) + } + e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)") + balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash) + e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$") + e.CheckNextLine(t, "^\\s*Updated:") + } + var checkAcc1Assets = func(t *testing.T, e *testcli.Executor) { + e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1) + // The order of assets is undefined. + for range 2 { + line := e.GetNextLine(t) + if strings.Contains(line, "GAS") { + checkAcc1GAS(t, e, line) + } else { + checkAcc1NEO(t, e, line) + } + } + } + + var ( + cmdbase = []string{"neo-go", "wallet", "nep17", "balance", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]} + addrparams = []string{"--address", testcli.TestWalletMultiAccount1} + walletparams = []string{"--wallet", testcli.TestWalletMultiPath} + ) + t.Run("Bad wallet", func(t *testing.T) { + e.RunWithError(t, append(cmdbase, "--wallet", "/dev/null")...) + }) + t.Run("empty wallet", func(t *testing.T) { + tmpDir := t.TempDir() + walletPath := filepath.Join(tmpDir, "emptywallet.json") + require.NoError(t, os.WriteFile(walletPath, []byte("{}"), 0o644)) + e.RunWithError(t, append(cmdbase, "--wallet", walletPath)...) + }) + t.Run("no wallet or address", func(t *testing.T) { + e.RunWithError(t, cmdbase...) + }) + for name, params := range map[string][]string{ + "address only": addrparams, + "address with wallet": slices.Concat(walletparams, addrparams), + } { + var cmd = append(cmdbase, params...) + t.Run(name, func(t *testing.T) { + t.Run("all tokens", func(t *testing.T) { + e.Run(t, cmd...) + checkAcc1Assets(t, e) + e.CheckEOF(t) + }) + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...) + }) + }) + t.Run("NEO", func(t *testing.T) { + checkResult := func(t *testing.T) { + e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1) + checkAcc1NEO(t, e, "") + e.CheckEOF(t) + } + t.Run("Alias", func(t *testing.T) { + e.Run(t, append(cmd, "--token", "NEO")...) + checkResult(t) + }) + t.Run("Hash", func(t *testing.T) { + e.Run(t, append(cmd, "--token", e.Chain.GoverningTokenHash().StringLE())...) + checkResult(t) + }) + }) + t.Run("GAS", func(t *testing.T) { + e.Run(t, append(cmd, "--token", "GAS")...) + e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1) + checkAcc1GAS(t, e, "") + }) + t.Run("Bad token", func(t *testing.T) { + e.Run(t, append(cmd, "--token", "kek")...) + e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1) + e.CheckNextLine(t, `^\s*Can't find data for "kek" token\s*`) + e.CheckEOF(t) + }) + } + t.Run("inexistent wallet account", func(t *testing.T) { + var cmd = append(cmdbase, walletparams...) + e.RunWithError(t, append(cmd, "--address", "NSPCCpw8YmgNDYWiBfXJHRfz38NDjv6WW3")...) + }) + t.Run("zero balance of known token", func(t *testing.T) { + e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...) + e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2) + e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)") + e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(0).String()+"$") + e.CheckNextLine(t, "^\\s*Updated:") + e.CheckEOF(t) + }) + t.Run("all accounts", func(t *testing.T) { + e.Run(t, append(cmdbase, walletparams...)...) + + checkAcc1Assets(t, e) + e.CheckNextLine(t, "^\\s*$") + + e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2) + e.CheckNextLine(t, "^\\s*$") + + e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount3) + e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)") + balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount3Hash) + e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$") + e.CheckNextLine(t, "^\\s*Updated:") + e.CheckEOF(t) + }) +} + +func TestNEP17Transfer(t *testing.T) { + w, err := wallet.NewWalletFromFile("../testdata/testwallet.json") + require.NoError(t, err) + + e := testcli.NewExecutor(t, true) + args := []string{ + "neo-go", "wallet", "nep17", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--to", w.Accounts[0].Address, + "--token", "NEO", + "--amount", "1", + "--from", testcli.ValidatorAddr, + } + + t.Run("missing receiver", func(t *testing.T) { + as := slices.Concat(args[:8], args[10:]) + e.In.WriteString("one\r") + e.RunWithErrorCheck(t, `Required flag "to" not set`, as...) + e.In.Reset() + }) + + t.Run("InvalidPassword", func(t *testing.T) { + e.In.WriteString("onetwothree\r") + e.RunWithError(t, args...) + e.In.Reset() + }) + + t.Run("no confirmation", func(t *testing.T) { + e.In.WriteString("one\r") + e.RunWithError(t, args...) + e.In.Reset() + }) + t.Run("cancel after prompt", func(t *testing.T) { + e.In.WriteString("one\r") + e.RunWithError(t, args...) + e.In.Reset() + }) + + e.In.WriteString("one\r") + e.In.WriteString("Y\r") + e.Run(t, args...) + e.CheckNextLine(t, `^Network fee:\s*(\d|\.)+`) + e.CheckNextLine(t, `^System fee:\s*(\d|\.)+`) + e.CheckNextLine(t, `^Total fee:\s*(\d|\.)+`) + e.CheckTxPersisted(t) + + sh := w.Accounts[0].ScriptHash() + b, _ := e.Chain.GetGoverningTokenBalance(sh) + require.Equal(t, big.NewInt(1), b) + + t.Run("with force", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(args, "--force")...) + e.CheckTxPersisted(t) + + b, _ := e.Chain.GetGoverningTokenBalance(sh) + require.Equal(t, big.NewInt(2), b) + }) + + hVerify := deployVerifyContract(t, e) + const validatorDefault = "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn" + + t.Run("default address", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, "neo-go", "wallet", "nep17", "multitransfer", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--from", testcli.ValidatorAddr, + "--force", + "NEO:"+validatorDefault+":42", + "GAS:"+validatorDefault+":7") + e.CheckTxPersisted(t) + + args := args[:len(args)-2] // cut '--from' argument + args = append(args, "--force") + e.In.WriteString("one\r") + e.Run(t, args...) + e.CheckTxPersisted(t) + + b, _ := e.Chain.GetGoverningTokenBalance(sh) + require.Equal(t, big.NewInt(3), b) + + sh, err = address.StringToUint160(validatorDefault) + require.NoError(t, err) + b, _ = e.Chain.GetGoverningTokenBalance(sh) + require.Equal(t, big.NewInt(41), b) + }) + + t.Run("with signers", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, "neo-go", "wallet", "nep17", "multitransfer", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--from", testcli.ValidatorAddr, + "--force", + "NEO:"+validatorDefault+":42", + "GAS:"+validatorDefault+":7", + "--", testcli.ValidatorAddr+":Global") + e.CheckTxPersisted(t) + }) + + validTil := e.Chain.BlockHeight() + 100 + cmd := []string{ + "neo-go", "wallet", "nep17", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--token", "GAS", + "--amount", "1", + "--force", + "--from", testcli.ValidatorAddr} + + t.Run("with await", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, "--to", nftOwnerAddr, "--await")...) + e.CheckAwaitableTxPersisted(t) + }) + + cmd = append(cmd, "--to", address.Uint160ToString(nativehashes.Notary), + "[", testcli.ValidatorAddr, strconv.Itoa(int(validTil)), "]") + + t.Run("with data", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, cmd...) + e.CheckTxPersisted(t) + }) + + t.Run("with data and signers", func(t *testing.T) { + t.Run("invalid sender's scope", func(t *testing.T) { + e.In.WriteString("one\r") + e.RunWithError(t, append(cmd, "--", testcli.ValidatorAddr+":None")...) + }) + t.Run("good", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, "--", testcli.ValidatorAddr+":Global")...) // CalledByEntry is enough, but it's the default value, so check something else + e.CheckTxPersisted(t) + }) + t.Run("several signers", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, "--", testcli.ValidatorAddr, hVerify.StringLE())...) + e.CheckTxPersisted(t) + }) + }) +} + +func TestNEP17MultiTransfer(t *testing.T) { + privs, _ := testcli.GenerateKeys(t, 3) + + e := testcli.NewExecutor(t, true) + neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo) + require.NoError(t, err) + args := []string{ + "neo-go", "wallet", "nep17", "multitransfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--from", testcli.ValidatorAddr, + "--force", + "NEO:" + privs[0].Address() + ":42", + "GAS:" + privs[1].Address() + ":7", + neoContractHash.StringLE() + ":" + privs[2].Address() + ":13", + } + hVerify := deployVerifyContract(t, e) + + t.Run("no cosigners", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, args...) + e.CheckTxPersisted(t) + + b, _ := e.Chain.GetGoverningTokenBalance(privs[0].GetScriptHash()) + require.Equal(t, big.NewInt(42), b) + b = e.Chain.GetUtilityTokenBalance(privs[1].GetScriptHash()) + require.Equal(t, big.NewInt(int64(fixedn.Fixed8FromInt64(7))), b) + b, _ = e.Chain.GetGoverningTokenBalance(privs[2].GetScriptHash()) + require.Equal(t, big.NewInt(13), b) + }) + + t.Run("invalid sender scope", func(t *testing.T) { + e.In.WriteString("one\r") + e.RunWithError(t, append(args, + "--", testcli.ValidatorAddr+":None")...) // invalid sender scope + }) + t.Run("Global sender scope", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(args, + "--", testcli.ValidatorAddr+":Global")...) + e.CheckTxPersisted(t) + }) + t.Run("Several cosigners", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(args, + "--", testcli.ValidatorAddr, hVerify.StringLE())...) + e.CheckTxPersisted(t) + }) +} + +func TestNEP17ImportToken(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + walletPath := filepath.Join(tmpDir, "walletForImport.json") + + neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo) + require.NoError(t, err) + gasContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Gas) + require.NoError(t, err) + nnsContractHash := deployNNSContract(t, e) + e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath) + + // missing token hash + e.RunWithErrorCheck(t, `Required flag "token" not set`, "neo-go", "wallet", "nep17", "import", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", walletPath) + + // additional parameter + e.RunWithError(t, "neo-go", "wallet", "nep17", "import", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", walletPath, + "--token", gasContractHash.StringLE(), "useless") + e.Run(t, "neo-go", "wallet", "nep17", "import", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", walletPath, + "--token", gasContractHash.StringLE()) + e.Run(t, "neo-go", "wallet", "nep17", "import", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", walletPath, + "--token", address.Uint160ToString(neoContractHash)) // try address instead of sh + + // not a NEP-17 token + e.RunWithError(t, "neo-go", "wallet", "nep17", "import", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", walletPath, + "--token", nnsContractHash.StringLE()) + + t.Run("Info", func(t *testing.T) { + checkGASInfo := func(t *testing.T) { + e.CheckNextLine(t, "^Name:\\s*GasToken") + e.CheckNextLine(t, "^Symbol:\\s*GAS") + e.CheckNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE()) + e.CheckNextLine(t, "^Decimals:\\s*8") + e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash)) + e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName)) + } + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, "neo-go", "wallet", "nep17", "info", + "--wallet", walletPath, "--token", gasContractHash.StringLE(), "parameter") + }) + t.Run("WithToken", func(t *testing.T) { + e.Run(t, "neo-go", "wallet", "nep17", "info", + "--wallet", walletPath, "--token", gasContractHash.StringLE()) + checkGASInfo(t) + }) + t.Run("NoToken", func(t *testing.T) { + e.Run(t, "neo-go", "wallet", "nep17", "info", + "--wallet", walletPath) + checkGASInfo(t) + _, err := e.Out.ReadString('\n') + require.NoError(t, err) + e.CheckNextLine(t, "^Name:\\s*NeoToken") + e.CheckNextLine(t, "^Symbol:\\s*NEO") + e.CheckNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE()) + e.CheckNextLine(t, "^Decimals:\\s*0") + e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash)) + e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName)) + }) + t.Run("Remove", func(t *testing.T) { + e.RunWithError(t, "neo-go", "wallet", "nep17", "remove", + "--wallet", walletPath, "--token", neoContractHash.StringLE(), "add") + e.In.WriteString("y\r") + e.Run(t, "neo-go", "wallet", "nep17", "remove", + "--wallet", walletPath, "--token", neoContractHash.StringLE()) + e.Run(t, "neo-go", "wallet", "nep17", "info", + "--wallet", walletPath) + checkGASInfo(t) + _, err := e.Out.ReadString('\n') + require.Equal(t, err, io.EOF) + }) + }) +} diff --git a/cli/options/cli_options_test.go b/cli/options/cli_options_test.go new file mode 100644 index 0000000..4cf9977 --- /dev/null +++ b/cli/options/cli_options_test.go @@ -0,0 +1,33 @@ +package options_test + +import ( + "flag" + "testing" + + "github.com/nspcc-dev/neo-go/cli/app" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" +) + +func TestGetRPCClient(t *testing.T) { + e := testcli.NewExecutor(t, true) + + t.Run("no endpoint", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + ctx := cli.NewContext(app.New(), set, nil) + gctx, _ := options.GetTimeoutContext(ctx) + _, ec := options.GetRPCClient(gctx, ctx) + require.Equal(t, 1, ec.ExitCode()) + }) + + t.Run("success", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String(options.RPCEndpointFlag, "http://"+e.RPC.Addresses()[0], "") + ctx := cli.NewContext(app.New(), set, nil) + gctx, _ := options.GetTimeoutContext(ctx) + _, ec := options.GetRPCClient(gctx, ctx) + require.Nil(t, ec) + }) +} diff --git a/cli/options/filtering_core.go b/cli/options/filtering_core.go new file mode 100644 index 0000000..4dd0ad2 --- /dev/null +++ b/cli/options/filtering_core.go @@ -0,0 +1,28 @@ +package options + +import "go.uber.org/zap/zapcore" + +// FilteringCore is custom implementation of zapcore.Core that allows to filter +// log entries using custom filtering function. +type FilteringCore struct { + zapcore.Core + filter FilterFunc +} + +// FilterFunc is the filter function that is called to check whether the given +// entry together with the associated fields is to be written to a core or not. +type FilterFunc func(zapcore.Entry) bool + +// NewFilteringCore returns a core middleware that uses the given filter function +// to decide whether to log this message or not. +func NewFilteringCore(next zapcore.Core, filter FilterFunc) zapcore.Core { + return &FilteringCore{next, filter} +} + +// Check implements zapcore.Core interface and performs log entries filtering. +func (c *FilteringCore) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if c.filter(e) { + return c.Core.Check(e, ce) + } + return ce +} diff --git a/cli/options/options.go b/cli/options/options.go new file mode 100644 index 0000000..7a084a5 --- /dev/null +++ b/cli/options/options.go @@ -0,0 +1,486 @@ +/* +Package options contains a set of common CLI options and helper functions to use them. +*/ +package options + +import ( + "context" + "errors" + "fmt" + "net/url" + "os" + "runtime" + "slices" + "strconv" + "strings" + "time" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/input" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/nspcc-dev/neofs-sdk-go/pool" + "github.com/nspcc-dev/neofs-sdk-go/user" + "github.com/urfave/cli/v2" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "golang.org/x/term" + "gopkg.in/yaml.v3" +) + +const ( + // DefaultTimeout is the default timeout used for RPC requests. + DefaultTimeout = 10 * time.Second + // DefaultAwaitableTimeout is the default timeout used for RPC requests that + // require transaction awaiting. It is set to the approximate time of three + // Neo N3 mainnet blocks accepting. + DefaultAwaitableTimeout = 3 * 15 * time.Second +) + +const ( + // RPCEndpointFlag is a long flag name for an RPC endpoint. It can be used to + // check for flag presence in the context. + RPCEndpointFlag = "rpc-endpoint" + // NeoFSRPCEndpointFlag is a long flag name for a NeoFS RPC endpoint. + NeoFSRPCEndpointFlag = "fs-rpc-endpoint" +) + +// Wallet is a set of flags used for wallet operations. +var Wallet = []cli.Flag{ + &cli.StringFlag{ + Name: "wallet", + Aliases: []string{"w"}, + Usage: "Wallet to use to get the key for transaction signing; conflicts with --wallet-config flag", + }, + &cli.StringFlag{ + Name: "wallet-config", + Usage: "Path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag", + }, +} + +// Network is a set of flags for choosing the network to operate on +// (privnet/mainnet/testnet). +var Network = []cli.Flag{ + &cli.BoolFlag{ + Name: "privnet", + Aliases: []string{"p"}, + Usage: "Use private network configuration (if --config-file option is not specified)", + }, + &cli.BoolFlag{ + Name: "mainnet", + Aliases: []string{"m"}, + Usage: "Use mainnet network configuration (if --config-file option is not specified)", + }, + &cli.BoolFlag{ + Name: "testnet", + Aliases: []string{"t"}, + Usage: "Use testnet network configuration (if --config-file option is not specified)", + }, + &cli.BoolFlag{ + Name: "unittest", + Hidden: true, + }, +} + +// RPC is a set of flags used for RPC connections (endpoint and timeout). +var RPC = []cli.Flag{ + &cli.StringFlag{ + Name: RPCEndpointFlag, + Aliases: []string{"r"}, + Usage: "RPC node address", + Required: true, + Action: cmdargs.EnsureNotEmpty("rpc-endpoint"), + }, + &cli.DurationFlag{ + Name: "timeout", + Aliases: []string{"s"}, + Value: DefaultTimeout, + Usage: "Timeout for the operation", + }, +} + +// NeoFSRPC is a set of flags used for NeoFS RPC connections (endpoint). +var NeoFSRPC = []cli.Flag{&cli.StringSliceFlag{ + Name: NeoFSRPCEndpointFlag, + Aliases: []string{"fsr"}, + Usage: "List of NeoFS storage node RPC addresses (comma-separated or multiple --fs-rpc-endpoint flags)", + Required: true, + Action: func(ctx *cli.Context, fsRpcEndpoints []string) error { + if slices.Contains(fsRpcEndpoints, "") { + return cli.Exit("NeoFS RPC endpoint cannot contain empty values", 1) + } + return nil + }, +}} + +// Historic is a flag for commands that can perform historic invocations. +var Historic = &cli.StringFlag{ + Name: "historic", + Usage: "Use historic state (height, block hash or state root hash)", +} + +// Config is a flag for commands that use node configuration. +var Config = &cli.StringFlag{ + Name: "config-path", + Usage: "Path to directory with per-network configuration files (may be overridden by --config-file option for the configuration file)", +} + +// ConfigFile is a flag for commands that use node configuration and provide +// path to the specific config file instead of config path. +var ConfigFile = &cli.StringFlag{ + Name: "config-file", + Usage: "Path to the node configuration file (overrides --config-path option)", +} + +// RelativePath is a flag for commands that use node configuration and provide +// a prefix to all relative paths in config files. +var RelativePath = &cli.StringFlag{ + Name: "relative-path", + Usage: "Prefix to all relative paths in the node configuration file", +} + +// Debug is a flag for commands that allow node in debug mode usage. +var Debug = &cli.BoolFlag{ + Name: "debug", + Aliases: []string{"d"}, + Usage: "Enable debug logging (LOTS of output, overrides configuration)", +} + +// ForceTimestampLogs is a flag for commands that run the node. This flag +// enables timestamp logging for every log record even if program is running +// not in terminal. +var ForceTimestampLogs = &cli.BoolFlag{ + Name: "force-timestamp-logs", + Usage: "Enable timestamps for log entries", +} + +var errInvalidHistoric = errors.New("invalid 'historic' parameter, neither a block number, nor a block/state hash") +var ErrNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet' or '-w' flag or specify wallet config file with the '--wallet-config' flag") +var errConflictingWalletFlags = errors.New("--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location") + +// GetNetwork examines Context's flags and returns the appropriate network. It +// defaults to PrivNet if no flags are given. +func GetNetwork(ctx *cli.Context) netmode.Magic { + var net = netmode.PrivNet + if ctx.Bool("testnet") { + net = netmode.TestNet + } + if ctx.Bool("mainnet") { + net = netmode.MainNet + } + if ctx.Bool("unittest") { + net = netmode.UnitTestNet + } + return net +} + +// GetTimeoutContext returns a context.Context with the default or a user-set timeout. +func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) { + dur := ctx.Duration("timeout") + if dur == 0 { + dur = DefaultTimeout + } + if !ctx.IsSet("timeout") && ctx.Bool("await") { + dur = DefaultAwaitableTimeout + } + return context.WithTimeout(context.Background(), dur) +} + +// GetRPCClient returns an RPC client instance for the given Context. +func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cli.ExitCoder) { + endpoint := ctx.String(RPCEndpointFlag) + c, err := rpcclient.New(gctx, endpoint, rpcclient.Options{}) + if err != nil { + return nil, cli.Exit(err, 1) + } + err = c.Init() + if err != nil { + return nil, cli.Exit(err, 1) + } + return c, nil +} + +// GetNeoFSClientPool returns a NeoFS pool and a signer for the given Context. +func GetNeoFSClientPool(ctx *cli.Context, acc *wallet.Account) (user.Signer, *pool.Pool, error) { + rpcNeoFS := ctx.StringSlice(NeoFSRPCEndpointFlag) + signer := user.NewAutoIDSignerRFC6979(acc.PrivateKey().PrivateKey) + + params := pool.DefaultOptions() + params.SetHealthcheckTimeout(neofs.DefaultHealthcheckTimeout) + params.SetNodeDialTimeout(neofs.DefaultDialTimeout) + params.SetNodeStreamTimeout(neofs.DefaultStreamTimeout) + p, err := pool.New(pool.NewFlatNodeParams(rpcNeoFS), signer, params) + if err != nil { + return nil, nil, fmt.Errorf("failed to create NeoFS pool: %w", err) + } + if err = p.Dial(context.Background()); err != nil { + return nil, nil, fmt.Errorf("failed to dial NeoFS pool: %w", err) + } + return signer, p, nil +} + +// GetInvoker returns an invoker using the given RPC client, context and signers. +// It parses "--historic" parameter to adjust it. +func GetInvoker(c *rpcclient.Client, ctx *cli.Context, signers []transaction.Signer) (*invoker.Invoker, cli.ExitCoder) { + historic := ctx.String("historic") + if historic == "" { + return invoker.New(c, signers), nil + } + if index, err := strconv.ParseUint(historic, 10, 32); err == nil { + return invoker.NewHistoricAtHeight(uint32(index), c, signers), nil + } + if u256, err := util.Uint256DecodeStringLE(historic); err == nil { + // Might as well be a block hash, but it makes no practical difference. + return invoker.NewHistoricWithState(u256, c, signers), nil + } + return nil, cli.Exit(errInvalidHistoric, 1) +} + +// GetRPCWithInvoker combines GetRPCClient with GetInvoker for cases where it's +// appropriate to do so. +func GetRPCWithInvoker(gctx context.Context, ctx *cli.Context, signers []transaction.Signer) (*rpcclient.Client, *invoker.Invoker, cli.ExitCoder) { + c, err := GetRPCClient(gctx, ctx) + if err != nil { + return nil, nil, err + } + inv, err := GetInvoker(c, ctx, signers) + if err != nil { + c.Close() + return nil, nil, err + } + return c, inv, err +} + +// GetConfigFromContext looks at the path and the mode flags in the given config and +// returns an appropriate config. +func GetConfigFromContext(ctx *cli.Context) (config.Config, error) { + var ( + configFile = ctx.String("config-file") + relativePath = ctx.String("relative-path") + ) + if len(configFile) != 0 { + return config.LoadFile(configFile, relativePath) + } + var configPath = config.DefaultConfigPath + if argCp := ctx.String("config-path"); argCp != "" { + configPath = argCp + } + return config.Load(configPath, GetNetwork(ctx), relativePath) +} + +var ( + // _winfileSinkRegistered denotes whether zap has registered + // user-supplied factory for all sinks with `winfile`-prefixed scheme. + _winfileSinkRegistered bool + _winfileSinkCloser func() error +) + +// HandleLoggingParams reads logging parameters. +// If a user selected debug level -- function enables it. +// If logPath is configured -- function creates a dir and a file for logging. +// If logPath is configured on Windows -- function returns closer to be +// able to close sink for the opened log output file. +// If the program is run in TTY then logger adds timestamp to its entries. +func HandleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration) (*zap.Logger, *zap.AtomicLevel, func() error, error) { + var ( + level = zapcore.InfoLevel + encoding = "console" + err error + ) + if len(cfg.LogLevel) > 0 { + level, err = zapcore.ParseLevel(cfg.LogLevel) + if err != nil { + return nil, nil, nil, fmt.Errorf("log setting: %w", err) + } + } + if len(cfg.LogEncoding) > 0 { + encoding = cfg.LogEncoding + } + if ctx != nil && ctx.Bool("debug") { + level = zapcore.DebugLevel + } + + cc := zap.NewProductionConfig() + cc.DisableCaller = true + cc.DisableStacktrace = true + cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder + cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder + if cfg.LogTimestamp == nil && term.IsTerminal(int(os.Stdout.Fd())) || + cfg.LogTimestamp != nil && *cfg.LogTimestamp || + ctx != nil && ctx.Bool("force-timestamp-logs") { + cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + } else { + cc.EncoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {} + } + cc.Encoding = encoding + cc.Level = zap.NewAtomicLevelAt(level) + cc.Sampling = nil + + if logPath := cfg.LogPath; logPath != "" { + if err := io.MakeDirForFile(logPath, "logger"); err != nil { + return nil, nil, nil, err + } + + if runtime.GOOS == "windows" { + if !_winfileSinkRegistered { + // See https://github.com/uber-go/zap/issues/621. + err := zap.RegisterSink("winfile", func(u *url.URL) (zap.Sink, error) { + if u.User != nil { + return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u) + } + if u.Fragment != "" { + return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u) + } + if u.RawQuery != "" { + return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u) + } + // Error messages are better if we check hostname and port separately. + if u.Port() != "" { + return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u) + } + if hn := u.Hostname(); hn != "" && hn != "localhost" { + return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u) + } + switch u.Path { + case "stdout": + return os.Stdout, nil + case "stderr": + return os.Stderr, nil + } + f, err := os.OpenFile(u.Path[1:], // Remove leading slash left after url.Parse. + os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) + _winfileSinkCloser = func() error { + _winfileSinkCloser = nil + return f.Close() + } + return f, err + }) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to register windows-specific sinc: %w", err) + } + _winfileSinkRegistered = true + } + logPath = "winfile:///" + logPath + } + + cc.OutputPaths = []string{logPath} + } + + log, err := cc.Build() + return log, &cc.Level, _winfileSinkCloser, err +} + +// GetRPCWithActor returns an RPC client instance and Actor instance for the given context. +func GetRPCWithActor(gctx context.Context, ctx *cli.Context, signers []actor.SignerAccount) (*rpcclient.Client, *actor.Actor, cli.ExitCoder) { + c, err := GetRPCClient(gctx, ctx) + if err != nil { + return nil, nil, err + } + + a, actorErr := actor.New(c, signers) + if actorErr != nil { + c.Close() + return nil, nil, cli.Exit(fmt.Errorf("failed to create Actor: %w", actorErr), 1) + } + return c, a, nil +} + +// GetAccFromContext returns account and wallet from context. If address is not set, default address is used. +func GetAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error) { + var addr util.Uint160 + + wPath := ctx.String("wallet") + walletConfigPath := ctx.String("wallet-config") + if len(wPath) != 0 && len(walletConfigPath) != 0 { + return nil, nil, errConflictingWalletFlags + } + if len(wPath) == 0 && len(walletConfigPath) == 0 { + return nil, nil, ErrNoWallet + } + var pass *string + if len(walletConfigPath) != 0 { + cfg, err := ReadWalletConfig(walletConfigPath) + if err != nil { + return nil, nil, err + } + wPath = cfg.Path + pass = &cfg.Password + } + + wall, err := wallet.NewWalletFromFile(wPath) + if err != nil { + return nil, nil, err + } + addrFlag := ctx.Generic("address").(*flags.Address) + if addrFlag.IsSet { + addr = addrFlag.Uint160() + } else { + addr = wall.GetChangeAddress() + if addr.Equals(util.Uint160{}) { + return nil, wall, errors.New("can't get default address") + } + } + + acc, err := GetUnlockedAccount(wall, addr, pass) + return acc, wall, err +} + +// GetUnlockedAccount returns account from wallet, address and uses pass to unlock specified account if given. +// If the password is not given, then it is requested from user. +func GetUnlockedAccount(wall *wallet.Wallet, addr util.Uint160, pass *string) (*wallet.Account, error) { + acc := wall.GetAccount(addr) + if acc == nil { + return nil, fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)) + } + + if acc.CanSign() || acc.EncryptedWIF == "" { + return acc, nil + } + + if pass == nil { + rawPass, err := input.ReadPassword( + fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr))) + if err != nil { + return nil, fmt.Errorf("error reading password: %w", err) + } + trimmed := strings.TrimRight(string(rawPass), "\n") + pass = &trimmed + } + err := acc.Decrypt(*pass, wall.Scrypt) + if err != nil { + return nil, err + } + return acc, nil +} + +// ReadWalletConfig reads wallet config from the given path. +func ReadWalletConfig(configPath string) (*config.Wallet, error) { + file, err := os.Open(configPath) + if err != nil { + return nil, err + } + defer file.Close() + + configData, err := os.ReadFile(configPath) + if err != nil { + return nil, fmt.Errorf("unable to read wallet config: %w", err) + } + + cfg := &config.Wallet{} + + err = yaml.Unmarshal(configData, &cfg) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal wallet config YAML: %w", err) + } + return cfg, nil +} diff --git a/cli/options/options_test.go b/cli/options/options_test.go new file mode 100644 index 0000000..649d9b2 --- /dev/null +++ b/cli/options/options_test.go @@ -0,0 +1,57 @@ +package options + +import ( + "flag" + "testing" + "time" + + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" +) + +func TestGetNetwork(t *testing.T) { + t.Run("privnet", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + ctx := cli.NewContext(cli.NewApp(), set, nil) + require.Equal(t, netmode.PrivNet, GetNetwork(ctx)) + }) + + t.Run("testnet", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.Bool("testnet", true, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + require.Equal(t, netmode.TestNet, GetNetwork(ctx)) + }) + + t.Run("mainnet", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.Bool("mainnet", true, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + require.Equal(t, netmode.MainNet, GetNetwork(ctx)) + }) +} + +func TestGetTimeoutContext(t *testing.T) { + t.Run("default", func(t *testing.T) { + start := time.Now() + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + ctx := cli.NewContext(cli.NewApp(), set, nil) + actualCtx, _ := GetTimeoutContext(ctx) + end := time.Now().Add(DefaultTimeout) + dl, _ := actualCtx.Deadline() + require.True(t, start.Before(dl) && (dl.Before(end) || dl.Equal(end))) + }) + + t.Run("set", func(t *testing.T) { + start := time.Now() + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.Duration("timeout", time.Duration(20), "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + actualCtx, _ := GetTimeoutContext(ctx) + end := time.Now().Add(time.Nanosecond * 20) + dl, _ := actualCtx.Deadline() + require.True(t, start.Before(dl) && (dl.Before(end) || dl.Equal(end))) + }) +} diff --git a/cli/paramcontext/context.go b/cli/paramcontext/context.go new file mode 100644 index 0000000..3d6429c --- /dev/null +++ b/cli/paramcontext/context.go @@ -0,0 +1,50 @@ +package paramcontext + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/context" + "github.com/nspcc-dev/neo-go/pkg/wallet" +) + +// InitAndSave creates an incompletely signed transaction which can be used +// as an input to `multisig sign`. If a wallet.Account is given and can sign, +// it's signed as well using it. +func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Account, filename string) error { + scCtx := context.NewParameterContext(context.TransactionType, net, tx) + if acc != nil && acc.CanSign() { + sign := acc.SignHashable(net, tx) + if err := scCtx.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil { + return fmt.Errorf("can't add signature: %w", err) + } + } + return Save(scCtx, filename) +} + +// Read reads the parameter context from the file. +func Read(filename string) (*context.ParameterContext, error) { + data, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("can't read input file: %w", err) + } + + c := new(context.ParameterContext) + if err := json.Unmarshal(data, c); err != nil { + return nil, fmt.Errorf("can't parse transaction: %w", err) + } + return c, nil +} + +// Save writes the parameter context to the file. +func Save(c *context.ParameterContext, filename string) error { + if data, err := json.Marshal(c); err != nil { + return fmt.Errorf("can't marshal transaction: %w", err) + } else if err := os.WriteFile(filename, data, 0644); err != nil { + return fmt.Errorf("can't write transaction to file: %w", err) + } + return nil +} diff --git a/cli/query/query.go b/cli/query/query.go new file mode 100644 index 0000000..5150bf3 --- /dev/null +++ b/cli/query/query.go @@ -0,0 +1,379 @@ +package query + +import ( + "bytes" + "cmp" + "encoding/base64" + "fmt" + "slices" + "strconv" + "strings" + "text/tabwriter" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" + "github.com/urfave/cli/v2" +) + +// NewCommands returns 'query' command. +func NewCommands() []*cli.Command { + queryTxFlags := append([]cli.Flag{ + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Output full tx info and execution logs", + }, + }, options.RPC...) + queryNotaryPoolFlags := append([]cli.Flag{ + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Output full main tx info", + }, + }, options.RPC...) + return []*cli.Command{{ + Name: "query", + Usage: "Query data from RPC node", + Subcommands: []*cli.Command{ + { + Name: "candidates", + Usage: "Get candidates and votes", + UsageText: "neo-go query candidates -r endpoint [-s timeout]", + Action: queryCandidates, + Flags: options.RPC, + }, + { + Name: "committee", + Usage: "Get committee list", + UsageText: "neo-go query committee -r endpoint [-s timeout]", + Action: queryCommittee, + Flags: options.RPC, + }, + { + Name: "height", + Usage: "Get node height", + UsageText: "neo-go query height -r endpoint [-s timeout]", + Action: queryHeight, + Flags: options.RPC, + }, + { + Name: "notarypool", + Usage: "Query the content of the notary pool", + UsageText: "neo-go query notarypool -r endpoint [-s timeout] [-v]", + Action: queryNotaryPool, + Flags: queryNotaryPoolFlags, + }, + { + Name: "tx", + Usage: "Query transaction status", + UsageText: "neo-go query tx -r endpoint [-s timeout] [-v] ", + Action: queryTx, + Flags: queryTxFlags, + }, + { + Name: "voter", + Usage: "Print NEO holder account state", + UsageText: "neo-go query voter -r endpoint [-s timeout]
", + Action: queryVoter, + Flags: options.RPC, + }, + }, + }} +} + +func queryTx(ctx *cli.Context) error { + args := ctx.Args().Slice() + if len(args) == 0 { + return cli.Exit("transaction hash is missing", 1) + } else if len(args) > 1 { + return cli.Exit("only one transaction hash is accepted", 1) + } + + txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x")) + if err != nil { + return cli.Exit(fmt.Sprintf("invalid tx hash: %s", args[0]), 1) + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) + if err != nil { + return cli.Exit(err, 1) + } + + txOut, err := c.GetRawTransactionVerbose(txHash) + if err != nil { + return cli.Exit(err, 1) + } + + var res *result.ApplicationLog + if !txOut.Blockhash.Equals(util.Uint256{}) { + res, err = c.GetApplicationLog(txHash, nil) + if err != nil { + return cli.Exit(err, 1) + } + } + + err = DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose")) + if err != nil { + return cli.Exit(err, 1) + } + return nil +} + +func DumpApplicationLog( + ctx *cli.Context, + res *result.ApplicationLog, + tx *transaction.Transaction, + txMeta *result.TransactionMetadata, + verbose bool, + fallbacks ...int) error { + var buf []byte + + buf = fmt.Appendf(buf, "Hash:\t%s\n", tx.Hash().StringLE()) + buf = fmt.Appendf(buf, "OnChain:\t%t\n", res != nil) + if res == nil { + buf = fmt.Appendf(buf, "ValidUntil:\t%s\n", strconv.FormatUint(uint64(tx.ValidUntilBlock), 10)) + if len(fallbacks) != 0 { + buf = fmt.Appendf(buf, "Fallbacks:\t%d\n", fallbacks[0]) + } + } else { + if txMeta != nil { + buf = fmt.Appendf(buf, "BlockHash:\t%s\n", txMeta.Blockhash.StringLE()) + } + if len(res.Executions) != 1 { + buf = fmt.Appendf(buf, "Success:\tunknown (no execution data)\n") + } else { + buf = fmt.Appendf(buf, "Success:\t%t\n", res.Executions[0].VMState == vmstate.Halt) + } + } + if verbose { + for _, sig := range tx.Signers { + buf = fmt.Appendf(buf, "Signer:\t%s (%s)\n", address.Uint160ToString(sig.Account), sig.Scopes) + } + buf = fmt.Appendf(buf, "SystemFee:\t%s GAS\n", fixedn.Fixed8(tx.SystemFee).String()) + buf = fmt.Appendf(buf, "NetworkFee:\t%s GAS\n", fixedn.Fixed8(tx.NetworkFee).String()) + buf = fmt.Appendf(buf, "Script:\t%s\n", base64.StdEncoding.EncodeToString(tx.Script)) + if len(fallbacks) == 0 { + v := vm.New() + v.Load(tx.Script) + opts := bytes.NewBuffer(nil) + v.PrintOps(opts) + buf = append(buf, opts.Bytes()...) + if res != nil { + for _, e := range res.Executions { + if e.VMState != vmstate.Halt { + buf = fmt.Appendf(buf, "Exception:\t%s\n", e.FaultException) + } + } + } + } + } + tw := tabwriter.NewWriter(ctx.App.Writer, 0, 4, 4, '\t', 0) + _, err := tw.Write(buf) + if err != nil { + return err + } + return tw.Flush() +} + +func queryCandidates(ctx *cli.Context) error { + var err error + + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) + if err != nil { + return cli.Exit(err, 1) + } + + vals, err := c.GetCandidates() + if err != nil { + return cli.Exit(err, 1) + } + comm, err := c.GetCommittee() + if err != nil { + return cli.Exit(err, 1) + } + + slices.SortFunc(vals, func(a, b result.Candidate) int { + if a.Active && !b.Active { + return 1 + } + if !a.Active && b.Active { + return -1 + } + return cmp.Or( + cmp.Compare(a.Votes, b.Votes), + a.PublicKey.Cmp(&b.PublicKey), + ) + }) + var res []byte + res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n") + for _, val := range vals { + res = fmt.Appendf(res, "%s\t%d\t%t\t%t\n", val.PublicKey.StringCompressed(), val.Votes, comm.Contains(&val.PublicKey), val.Active) + } + tw := tabwriter.NewWriter(ctx.App.Writer, 0, 2, 2, ' ', 0) + _, err = tw.Write(res) + if err != nil { + return err + } + return tw.Flush() +} + +func queryCommittee(ctx *cli.Context) error { + var err error + + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) + if err != nil { + return cli.Exit(err, 1) + } + + comm, err := c.GetCommittee() + if err != nil { + return cli.Exit(err, 1) + } + + for _, k := range comm { + fmt.Fprintln(ctx.App.Writer, k.StringCompressed()) + } + return nil +} + +func queryHeight(ctx *cli.Context) error { + var err error + + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) + if err != nil { + return cli.Exit(err, 1) + } + + blockCount, err := c.GetBlockCount() + if err != nil { + return cli.Exit(err, 1) + } + blockHeight := blockCount - 1 // GetBlockCount returns block count (including 0), not the highest block index. + + fmt.Fprintf(ctx.App.Writer, "Latest block: %d\n", blockHeight) + + stateHeight, err := c.GetStateHeight() + if err == nil { // We can be talking to a node without getstateheight request support. + fmt.Fprintf(ctx.App.Writer, "Validated state: %d\n", stateHeight.Validated) + } + + return nil +} + +func queryVoter(ctx *cli.Context) error { + args := ctx.Args().Slice() + if len(args) == 0 { + return cli.Exit("no address specified", 1) + } else if len(args) > 1 { + return cli.Exit("this command only accepts one address", 1) + } + + addr, err := flags.ParseAddress(args[0]) + if err != nil { + return cli.Exit(fmt.Sprintf("wrong address: %s", args[0]), 1) + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + c, exitErr := options.GetRPCClient(gctx, ctx) + if exitErr != nil { + return exitErr + } + + neoToken := neo.NewReader(invoker.New(c, nil)) + + st, err := neoToken.GetAccountState(addr) + if err != nil { + return cli.Exit(err, 1) + } + if st == nil { + st = new(state.NEOBalance) + } + dec, err := neoToken.Decimals() + if err != nil { + return cli.Exit(fmt.Errorf("failed to get decimals: %w", err), 1) + } + voted := "null" + if st.VoteTo != nil { + voted = fmt.Sprintf("%s (%s)", st.VoteTo.StringCompressed(), address.Uint160ToString(st.VoteTo.GetScriptHash())) + } + fmt.Fprintf(ctx.App.Writer, "\tVoted: %s\n", voted) + fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec))) + fmt.Fprintf(ctx.App.Writer, "\tBlock: %d\n", st.BalanceHeight) + return nil +} + +func queryNotaryPool(ctx *cli.Context) error { + var err error + + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + c, err := options.GetRPCClient(gctx, ctx) + if err != nil { + return cli.Exit(err, 1) + } + + h, err := c.GetBlockCount() + if err != nil { + return cli.Exit(err, 1) + } + fmt.Fprintf(ctx.App.Writer, "Current height: %d\n", h) + + p, err := c.GetRawNotaryPool() + if err != nil { + return cli.Exit(err, 1) + } + for m, fbs := range p.Hashes { + tx, err := c.GetRawNotaryTransaction(m) + if err != nil { + fmt.Fprintf(ctx.App.Writer, "%s (%d fallbacks): %s\n", m.StringLE(), len(fbs), err) + continue + } + err = DumpApplicationLog(ctx, nil, tx, nil, ctx.Bool("verbose"), len(fbs)) + if err != nil { + return cli.Exit(err, 1) + } + } + + return nil +} diff --git a/cli/query/query_test.go b/cli/query/query_test.go new file mode 100644 index 0000000..e508412 --- /dev/null +++ b/cli/query/query_test.go @@ -0,0 +1,267 @@ +package query_test + +import ( + "context" + "encoding/base64" + "fmt" + "math/big" + "regexp" + "strconv" + "strings" + "testing" + "time" + + "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/notary" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/stretchr/testify/require" +) + +func TestQueryTx(t *testing.T) { + e := testcli.NewExecutorSuspended(t) + + w, err := wallet.NewWalletFromFile("../testdata/testwallet.json") + require.NoError(t, err) + + transferArgs := []string{ + "neo-go", "wallet", "nep17", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--to", w.Accounts[0].Address, + "--token", "NEO", + "--from", testcli.ValidatorAddr, + "--force", + } + + e.In.WriteString("one\r") + e.Run(t, append(transferArgs, "--amount", "1")...) + line := e.GetNextLine(t) + txHash, err := util.Uint256DecodeStringLE(line) + require.NoError(t, err) + + tx, ok := e.Chain.GetMemPool().TryGetValue(txHash) + require.True(t, ok) + + args := []string{"neo-go", "query", "tx", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]} + e.Run(t, append(args, txHash.StringLE())...) + e.CheckNextLine(t, `Hash:\s+`+txHash.StringLE()) + e.CheckNextLine(t, `OnChain:\s+false`) + e.CheckNextLine(t, `ValidUntil:\s+`+strconv.FormatUint(uint64(tx.ValidUntilBlock), 10)) + e.CheckEOF(t) + + go e.Chain.Run() + require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50) + + e.Run(t, append(args, txHash.StringLE())...) + e.CheckNextLine(t, `Hash:\s+`+txHash.StringLE()) + e.CheckNextLine(t, `OnChain:\s+true`) + + _, height, err := e.Chain.GetTransaction(txHash) + require.NoError(t, err) + e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE()) + e.CheckNextLine(t, `Success:\s+true`) + e.CheckEOF(t) + + t.Run("verbose", func(t *testing.T) { + e.Run(t, append(args, "--verbose", txHash.StringLE())...) + compareQueryTxVerbose(t, e, tx) + + t.Run("FAULT", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, "neo-go", "contract", "invokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--address", testcli.ValidatorAddr, + "--force", + random.Uint160().StringLE(), + "randomMethod") + + e.CheckNextLine(t, `Warning:`) + e.CheckNextLine(t, "Sending transaction") + line := strings.TrimPrefix(e.GetNextLine(t), "Sent invocation transaction ") + txHash, err := util.Uint256DecodeStringLE(line) + require.NoError(t, err) + + require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50) + + tx, _, err := e.Chain.GetTransaction(txHash) + require.NoError(t, err) + e.Run(t, append(args, "--verbose", txHash.StringLE())...) + compareQueryTxVerbose(t, e, tx) + }) + }) + + t.Run("invalid", func(t *testing.T) { + t.Run("missing tx argument", func(t *testing.T) { + e.RunWithError(t, args...) + }) + t.Run("excessive arguments", func(t *testing.T) { + e.RunWithError(t, append(args, txHash.StringLE(), txHash.StringLE())...) + }) + t.Run("invalid hash", func(t *testing.T) { + e.RunWithError(t, append(args, "notahash")...) + }) + t.Run("good hash, missing tx", func(t *testing.T) { + e.RunWithError(t, append(args, random.Uint256().StringLE())...) + }) + }) +} + +func compareQueryTxVerbose(t *testing.T, e *testcli.Executor, tx *transaction.Transaction, fallbacks ...int) { + if len(fallbacks) > 0 { + e.CheckNextLine(t, `Current height:\s+(\d|\.)+`) + } + e.CheckNextLine(t, `Hash:\s+`+tx.Hash().StringLE()) + e.CheckNextLine(t, `OnChain:\s+`+strconv.FormatBool(len(fallbacks) == 0)) + var ( + res []state.AppExecResult + err error + ) + if len(fallbacks) == 0 { + _, height, err := e.Chain.GetTransaction(tx.Hash()) + require.NoError(t, err) + e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE()) + res, _ = e.Chain.GetAppExecResults(tx.Hash(), trigger.Application) + e.CheckNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].VMState == vmstate.Halt)) + } else { + e.CheckNextLine(t, `ValidUntil:\s+`+strconv.FormatUint(uint64(tx.ValidUntilBlock), 10)) + e.CheckNextLine(t, `Fallbacks:\s+`+strconv.Itoa(len(fallbacks))) + } + + for _, s := range tx.Signers { + e.CheckNextLine(t, fmt.Sprintf(`Signer:\s+%s\s*\(%s\)`, address.Uint160ToString(s.Account), s.Scopes.String())) + } + e.CheckNextLine(t, `SystemFee:\s+`+fixedn.Fixed8(tx.SystemFee).String()+" GAS$") + e.CheckNextLine(t, `NetworkFee:\s+`+fixedn.Fixed8(tx.NetworkFee).String()+" GAS$") + e.CheckNextLine(t, `Script:\s+`+regexp.QuoteMeta(base64.StdEncoding.EncodeToString(tx.Script))) + if len(fallbacks) == 0 { + c := vm.NewContext(tx.Script) + n := 0 + for ; c.NextIP() < c.LenInstr(); _, _, err = c.Next() { + require.NoError(t, err) + n++ + } + e.CheckScriptDump(t, n) + + if res[0].VMState != vmstate.Halt { + e.CheckNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].FaultException)) + } + } + + e.CheckEOF(t) +} + +func TestQueryHeight(t *testing.T) { + e := testcli.NewExecutor(t, true) + + args := []string{"neo-go", "query", "height", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]} + e.Run(t, args...) + e.CheckNextLine(t, `^Latest block: [0-9]+$`) + e.CheckNextLine(t, `^Validated state: [0-9]+$`) + e.CheckEOF(t) + t.Run("excessive arguments", func(t *testing.T) { + e.RunWithError(t, append(args, "something")...) + }) +} + +func TestQueryNotaryPool(t *testing.T) { + e := testcli.NewExecutorWithConfig(t, true, true, func(cfg *config.Config) { + cfg.ProtocolConfiguration.Hardforks = map[string]uint32{ + config.HFFaun.String(): 0, + } + }) + endpoint := "http://" + e.RPC.Addresses()[0] + + w, err := wallet.NewWalletFromFile("../testdata/testwallet.json") + require.NoError(t, err) + acc := w.Accounts[0] + require.NoError(t, acc.Decrypt(testcli.TestWalletPass, w.Scrypt)) + + // Transfer some GAS to the account. + transferArgs := []string{ + "neo-go", "wallet", "nep17", "transfer", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--to", acc.Address, + "--token", "GAS", + "--amount", "10000", + "--from", testcli.ValidatorAddr, + "--force", + } + e.In.WriteString("one\r") + e.Run(t, transferArgs...) + line := e.GetNextLine(t) + txHash, err := util.Uint256DecodeStringLE(line) + require.NoError(t, err) + require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50) + + // Make a notary deposit. + c, err := rpcclient.New(context.Background(), endpoint, rpcclient.Options{}) + require.NoError(t, err) + require.NoError(t, c.Init()) + signer := actor.SignerAccount{ + Signer: transaction.Signer{Account: acc.ScriptHash(), Scopes: transaction.Global}, + Account: acc, + } + act, err := actor.New(c, []actor.SignerAccount{signer}) + require.NoError(t, err) + g := gas.New(act) + h, _, err := g.Transfer(acc.ScriptHash(), nativehashes.Notary, big.NewInt(20000000), []any{stackitem.Null{}, e.Chain.BlockHeight() + 1000}) + require.NoError(t, err) + require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(h, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50) + + // Submit some notary request. + pk, err := keys.NewPrivateKey() + require.NoError(t, err) + fakeAcc := notary.FakeSimpleAccount(pk.PublicKey()) + ntr, err := notary.NewActor(c, []actor.SignerAccount{ + signer, + { + Signer: transaction.Signer{Account: fakeAcc.ScriptHash(), Scopes: transaction.Global}, + Account: fakeAcc, + }, + }, acc) + require.NoError(t, err) + tx, err := ntr.MakeCall(nativehashes.GasToken, "transfer", acc.ScriptHash(), acc.ScriptHash(), big.NewInt(1), nil) + mainH, _, vub, err := ntr.Notarize(tx, err) + require.NoError(t, err) + + // Check the notary pool. + args := []string{"neo-go", "query", "notarypool", "--rpc-endpoint", endpoint} + t.Run("silent", func(t *testing.T) { + e.Run(t, args...) + e.CheckNextLine(t, `Current height:\s+(\d|\.)+`) + e.CheckNextLine(t, `Hash:\s+`+mainH.StringLE()) + e.CheckNextLine(t, `OnChain:\s+false`) + e.CheckNextLine(t, `ValidUntil:\s+`+strconv.FormatUint(uint64(vub), 10)) + e.CheckNextLine(t, `Fallbacks:\s+1`) + e.CheckEOF(t) + }) + + t.Run("verbose", func(t *testing.T) { + e.Run(t, append(args, "--verbose")...) + compareQueryTxVerbose(t, e, tx, 1) + }) + + t.Run("invalid", func(t *testing.T) { + t.Run("excessive arguments", func(t *testing.T) { + e.RunWithError(t, append(args, "bla")...) + }) + }) +} diff --git a/cli/server/cli_dump_test.go b/cli/server/cli_dump_test.go new file mode 100644 index 0000000..acc5eb5 --- /dev/null +++ b/cli/server/cli_dump_test.go @@ -0,0 +1,163 @@ +package server_test + +import ( + "os" + "path/filepath" + "strconv" + "testing" + + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +// generated via `go run ./scripts/gendump/main.go --out ./cli/server/testdata/chain50x2.acc --blocks 50 --txs 2`. +const inDump = "./testdata/chain50x2.acc" + +func TestDBRestoreDump(t *testing.T) { + tmpDir := t.TempDir() + + loadConfig := func(t *testing.T) config.Config { + chainPath := filepath.Join(tmpDir, "neogotestchain") + cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) + require.NoError(t, err, "could not load config") + cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB + cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath + return cfg + } + + cfg := loadConfig(t) + out, err := yaml.Marshal(cfg) + require.NoError(t, err) + + cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml") + require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm)) + + e := testcli.NewExecutor(t, false) + + stateDump := filepath.Join(tmpDir, "neogo.teststate") + baseArgs := []string{"neo-go", "db", "restore", "--unittest", + "--config-path", tmpDir, "--in", inDump, "--dump", stateDump} + + t.Run("excessive restore parameters", func(t *testing.T) { + e.RunWithError(t, append(baseArgs, "something")...) + }) + // First 15 blocks. + e.Run(t, append(baseArgs, "--count", "15")...) + + // Big count. + e.RunWithError(t, append(baseArgs, "--count", "1000")...) + + // Continue 15..25 + e.Run(t, append(baseArgs, "--count", "10")...) + + // Continue till end. + e.Run(t, baseArgs...) + + // Dump and compare. + dumpPath := filepath.Join(tmpDir, "testdump.acc") + incDumpPath := filepath.Join(tmpDir, "testincdump.acc") + + t.Run("missing config", func(t *testing.T) { + e.RunWithError(t, "neo-go", "db", "dump", "--privnet", + "--config-path", tmpDir, "--out", dumpPath) + }) + t.Run("bad logger config", func(t *testing.T) { + badConfigDir := t.TempDir() + logfile := filepath.Join(badConfigDir, "logdir") + require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) + cfg = loadConfig(t) + cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log") + out, err = yaml.Marshal(cfg) + require.NoError(t, err) + + cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml") + require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm)) + + e.RunWithError(t, "neo-go", "db", "dump", "--unittest", + "--config-path", badConfigDir, "--out", dumpPath) + }) + t.Run("bad storage config", func(t *testing.T) { + badConfigDir := t.TempDir() + logfile := filepath.Join(badConfigDir, "logdir") + require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) + cfg = loadConfig(t) + cfg.ApplicationConfiguration.DBConfiguration.Type = "" + out, err = yaml.Marshal(cfg) + require.NoError(t, err) + + cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml") + require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm)) + + e.RunWithError(t, "neo-go", "db", "dump", "--unittest", + "--config-path", badConfigDir, "--out", dumpPath) + }) + + baseCmd := []string{"neo-go", "db", "dump", "--unittest", + "--config-path", tmpDir, "--out", dumpPath} + + t.Run("invalid start/count", func(t *testing.T) { + e.RunWithError(t, append(baseCmd, "--start", "5", "--count", strconv.Itoa(50-5+1+1))...) + }) + t.Run("excessive dump parameters", func(t *testing.T) { + e.RunWithError(t, append(baseCmd, "something")...) + }) + + e.Run(t, append(baseCmd, "--non-incremental", "--out", dumpPath)...) + e.Run(t, append(baseCmd, "--out", incDumpPath)...) + + d1, err := os.ReadFile(inDump) + require.NoError(t, err) + d2, err := os.ReadFile(dumpPath) + require.NoError(t, err) + require.Equal(t, d1, d2, "dumps differ") + d3, err := os.ReadFile(incDumpPath) + require.NoError(t, err) + require.Equal(t, append([]byte{0, 0, 0, 0}, d1...), d3, "dumps differ") +} + +func TestDBDumpRestoreIncremental(t *testing.T) { + tmpDir := t.TempDir() + chainPath := filepath.Join(tmpDir, "neogotestchain") + nonincDump := filepath.Join(tmpDir, "nonincDump.acc") + incDump := filepath.Join(tmpDir, "incDump.acc") + + cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) + require.NoError(t, err, "could not load config") + cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB + cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath + out, err := yaml.Marshal(cfg) + require.NoError(t, err) + + cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml") + require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm)) + + e := testcli.NewExecutor(t, false) + + // Create DB from dump. + e.Run(t, "neo-go", "db", "restore", "--unittest", "--config-path", tmpDir, "--in", inDump) + + // Create two dumps: non-incremental and incremental. + dumpBaseArgs := []string{"neo-go", "db", "dump", "--unittest", + "--config-path", tmpDir} + + // Dump first 15 blocks to a non-incremental dump. + e.Run(t, append(dumpBaseArgs, "--out", nonincDump, "--count", "15")...) + + // Dump second 15 blocks to an incremental dump. + e.Run(t, append(dumpBaseArgs, "--out", incDump, "--start", "15", "--count", "15")...) + + // Clean the DB. + require.NoError(t, os.RemoveAll(chainPath)) + + // Restore chain from two dumps. + restoreBaseArgs := []string{"neo-go", "db", "restore", "--unittest", "--config-path", tmpDir} + + // Restore first 15 blocks from non-incremental dump. + e.Run(t, append(restoreBaseArgs, "--in", nonincDump)...) + + // Restore second 15 blocks from incremental dump. + e.Run(t, append(restoreBaseArgs, "--in", incDump, "--count", "15")...) +} diff --git a/cli/server/cli_server_test.go b/cli/server/cli_server_test.go new file mode 100644 index 0000000..5e76d45 --- /dev/null +++ b/cli/server/cli_server_test.go @@ -0,0 +1,132 @@ +package server_test + +import ( + "errors" + "io" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" + + "github.com/nspcc-dev/neo-go/cli/server" + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestServerStart(t *testing.T) { + tmpDir := t.TempDir() + goodCfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) + require.NoError(t, err, "could not load config") + ptr := &goodCfg + saveCfg := func(t *testing.T, f func(cfg *config.Config)) string { + cfg := *ptr + chainPath := filepath.Join(t.TempDir(), "neogotestchain") + cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB + cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath + f(&cfg) + out, err := yaml.Marshal(cfg) + require.NoError(t, err) + + cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml") + require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm)) + t.Cleanup(func() { + require.NoError(t, os.Remove(cfgPath)) + }) + return cfgPath + } + + baseCmd := []string{"neo-go", "node", "--unittest", "--config-path", tmpDir} + e := testcli.NewExecutor(t, false) + + t.Run("invalid config path", func(t *testing.T) { + e.RunWithError(t, baseCmd...) + }) + t.Run("bad logger config", func(t *testing.T) { + badConfigDir := t.TempDir() + logfile := filepath.Join(badConfigDir, "logdir") + require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) + saveCfg(t, func(cfg *config.Config) { + cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log") + }) + e.RunWithError(t, baseCmd...) + }) + t.Run("invalid storage", func(t *testing.T) { + saveCfg(t, func(cfg *config.Config) { + cfg.ApplicationConfiguration.DBConfiguration.Type = "" + }) + e.RunWithError(t, baseCmd...) + }) + t.Run("stateroot service is on && StateRootInHeader=true", func(t *testing.T) { + saveCfg(t, func(cfg *config.Config) { + cfg.ApplicationConfiguration.StateRoot.Enabled = true + cfg.ProtocolConfiguration.StateRootInHeader = true + }) + e.RunWithError(t, baseCmd...) + }) + t.Run("invalid Oracle config", func(t *testing.T) { + saveCfg(t, func(cfg *config.Config) { + cfg.ApplicationConfiguration.Oracle.Enabled = true + cfg.ApplicationConfiguration.Oracle.UnlockWallet.Path = "bad_orc_wallet.json" + }) + e.RunWithError(t, baseCmd...) + }) + t.Run("invalid consensus config", func(t *testing.T) { + saveCfg(t, func(cfg *config.Config) { + cfg.ApplicationConfiguration.Consensus.Enabled = true + cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path = "bad_consensus_wallet.json" + }) + e.RunWithError(t, baseCmd...) + }) + t.Run("invalid Notary config", func(t *testing.T) { + t.Run("malformed config", func(t *testing.T) { + saveCfg(t, func(cfg *config.Config) { + cfg.ProtocolConfiguration.P2PSigExtensions = false + cfg.ApplicationConfiguration.P2PNotary.Enabled = true + }) + e.RunWithError(t, baseCmd...) + }) + t.Run("invalid wallet", func(t *testing.T) { + saveCfg(t, func(cfg *config.Config) { + cfg.ProtocolConfiguration.P2PSigExtensions = true + cfg.ApplicationConfiguration.P2PNotary.Enabled = true + cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path = "bad_notary_wallet.json" + }) + e.RunWithError(t, baseCmd...) + }) + }) + // We can't properly shutdown server on windows and release the resources. + // Also, windows doesn't support SIGHUP and SIGINT. + if runtime.GOOS != "windows" { + saveCfg(t, func(cfg *config.Config) {}) + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, append(baseCmd, "something")...) + }) + t.Run("good", func(t *testing.T) { + go func() { + e.Run(t, baseCmd...) + }() + + var line string + require.Eventually(t, func() bool { + line, err = e.Out.ReadString('\n') + if err != nil && !errors.Is(err, io.EOF) { + t.Fatalf("unexpected error while reading CLI output: %s", err) + } + return err == nil + }, 2*time.Second, 100*time.Millisecond) + for expected := range strings.SplitSeq(server.Logo(), "\n") { + // It should be regexp, so escape all backslashes. + expected = strings.ReplaceAll(expected, `\`, `\\`) + e.CheckLine(t, line, expected) + line = e.GetNextLine(t) + } + e.CheckNextLine(t, "") + e.CheckEOF(t) + }) + } +} diff --git a/cli/server/dump.go b/cli/server/dump.go new file mode 100644 index 0000000..d7b6a18 --- /dev/null +++ b/cli/server/dump.go @@ -0,0 +1,102 @@ +package server + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/nspcc-dev/neo-go/pkg/core/storage" + "github.com/nspcc-dev/neo-go/pkg/core/storage/dboper" +) + +type dump []blockDump + +type blockDump struct { + Block uint32 `json:"block"` + Size int `json:"size"` + Storage []dboper.Operation `json:"storage"` +} + +func newDump() *dump { + return new(dump) +} + +func (d *dump) add(index uint32, batch *storage.MemBatch) { + ops := storage.BatchToOperations(batch) + *d = append(*d, blockDump{ + Block: index, + Size: len(ops), + Storage: ops, + }) +} + +func (d *dump) tryPersist(prefix string, index uint32) error { + if len(*d) == 0 { + return nil + } + path, err := getPath(prefix, index) + if err != nil { + return err + } + old, err := readFile(path) + if err == nil { + *old = append(*old, *d...) + } else { + old = d + } + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + if err := enc.Encode(*old); err != nil { + return err + } + + *d = (*d)[:0] + + return nil +} + +func readFile(path string) (*dump, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + d := newDump() + if err := json.Unmarshal(data, d); err != nil { + return nil, err + } + return d, err +} + +// getPath returns filename for storing blocks up to index. +// Directory structure is the following: +// https://github.com/NeoResearch/neo-storage-audit#folder-organization-where-to-find-the-desired-block +// Dir `BlockStorage_$DIRNO` contains blocks up to $DIRNO (from $DIRNO-100k) +// Inside it there are files grouped by 1k blocks. +// File dump-block-$FILENO.json contains blocks from $FILENO-999, $FILENO +// Example: file `BlockStorage_100000/dump-block-6000.json` contains blocks from 5001 to 6000. +func getPath(prefix string, index uint32) (string, error) { + dirN := ((index + 99999) / 100000) * 100000 + dir := fmt.Sprintf("BlockStorage_%d", dirN) + + path := filepath.Join(prefix, dir) + info, err := os.Stat(path) + if os.IsNotExist(err) { + err := os.MkdirAll(path, os.ModePerm) + if err != nil { + return "", err + } + } else if !info.IsDir() { + return "", fmt.Errorf("file `%s` is not a directory", path) + } + + fileN := ((index + 999) / 1000) * 1000 + file := fmt.Sprintf("dump-block-%d.json", fileN) + return filepath.Join(path, file), nil +} diff --git a/cli/server/dump_bin.go b/cli/server/dump_bin.go new file mode 100644 index 0000000..66a5d3e --- /dev/null +++ b/cli/server/dump_bin.go @@ -0,0 +1,87 @@ +package server + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/urfave/cli/v2" +) + +func dumpBin(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + cfg, err := options.GetConfigFromContext(ctx) + if err != nil { + return cli.Exit(err, 1) + } + log, _, logCloser, err := options.HandleLoggingParams(ctx, cfg.ApplicationConfiguration) + if err != nil { + return cli.Exit(err, 1) + } + if logCloser != nil { + defer func() { _ = logCloser() }() + } + count := uint32(ctx.Uint("count")) + start := uint32(ctx.Uint("start")) + + chain, prometheus, pprof, err := InitBCWithMetrics(cfg, log) + if err != nil { + return err + } + defer func() { + pprof.ShutDown() + prometheus.ShutDown() + chain.Close() + }() + + blocksCount := chain.BlockHeight() + 1 + if start+count > blocksCount { + return cli.Exit(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", blocksCount-1, count, start), 1) + } + if count == 0 { + count = blocksCount - start + } + + out := ctx.String("out") + if out == "" { + return cli.Exit("output directory is not specified", 1) + } + if _, err = os.Stat(out); os.IsNotExist(err) { + if err = os.MkdirAll(out, os.ModePerm); err != nil { + return cli.Exit(fmt.Sprintf("failed to create directory %s: %s", out, err), 1) + } + } + if err != nil { + return cli.Exit(fmt.Sprintf("failed to check directory %s: %s", out, err), 1) + } + + for i := start; i < start+count; i++ { + blk, err := chain.GetBlock(chain.GetHeaderHash(i)) + if err != nil { + return cli.Exit(fmt.Sprintf("failed to get block %d: %s", i, err), 1) + } + filePath := filepath.Join(out, fmt.Sprintf("block-%d.bin", i)) + if err = saveBlockToFile(blk, filePath); err != nil { + return cli.Exit(fmt.Sprintf("failed to save block %d to file %s: %s", i, filePath, err), 1) + } + } + return nil +} + +func saveBlockToFile(blk *block.Block, filePath string) error { + file, err := os.Create(filePath) + if err != nil { + return err + } + defer file.Close() + + writer := io.NewBinWriterFromIO(file) + blk.EncodeBinary(writer) + return writer.Err +} diff --git a/cli/server/dump_bin_test.go b/cli/server/dump_bin_test.go new file mode 100644 index 0000000..0524f2c --- /dev/null +++ b/cli/server/dump_bin_test.go @@ -0,0 +1,91 @@ +package server_test + +import ( + "os" + "path/filepath" + "strconv" + "testing" + + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestDumpBin(t *testing.T) { + tmpDir := t.TempDir() + + loadConfig := func(t *testing.T) config.Config { + chainPath := filepath.Join(tmpDir, "neogotestchain") + cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml")) + require.NoError(t, err, "could not load config") + cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB + cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath + return cfg + } + + cfg := loadConfig(t) + out, err := yaml.Marshal(cfg) + require.NoError(t, err) + + cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml") + require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm)) + + e := testcli.NewExecutor(t, false) + + restoreArgs := []string{"neo-go", "db", "restore", + "--config-file", cfgPath, "--in", inDump} + e.Run(t, restoreArgs...) + + t.Run("missing output directory", func(t *testing.T) { + args := []string{"neo-go", "db", "dump-bin", + "--config-file", cfgPath, "--out", ""} + e.RunWithErrorCheck(t, "output directory is not specified", args...) + }) + + t.Run("successful dump", func(t *testing.T) { + outDir := filepath.Join(tmpDir, "blocks") + args := []string{"neo-go", "db", "dump-bin", + "--config-file", cfgPath, "--out", outDir, "--count", "5", "--start", "0"} + + e.Run(t, args...) + + require.DirExists(t, outDir) + + for i := range 5 { + blockFile := filepath.Join(outDir, "block-"+strconv.Itoa(i)+".bin") + require.FileExists(t, blockFile) + } + }) + + t.Run("invalid block range", func(t *testing.T) { + outDir := filepath.Join(tmpDir, "invalid-blocks") + args := []string{"neo-go", "db", "dump-bin", + "--config-file", cfgPath, "--out", outDir, "--count", "1000", "--start", "0"} + + e.RunWithError(t, args...) + }) + + t.Run("zero blocks (full chain dump)", func(t *testing.T) { + outDir := filepath.Join(tmpDir, "full-dump") + args := []string{"neo-go", "db", "dump-bin", + "--config-file", cfgPath, "--out", outDir} + + e.Run(t, args...) + + require.DirExists(t, outDir) + for i := range 5 { + blockFile := filepath.Join(outDir, "block-"+strconv.Itoa(i)+".bin") + require.FileExists(t, blockFile) + } + }) + + t.Run("invalid config file", func(t *testing.T) { + outDir := filepath.Join(tmpDir, "blocks") + args := []string{"neo-go", "db", "dump-bin", + "--config-file", "invalid-config-path", "--out", outDir} + + e.RunWithError(t, args...) + }) +} diff --git a/cli/server/dump_test.go b/cli/server/dump_test.go new file mode 100644 index 0000000..74877e4 --- /dev/null +++ b/cli/server/dump_test.go @@ -0,0 +1,23 @@ +package server + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetPath(t *testing.T) { + testPath := t.TempDir() + actual, err := getPath(testPath, 123) + require.NoError(t, err) + require.Equal(t, filepath.Join(testPath, "BlockStorage_100000", "dump-block-1000.json"), actual) + + actual, err = getPath(testPath, 1230) + require.NoError(t, err) + require.Equal(t, filepath.Join(testPath, "BlockStorage_100000", "dump-block-2000.json"), actual) + + actual, err = getPath(testPath, 123000) + require.NoError(t, err) + require.Equal(t, filepath.Join(testPath, "BlockStorage_200000", "dump-block-123000.json"), actual) +} diff --git a/cli/server/metrics.go b/cli/server/metrics.go new file mode 100644 index 0000000..ddea09d --- /dev/null +++ b/cli/server/metrics.go @@ -0,0 +1,23 @@ +package server + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +var neogoVersion = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Help: "NeoGo version", + Name: "version", + Namespace: "neogo", + }, + []string{"version"}) + +func setNeoGoVersion(nodeVer string) { + neogoVersion.WithLabelValues(nodeVer).Add(1) +} + +func init() { + prometheus.MustRegister( + neogoVersion, + ) +} diff --git a/cli/server/server.go b/cli/server/server.go new file mode 100644 index 0000000..69423c5 --- /dev/null +++ b/cli/server/server.go @@ -0,0 +1,709 @@ +package server + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + stdio "io" + "os" + "os/signal" + "slices" + "syscall" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/consensus" + "github.com/nspcc-dev/neo-go/pkg/core" + "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/core/chaindump" + corestate "github.com/nspcc-dev/neo-go/pkg/core/stateroot" + "github.com/nspcc-dev/neo-go/pkg/core/storage" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/network" + "github.com/nspcc-dev/neo-go/pkg/services/metrics" + "github.com/nspcc-dev/neo-go/pkg/services/notary" + "github.com/nspcc-dev/neo-go/pkg/services/oracle" + "github.com/nspcc-dev/neo-go/pkg/services/rpcsrv" + "github.com/nspcc-dev/neo-go/pkg/services/stateroot" + "github.com/urfave/cli/v2" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// NewCommands returns 'node' command. +func NewCommands() []*cli.Command { + cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath} + cfgFlags = append(cfgFlags, options.Network...) + + var cfgWithCountFlags = slices.Clone(cfgFlags) + cfgFlags = append(cfgFlags, options.Debug, options.ForceTimestampLogs) + cfgWithCountFlags = append(cfgWithCountFlags, + &cli.UintFlag{ + Name: "count", + Aliases: []string{"c"}, + Usage: "Number of blocks to be processed (default or 0: all chain)", + }, + ) + var cfgCountOutFlags = slices.Clone(cfgWithCountFlags) + cfgCountOutFlags = append(cfgCountOutFlags, + &cli.UintFlag{ + Name: "start", + Aliases: []string{"s"}, + Usage: "Block number to start from", + }, + &cli.StringFlag{ + Name: "out", + Aliases: []string{"o"}, + Usage: "Output file (stdout if not given)", + }, + ) + var cfgCountInFlags = slices.Clone(cfgWithCountFlags) + cfgCountInFlags = append(cfgCountInFlags, + &cli.StringFlag{ + Name: "in", + Aliases: []string{"i"}, + Usage: "Input file (stdin if not given)", + }, + &cli.StringFlag{ + Name: "dump", + Usage: "Directory for storing JSON dumps", + }, + ) + var cfgHeightFlags = slices.Clone(cfgFlags) + cfgHeightFlags = append(cfgHeightFlags, &cli.UintFlag{ + Name: "height", + Usage: "Height of the state to reset DB to", + Required: true, + }) + return []*cli.Command{ + { + Name: "node", + Usage: "Start a NeoGo node", + UsageText: "neo-go node [--config-path path] [-d] [-p/-m/-t] [--config-file file] [--force-timestamp-logs]", + Action: startServer, + Flags: cfgFlags, + }, + { + Name: "db", + Usage: "Database manipulations", + Subcommands: []*cli.Command{ + { + Name: "dump", + Usage: "Dump blocks (starting with the genesis or specified block) to the file", + UsageText: "neo-go db dump [-o file] [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file] [--force-timestamp-logs]", + Action: dumpDB, + Flags: append(cfgCountOutFlags, + &cli.BoolFlag{ + Name: "non-incremental", + Usage: "Force legacy (non-incremental) dump output", + }, + ), + }, + { + Name: "dump-bin", + Usage: "Dump blocks (starting with the genesis or specified block) to the directory in binary format", + UsageText: "neo-go db dump-bin -o directory [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file] [--force-timestamp-logs]", + Action: dumpBin, + Flags: cfgCountOutFlags, + }, + { + Name: "restore", + Usage: "Restore blocks from the file", + UsageText: "neo-go db restore [-i file] [--dump] [-c count] [--config-path path] [-p/-m/-t] [--config-file file] [--force-timestamp-logs]", + Action: restoreDB, + Flags: cfgCountInFlags, + }, + { + Name: "reset", + Usage: "Reset database to the previous state", + UsageText: "neo-go db reset --height height [--config-path path] [-p/-m/-t] [--config-file file] [--force-timestamp-logs]", + Action: resetDB, + Flags: cfgHeightFlags, + }, + }, + }, + } +} + +func newGraceContext() context.Context { + ctx, cancel := context.WithCancel(context.Background()) + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt) + signal.Notify(stop, syscall.SIGTERM) + go func() { + <-stop + cancel() + }() + return ctx +} + +// InitBCWithMetrics initializes the blockchain with metrics with the given configuration. +func InitBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) { + chain, _, err := initBlockChain(cfg, log) + if err != nil { + return nil, nil, nil, cli.Exit(err, 1) + } + prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log) + pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log) + + go chain.Run() + err = prometheus.Start() + if err != nil { + return nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Prometheus service: %w", err), 1) + } + err = pprof.Start() + if err != nil { + return nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Pprof service: %w", err), 1) + } + + return chain, prometheus, pprof, nil +} + +func dumpDB(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + cfg, err := options.GetConfigFromContext(ctx) + if err != nil { + return cli.Exit(err, 1) + } + log, _, logCloser, err := options.HandleLoggingParams(ctx, cfg.ApplicationConfiguration) + if err != nil { + return cli.Exit(err, 1) + } + if logCloser != nil { + defer func() { _ = logCloser() }() + } + count := uint32(ctx.Uint("count")) + start := uint32(ctx.Uint("start")) + + var outStream = os.Stdout + if out := ctx.String("out"); out != "" { + outStream, err = os.Create(out) + if err != nil { + return cli.Exit(err, 1) + } + } + defer outStream.Close() + writer := io.NewBinWriterFromIO(outStream) + + chain, prometheus, pprof, err := InitBCWithMetrics(cfg, log) + if err != nil { + return err + } + defer func() { + pprof.ShutDown() + prometheus.ShutDown() + chain.Close() + }() + + chainCount := chain.BlockHeight() + 1 + if start+count > chainCount { + return cli.Exit(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", chainCount-1, count, start), 1) + } + if count == 0 { + count = chainCount - start + } + if start != 0 || !ctx.Bool("non-incremental") { + writer.WriteU32LE(start) + } + writer.WriteU32LE(count) + err = chaindump.Dump(chain, writer, start, count) + if err != nil { + return cli.Exit(err.Error(), 1) + } + return nil +} + +func restoreDB(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + cfg, err := options.GetConfigFromContext(ctx) + if err != nil { + return err + } + log, _, logCloser, err := options.HandleLoggingParams(ctx, cfg.ApplicationConfiguration) + if err != nil { + return cli.Exit(err, 1) + } + if logCloser != nil { + defer func() { _ = logCloser() }() + } + count := uint32(ctx.Uint("count")) + + var inStream = os.Stdin + if in := ctx.String("in"); in != "" { + inStream, err = os.Open(in) + if err != nil { + return cli.Exit(err, 1) + } + } + defer inStream.Close() + reader := io.NewBinReaderFromIO(inStream) + + dumpDir := ctx.String("dump") + if dumpDir != "" { + cfg.ApplicationConfiguration.SaveStorageBatch = true + } + + chain, prometheus, pprof, err := InitBCWithMetrics(cfg, log) + if err != nil { + return err + } + defer func() { + pprof.ShutDown() + prometheus.ShutDown() + chain.Close() + }() + + var start = reader.ReadU32LE() + if reader.Err != nil { + return cli.Exit(err, 1) + } + var allBlocks = reader.ReadU32LE() + if reader.Err != nil { + return cli.Exit(err, 1) + } + var versionOrLen = reader.ReadU32LE() + if reader.Err != nil { + return cli.Exit(err, 1) + } + + var buf []byte + if versionOrLen == block.VersionInitial { + // Block length > 0 => we read block version, so we have ordinary chain dump. + buf = binary.LittleEndian.AppendUint32(buf, allBlocks) + allBlocks = start + start = 0 + } + buf = binary.LittleEndian.AppendUint32(buf, versionOrLen) + reader = io.NewBinReaderFromIO(stdio.MultiReader( + bytes.NewReader(buf), + inStream, + )) + + if chain.BlockHeight()+1 < start { + return cli.Exit(fmt.Errorf("expected height: %d, dump starts at %d", + chain.BlockHeight()+1, start), 1) + } + + var skip uint32 + if chain.BlockHeight() != 0 { + skip = chain.BlockHeight() + 1 - start + } + + if skip+count > allBlocks { + return cli.Exit(fmt.Errorf("input file has only %d blocks, can't read %d starting from %d", allBlocks, count, skip), 1) + } + if count == 0 { + count = allBlocks - skip + } + log.Info("initialize restore", + zap.Uint32("start", start), + zap.Uint32("height", chain.BlockHeight()), + zap.Uint32("skip", skip), + zap.Uint32("count", count)) + + gctx := newGraceContext() + var lastIndex uint32 + dump := newDump() + defer func() { + _ = dump.tryPersist(dumpDir, lastIndex) + }() + + var f = func(b *block.Block) error { + select { + case <-gctx.Done(): + return gctx.Err() + default: + return nil + } + } + if dumpDir != "" { + f = func(b *block.Block) error { + select { + case <-gctx.Done(): + return gctx.Err() + default: + } + batch := chain.LastBatch() + // The genesis block may already be persisted, so LastBatch() will return nil. + if batch == nil && b.Index == 0 { + return nil + } + dump.add(b.Index, batch) + lastIndex = b.Index + if b.Index%1000 == 0 { + if err := dump.tryPersist(dumpDir, b.Index); err != nil { + return fmt.Errorf("can't dump storage to file: %w", err) + } + } + return nil + } + } + + err = chaindump.Restore(chain, reader, skip, count, f) + if err != nil { + return cli.Exit(fmt.Errorf("wrong dump file or settings mismatch: %w", err), 1) + } + return nil +} + +func resetDB(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + cfg, err := options.GetConfigFromContext(ctx) + if err != nil { + return cli.Exit(err, 1) + } + h := uint32(ctx.Uint("height")) + + log, _, logCloser, err := options.HandleLoggingParams(ctx, cfg.ApplicationConfiguration) + if err != nil { + return cli.Exit(err, 1) + } + if logCloser != nil { + defer func() { _ = logCloser() }() + } + chain, store, err := initBlockChain(cfg, log) + if err != nil { + return cli.Exit(fmt.Errorf("failed to create Blockchain instance: %w", err), 1) + } + + err = chain.Reset(h) + if err != nil { + return cli.Exit(fmt.Errorf("failed to reset chain state to height %d: %w", h, err), 1) + } + err = store.Close() + if err != nil { + return cli.Exit(fmt.Errorf("failed to close the DB: %w", err), 1) + } + return nil +} + +// oracleService is an interface representing Oracle service with network.Service +// capabilities and ability to submit oracle responses. +type oracleService interface { + rpcsrv.OracleHandler + network.Service +} + +func mkOracle(config config.OracleConfiguration, magic netmode.Magic, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (oracleService, error) { + if !config.Enabled { + return nil, nil + } + orcCfg := oracle.Config{ + Log: log, + Network: magic, + MainCfg: config, + Chain: chain, + OnTransaction: serv.RelayTxn, + } + orc, err := oracle.NewOracle(orcCfg) + if err != nil { + return nil, fmt.Errorf("can't initialize Oracle module: %w", err) + } + chain.SetOracle(orc) + serv.AddService(orc) + return orc, nil +} + +func mkConsensus(config config.Consensus, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) { + if !config.Enabled { + return nil, nil + } + srv, err := consensus.NewService(consensus.Config{ + Logger: log, + Broadcast: serv.BroadcastExtensible, + Chain: chain, + BlockQueue: serv.GetBlockQueue(), + ProtocolConfiguration: chain.GetConfig().ProtocolConfiguration, + RequestTx: serv.RequestTx, + StopTxFlow: serv.StopTxFlow, + Wallet: config.UnlockWallet, + }) + if err != nil { + return nil, fmt.Errorf("can't initialize Consensus module: %w", err) + } + + serv.AddConsensusService(srv, srv.OnPayload, srv.OnTransaction) + return srv, nil +} + +func mkP2PNotary(config config.P2PNotary, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*notary.Notary, error) { + if !config.Enabled { + return nil, nil + } + if !chain.P2PSigExtensionsEnabled() { + return nil, errors.New("P2PSigExtensions are disabled, but Notary service is enabled") + } + cfg := notary.Config{ + MainCfg: config, + Chain: chain, + Log: log, + } + n, err := notary.NewNotary(cfg, serv.Net, serv.GetNotaryPool(), func(tx *transaction.Transaction) error { + err := serv.RelayTxn(tx) + if err != nil && !errors.Is(err, core.ErrAlreadyExists) && !errors.Is(err, core.ErrAlreadyInPool) { + return fmt.Errorf("can't relay completed notary transaction: hash %s, error: %w", tx.Hash().StringLE(), err) + } + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to create Notary module: %w", err) + } + serv.AddService(n) + chain.SetNotary(n) + return n, nil +} + +func startServer(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + + cfg, err := options.GetConfigFromContext(ctx) + if err != nil { + return cli.Exit(err, 1) + } + var logDebug = ctx.Bool("debug") + log, logLevel, logCloser, err := options.HandleLoggingParams(ctx, cfg.ApplicationConfiguration) + if err != nil { + return cli.Exit(err, 1) + } + if logCloser != nil { + defer func() { _ = logCloser() }() + } + + grace, cancel := context.WithCancel(newGraceContext()) + defer cancel() + + serverConfig, err := network.NewServerConfig(cfg) + if err != nil { + return cli.Exit(err, 1) + } + + chain, prometheus, pprof, err := InitBCWithMetrics(cfg, log) + if err != nil { + return cli.Exit(err, 1) + } + defer func() { + pprof.ShutDown() + prometheus.ShutDown() + chain.Close() + }() + + serv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), log) + if err != nil { + return cli.Exit(fmt.Errorf("failed to create network server: %w", err), 1) + } + srMod := chain.GetStateModule().(*corestate.Module) // Take full responsibility here. + sr, err := stateroot.New(serverConfig.StateRootCfg, srMod, log, chain, serv.BroadcastExtensible) + if err != nil { + return cli.Exit(fmt.Errorf("can't initialize StateRoot service: %w", err), 1) + } + serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload) + + oracleSrv, err := mkOracle(cfg.ApplicationConfiguration.Oracle, cfg.ProtocolConfiguration.Magic, chain, serv, log) + if err != nil { + return cli.Exit(err, 1) + } + dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, chain, serv, log) + if err != nil { + return cli.Exit(err, 1) + } + p2pNotary, err := mkP2PNotary(cfg.ApplicationConfiguration.P2PNotary, chain, serv, log) + if err != nil { + return cli.Exit(err, 1) + } + errChan := make(chan error) + rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan) + serv.AddService(rpcServer) + setNeoGoVersion(config.Version) + serv.Start() + if !cfg.ApplicationConfiguration.RPC.StartWhenSynchronized { + // Run RPC server in a separate routine. This is necessary to avoid a potential + // deadlock: Start() can write errors to errChan which is not yet read in the + // current execution context (see for-loop below). + go rpcServer.Start() + } + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, sighup) + signal.Notify(sigCh, sigusr1) + signal.Notify(sigCh, sigusr2) + + fmt.Fprintln(ctx.App.Writer, Logo()) + fmt.Fprintln(ctx.App.Writer, serv.UserAgent) + fmt.Fprintln(ctx.App.Writer) + + var shutdownErr error +Main: + for { + select { + case err := <-errChan: + shutdownErr = fmt.Errorf("server error: %w", err) + cancel() + case sig := <-sigCh: + var newLogLevel = zapcore.InvalidLevel + + log.Info("signal received", zap.Stringer("name", sig)) + cfgnew, err := options.GetConfigFromContext(ctx) + if err != nil { + log.Warn("can't reread the config file, signal ignored", zap.Error(err)) + break // Continue working. + } + if !cfg.ProtocolConfiguration.Equals(&cfgnew.ProtocolConfiguration) { + log.Warn("ProtocolConfiguration changed, signal ignored") + break // Continue working. + } + if !cfg.ApplicationConfiguration.EqualsButServices(&cfgnew.ApplicationConfiguration) { + log.Warn("ApplicationConfiguration changed in incompatible way, signal ignored") + break // Continue working. + } + if !logDebug && cfgnew.ApplicationConfiguration.LogLevel != cfg.ApplicationConfiguration.LogLevel { + newLogLevel, err = zapcore.ParseLevel(cfgnew.ApplicationConfiguration.LogLevel) + if err != nil { + log.Warn("wrong LogLevel in ApplicationConfiguration, signal ignored", zap.Error(err)) + break // Continue working. + } + } + switch sig { + case sighup: + if newLogLevel != zapcore.InvalidLevel { + logLevel.SetLevel(newLogLevel) + log.Warn("using new logging level", zap.Stringer("level", newLogLevel)) + } + serv.DelService(rpcServer) + rpcServer.Shutdown() + rpcServer = rpcsrv.New(chain, cfgnew.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan) + serv.AddService(rpcServer) + if !cfgnew.ApplicationConfiguration.RPC.StartWhenSynchronized || serv.IsInSync() { + // Here similar to the initial run (see above for-loop), so async. + go rpcServer.Start() + } + pprof.ShutDown() + pprof = metrics.NewPprofService(cfgnew.ApplicationConfiguration.Pprof, log) + err = pprof.Start() + if err != nil { + shutdownErr = fmt.Errorf("failed to start Pprof service: %w", err) + cancel() // Fatal error, like for RPC server. + } + prometheus.ShutDown() + prometheus = metrics.NewPrometheusService(cfgnew.ApplicationConfiguration.Prometheus, log) + err = prometheus.Start() + if err != nil { + shutdownErr = fmt.Errorf("failed to start Prometheus service: %w", err) + cancel() // Fatal error, like for RPC server. + } + case sigusr1: + if oracleSrv != nil { + serv.DelService(oracleSrv) + chain.SetOracle(nil) + rpcServer.SetOracleHandler(nil) + oracleSrv.Shutdown() + } + oracleSrv, err = mkOracle(cfgnew.ApplicationConfiguration.Oracle, cfgnew.ProtocolConfiguration.Magic, chain, serv, log) + if err != nil { + log.Error("failed to create oracle service", zap.Error(err)) + break // Keep going. + } + if oracleSrv != nil { + rpcServer.SetOracleHandler(oracleSrv) + if serv.IsInSync() { + oracleSrv.Start() + } + } + if p2pNotary != nil { + serv.DelService(p2pNotary) + chain.SetNotary(nil) + p2pNotary.Shutdown() + } + p2pNotary, err = mkP2PNotary(cfgnew.ApplicationConfiguration.P2PNotary, chain, serv, log) + if err != nil { + log.Error("failed to create notary service", zap.Error(err)) + break // Keep going. + } + if p2pNotary != nil && serv.IsInSync() { + p2pNotary.Start() + } + serv.DelExtensibleService(sr, stateroot.Category) + srMod.SetUpdateValidatorsCallback(nil) + sr.Shutdown() + sr, err = stateroot.New(cfgnew.ApplicationConfiguration.StateRoot, srMod, log, chain, serv.BroadcastExtensible) + if err != nil { + log.Error("failed to create state validation service", zap.Error(err)) + break // The show must go on. + } + serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload) + if serv.IsInSync() { + sr.Start() + } + case sigusr2: + if dbftSrv != nil { + serv.DelConsensusService(dbftSrv) + dbftSrv.Shutdown() + } + dbftSrv, err = mkConsensus(cfgnew.ApplicationConfiguration.Consensus, chain, serv, log) + if err != nil { + log.Error("failed to create consensus service", zap.Error(err)) + break // Whatever happens, I'll leave it all to chance. + } + if dbftSrv != nil && serv.IsInSync() { + dbftSrv.Start() + } + } + cfg = cfgnew + case <-grace.Done(): + signal.Stop(sigCh) + serv.Shutdown() + break Main + } + } + + if shutdownErr != nil { + return cli.Exit(shutdownErr, 1) + } + + return nil +} + +// initBlockChain initializes BlockChain with preselected DB. +func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, error) { + store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration) + if err != nil { + return nil, nil, cli.Exit(fmt.Errorf("could not initialize storage: %w", err), 1) + } + + chain, err := core.NewBlockchain(store, cfg.Blockchain(), log) + if err != nil { + errText := "could not initialize blockchain: %w" + errArgs := []any{err} + closeErr := store.Close() + if closeErr != nil { + errText += "; failed to close the DB: %w" + errArgs = append(errArgs, closeErr) + } + + return nil, nil, cli.Exit(fmt.Errorf(errText, errArgs...), 1) + } + return chain, store, nil +} + +// Logo returns NeoGo logo. +func Logo() string { + return ` + _ ____________ __________ + / | / / ____/ __ \ / ____/ __ \ + / |/ / __/ / / / /_____/ / __/ / / / + / /| / /___/ /_/ /_____/ /_/ / /_/ / +/_/ |_/_____/\____/ \____/\____/ +` +} diff --git a/cli/server/server_test.go b/cli/server/server_test.go new file mode 100644 index 0000000..6c44784 --- /dev/null +++ b/cli/server/server_test.go @@ -0,0 +1,408 @@ +package server + +import ( + "encoding/binary" + "flag" + "os" + "path/filepath" + "runtime" + "testing" + + "go.uber.org/zap/zapcore" + + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" +) + +// serverTestWD is the default working directory for server tests. +var serverTestWD string + +func init() { + var err error + serverTestWD, err = os.Getwd() + if err != nil { + panic("can't get current working directory") + } +} + +func TestGetConfigFromContext(t *testing.T) { + t.Run("config-path", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("config-path", "../../config", "") + set.Bool("testnet", true, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + cfg, err := options.GetConfigFromContext(ctx) + require.NoError(t, err) + require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic) + }) + t.Run("config-file", func(t *testing.T) { + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("config-path", "../../config", "") + set.Bool("testnet", true, "") + set.String("config-file", "../../config/protocol.testnet.yml", "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + cfg, err := options.GetConfigFromContext(ctx) + require.NoError(t, err) + require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic) + }) + t.Run("relative-path windows", func(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("skipping Windows specific test") + } + + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("relative-path", "..\\..\\config", "") + set.Bool("testnet", true, "") + set.String("config-file", ".\\testdata\\protocol.testnet.windows.yml", "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + cfg, err := options.GetConfigFromContext(ctx) + require.NoError(t, err) + require.Equal(t, filepath.Join("..", "..", "config", "chains", "testnet"), cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath) + require.Equal(t, "C:\\someFolder\\cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path) + require.Equal(t, "C:\\someFolder\\notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path) + }) + + t.Run("relative-path non-windows", func(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping non-Windows specific test") + } + + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("relative-path", "../../config", "") + set.Bool("testnet", true, "") + set.String("config-file", "../../config/protocol.testnet.yml", "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + cfg, err := options.GetConfigFromContext(ctx) + require.NoError(t, err) + require.Equal(t, filepath.Join("..", "..", "config", "chains", "testnet"), cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath) + require.Equal(t, "/cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path) + require.Equal(t, "/notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path) + }) +} + +func TestHandleLoggingParams(t *testing.T) { + d := t.TempDir() + testLog := filepath.Join(d, "file.log") + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + debug := set.Bool("debug", false, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + + t.Run("logdir is a file", func(t *testing.T) { + logfile := filepath.Join(d, "logdir") + require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) + cfg := config.ApplicationConfiguration{ + Logger: config.Logger{ + LogPath: filepath.Join(logfile, "file.log"), + }, + } + _, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) + require.Error(t, err) + require.Nil(t, lvl) + require.Nil(t, closer) + }) + + t.Run("broken level", func(t *testing.T) { + cfg := config.ApplicationConfiguration{ + Logger: config.Logger{ + LogPath: testLog, + LogLevel: "qwerty", + }, + } + _, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) + require.Error(t, err) + require.Nil(t, lvl) + require.Nil(t, closer) + }) + + t.Run("default", func(t *testing.T) { + cfg := config.ApplicationConfiguration{ + Logger: config.Logger{ + LogPath: testLog, + }, + } + logger, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) + require.NotNil(t, lvl) + require.NoError(t, err) + t.Cleanup(func() { + if closer != nil { + require.NoError(t, closer()) + } + }) + require.Equal(t, zapcore.InfoLevel, lvl.Level()) + require.True(t, logger.Core().Enabled(zapcore.InfoLevel)) + require.False(t, logger.Core().Enabled(zapcore.DebugLevel)) + }) + + t.Run("warn", func(t *testing.T) { + cfg := config.ApplicationConfiguration{ + Logger: config.Logger{ + LogPath: testLog, + LogLevel: "warn", + }, + } + logger, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) + require.NoError(t, err) + t.Cleanup(func() { + if closer != nil { + require.NoError(t, closer()) + } + }) + require.Equal(t, zapcore.WarnLevel, lvl.Level()) + require.True(t, logger.Core().Enabled(zapcore.WarnLevel)) + require.False(t, logger.Core().Enabled(zapcore.InfoLevel)) + }) + + t.Run("debug", func(t *testing.T) { + cfg := config.ApplicationConfiguration{ + Logger: config.Logger{ + LogPath: testLog, + }, + } + *debug = true + t.Cleanup(func() { *debug = false }) + logger, lvl, closer, err := options.HandleLoggingParams(ctx, cfg) + require.NoError(t, err) + t.Cleanup(func() { + if closer != nil { + require.NoError(t, closer()) + } + }) + require.Equal(t, zapcore.DebugLevel, lvl.Level()) + require.True(t, logger.Core().Enabled(zapcore.InfoLevel)) + require.True(t, logger.Core().Enabled(zapcore.DebugLevel)) + }) +} + +func TestInitBCWithMetrics(t *testing.T) { + d := t.TempDir() + err := os.Chdir(d) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) }) + + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "") + set.Bool("testnet", true, "") + set.Bool("debug", true, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + cfg, err := options.GetConfigFromContext(ctx) + require.NoError(t, err) + logger, _, closer, err := options.HandleLoggingParams(ctx, cfg.ApplicationConfiguration) + require.NoError(t, err) + t.Cleanup(func() { + if closer != nil { + require.NoError(t, closer()) + } + }) + + t.Run("bad store", func(t *testing.T) { + _, _, _, err = InitBCWithMetrics(config.Config{}, logger) + require.Error(t, err) + }) + + chain, prometheus, pprof, err := InitBCWithMetrics(cfg, logger) + require.NoError(t, err) + t.Cleanup(func() { + chain.Close() + prometheus.ShutDown() + pprof.ShutDown() + }) + require.Equal(t, netmode.TestNet, chain.GetConfig().Magic) +} + +func TestDumpDB(t *testing.T) { + testDump := "file.acc" + + t.Run("too low chain", func(t *testing.T) { + d := t.TempDir() + err := os.Chdir(d) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) }) + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "") + set.Bool("privnet", true, "") + set.Bool("debug", true, "") + set.Int("start", 0, "") + set.Int("count", 5, "") + set.String("out", testDump, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + err = dumpDB(ctx) + require.Error(t, err) + }) + + t.Run("positive", func(t *testing.T) { + d := t.TempDir() + err := os.Chdir(d) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) }) + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "") + set.Bool("privnet", true, "") + set.Bool("debug", true, "") + set.Int("start", 0, "") + set.Int("count", 1, "") + set.String("out", testDump, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + err = dumpDB(ctx) + require.NoError(t, err) + }) +} + +func TestRestoreDB(t *testing.T) { + d := t.TempDir() + testDump := "file1.acc" + saveDump := "file2.acc" + err := os.Chdir(d) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) }) + + // dump first + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + goodCfg := filepath.Join(serverTestWD, "..", "..", "config") + cfgPath := set.String("config-path", goodCfg, "") + set.Bool("privnet", true, "") + set.Bool("debug", true, "") + set.Bool("non-incremental", true, "") + set.Int("start", 0, "") + set.Int("count", 1, "") + set.String("out", testDump, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + err = dumpDB(ctx) + require.NoError(t, err) + + // and then restore + t.Run("invalid config", func(t *testing.T) { + *cfgPath = filepath.Join(serverTestWD, "..", "..", "config_invalid") + require.Error(t, restoreDB(ctx)) + }) + t.Run("invalid logger path", func(t *testing.T) { + badCfgDir := t.TempDir() + logfile := filepath.Join(badCfgDir, "logdir") + require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm)) + cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml")) + require.NoError(t, err, "could not load config") + cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log") + out, err := yaml.Marshal(cfg) + require.NoError(t, err) + + badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml") + require.NoError(t, os.WriteFile(badCfgPath, out, os.ModePerm)) + + *cfgPath = badCfgDir + require.Error(t, restoreDB(ctx)) + + *cfgPath = goodCfg + }) + t.Run("invalid bc config", func(t *testing.T) { + badCfgDir := t.TempDir() + cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml")) + require.NoError(t, err, "could not load config") + cfg.ApplicationConfiguration.DBConfiguration.Type = "" + out, err := yaml.Marshal(cfg) + require.NoError(t, err) + + badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml") + require.NoError(t, os.WriteFile(badCfgPath, out, os.ModePerm)) + + *cfgPath = badCfgDir + require.Error(t, restoreDB(ctx)) + + *cfgPath = goodCfg + }) + + in := set.String("in", testDump, "") + incremental := set.Bool("incremental", false, "") + t.Run("invalid in", func(t *testing.T) { + *in = "unknown-file" + require.Error(t, restoreDB(ctx)) + + *in = testDump + }) + t.Run("corrupted in: invalid block count", func(t *testing.T) { + inPath := filepath.Join(t.TempDir(), "file3.acc") + require.NoError(t, os.WriteFile(inPath, []byte{1, 2, 3}, // file is expected to start from uint32 + os.ModePerm)) + *in = inPath + require.Error(t, restoreDB(ctx)) + + *in = testDump + }) + t.Run("corrupted in: corrupted block", func(t *testing.T) { + inPath := filepath.Join(t.TempDir(), "file3.acc") + b, err := os.ReadFile(testDump) + require.NoError(t, err) + b[5] = 0xff // file is expected to start from uint32 (4 bytes) followed by the first block, so corrupt the first block bytes + require.NoError(t, os.WriteFile(inPath, b, os.ModePerm)) + *in = inPath + require.Error(t, restoreDB(ctx)) + + *in = testDump + }) + t.Run("incremental dump", func(t *testing.T) { + inPath := filepath.Join(t.TempDir(), "file1_incremental.acc") + b, err := os.ReadFile(testDump) + require.NoError(t, err) + start := make([]byte, 4) + t.Run("good", func(t *testing.T) { + binary.LittleEndian.PutUint32(start, 1) // start from the first block + require.NoError(t, os.WriteFile(inPath, append(start, b...), + os.ModePerm)) + *in = inPath + *incremental = true + + require.NoError(t, restoreDB(ctx)) + }) + t.Run("dump is too high", func(t *testing.T) { + binary.LittleEndian.PutUint32(start, 2) // start from the second block + require.NoError(t, os.WriteFile(inPath, append(start, b...), + os.ModePerm)) + *in = inPath + *incremental = true + + require.Error(t, restoreDB(ctx)) + }) + + *in = testDump + *incremental = false + }) + + set.String("dump", saveDump, "") + require.NoError(t, restoreDB(ctx)) +} + +func TestInitBlockChain(t *testing.T) { + t.Run("bad storage", func(t *testing.T) { + _, _, err := initBlockChain(config.Config{}, nil) + require.Error(t, err) + }) + + t.Run("empty logger", func(t *testing.T) { + _, _, err := initBlockChain(config.Config{ + ApplicationConfiguration: config.ApplicationConfiguration{ + DBConfiguration: dbconfig.DBConfiguration{ + Type: dbconfig.InMemoryDB, + }, + }, + }, nil) + require.Error(t, err) + }) +} + +func TestResetDB(t *testing.T) { + d := t.TempDir() + err := os.Chdir(d) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) }) + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "") + set.Bool("privnet", true, "") + set.Bool("debug", true, "") + set.Int("height", 0, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + err = resetDB(ctx) + require.NoError(t, err) +} diff --git a/cli/server/signals_unix.go b/cli/server/signals_unix.go new file mode 100644 index 0000000..ef9135b --- /dev/null +++ b/cli/server/signals_unix.go @@ -0,0 +1,11 @@ +//go:build !windows + +package server + +import "syscall" + +const ( + sighup = syscall.SIGHUP + sigusr1 = syscall.SIGUSR1 + sigusr2 = syscall.SIGUSR2 +) diff --git a/cli/server/signals_windows.go b/cli/server/signals_windows.go new file mode 100644 index 0000000..30a7033 --- /dev/null +++ b/cli/server/signals_windows.go @@ -0,0 +1,12 @@ +//go:build windows + +package server + +import "syscall" + +const ( + // Doesn't really matter, Windows can't do it. + sighup = syscall.SIGHUP + sigusr1 = syscall.Signal(0xa) + sigusr2 = syscall.Signal(0xc) +) diff --git a/cli/smartcontract/contract_test.go b/cli/smartcontract/contract_test.go new file mode 100644 index 0000000..725fe39 --- /dev/null +++ b/cli/smartcontract/contract_test.go @@ -0,0 +1,1448 @@ +package smartcontract_test + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/nspcc-dev/neo-go/cli/smartcontract" + "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/internal/testchain" + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/internal/versionutil" + "github.com/nspcc-dev/neo-go/pkg/config" + "github.com/nspcc-dev/neo-go/pkg/core/interop/storage" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +// Keep contract NEFs consistent between runs. +const _ = versionutil.TestVersion + +func TestCalcHash(t *testing.T) { + tmpDir := t.TempDir() + e := testcli.NewExecutor(t, false) + + nefPath := "./testdata/verify.nef" + src, err := os.ReadFile(nefPath) + require.NoError(t, err) + nefF, err := nef.FileFromBytes(src) + require.NoError(t, err) + manifestPath := "./testdata/verify.manifest.json" + manifestBytes, err := os.ReadFile(manifestPath) + require.NoError(t, err) + manif := &manifest.Manifest{} + err = json.Unmarshal(manifestBytes, manif) + require.NoError(t, err) + sender := random.Uint160() + + cmd := []string{"neo-go", "contract", "calc-hash"} + t.Run("no sender", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "sender" not set`, append(cmd, "--in", nefPath, "--manifest", manifestPath)...) + }) + t.Run("no nef file", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "in" not set`, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...) + }) + t.Run("no manifest file", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "manifest" not set`, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...) + }) + t.Run("invalid nef path", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), + "--in", "./testdata/verify.nef123", "--manifest", manifestPath)...) + }) + t.Run("invalid manifest path", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), + "--in", nefPath, "--manifest", "./testdata/verify.manifest123")...) + }) + t.Run("invalid nef file", func(t *testing.T) { + p := filepath.Join(tmpDir, "neogo.calchash.verify.nef") + require.NoError(t, os.WriteFile(p, src[:4], os.ModePerm)) + e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", p, "--manifest", manifestPath)...) + }) + t.Run("invalid manifest file", func(t *testing.T) { + p := filepath.Join(tmpDir, "neogo.calchash.verify.manifest.json") + require.NoError(t, os.WriteFile(p, manifestBytes[:4], os.ModePerm)) + e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", nefPath, "--manifest", p)...) + }) + + cmd = append(cmd, "--in", nefPath, "--manifest", manifestPath) + expected := state.CreateContractHash(sender, nefF.Checksum, manif.Name) + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "something")...) + }) + t.Run("valid, uint160", func(t *testing.T) { + e.Run(t, append(cmd, "--sender", sender.StringLE())...) + e.CheckNextLine(t, expected.StringLE()) + }) + t.Run("valid, uint160 with 0x", func(t *testing.T) { + e.Run(t, append(cmd, "--sender", "0x"+sender.StringLE())...) + e.CheckNextLine(t, expected.StringLE()) + }) + t.Run("valid, address", func(t *testing.T) { + e.Run(t, append(cmd, "--sender", address.Uint160ToString(sender))...) + e.CheckNextLine(t, expected.StringLE()) + }) +} + +func TestContractBindings(t *testing.T) { + // For proper contract init. The actual version as it will be replaced. + smartcontract.ModVersion = "v0.0.0" + + tmpDir := t.TempDir() + e := testcli.NewExecutor(t, false) + + ctrPath := filepath.Join(tmpDir, "testcontract") + e.Run(t, "neo-go", "contract", "init", "--name", ctrPath) + + srcPath := filepath.Join(ctrPath, "main.go") + require.NoError(t, os.WriteFile(srcPath, []byte(`package testcontract +import( + alias "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" +) +type MyPair struct { + Key int + Value string +} +func ToMap(a []MyPair) map[int]string { + return nil +} +func ToArray(m map[int]string) []MyPair { + return nil +} +func Block() *alias.Block{ + return alias.GetBlock(1) +} +func Blocks() []*alias.Block { + return []*alias.Block{ + alias.GetBlock(10), + alias.GetBlock(11), + } +} +`), os.ModePerm)) + + cfgPath := filepath.Join(ctrPath, "neo-go.yml") + manifestPath := filepath.Join(tmpDir, "manifest.json") + bindingsPath := filepath.Join(tmpDir, "bindings.yml") + cmd := []string{"neo-go", "contract", "compile"} + + cmd = append(cmd, "--in", ctrPath, "--bindings", bindingsPath) + + // Replace `pkg/interop` in go.mod to avoid getting an actual module version. + require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop")) + + cmd = append(cmd, "--config", cfgPath, + "--out", filepath.Join(tmpDir, "out.nef"), + "--manifest", manifestPath, + "--bindings", bindingsPath) + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, append(cmd, "something")...) + }) + e.Run(t, cmd...) + e.CheckEOF(t) + require.FileExists(t, bindingsPath) + + outPath := filepath.Join(t.TempDir(), "binding.go") + e.Run(t, "neo-go", "contract", "generate-wrapper", + "--config", bindingsPath, "--manifest", manifestPath, + "--out", outPath, "--hash", "0x0123456789987654321001234567899876543210") + + bs, err := os.ReadFile(outPath) + require.NoError(t, err) + require.Equal(t, `// Code generated by neo-go contract generate-wrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package testcontract contains wrappers for testcontract contract. +package testcontract + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" + "myimport.com/testcontract" +) + +// Hash contains contract hash in big-endian form. +const Hash = "\x10\x32\x54\x76\x98\x89\x67\x45\x23\x01\x10\x32\x54\x76\x98\x89\x67\x45\x23\x01" + +// Block invokes `+"`block`"+` method of contract. +func Block() *ledger.Block { + return neogointernal.CallWithToken(Hash, "block", int(contract.All)).(*ledger.Block) +} + +// Blocks invokes `+"`blocks`"+` method of contract. +func Blocks() []*ledger.Block { + return neogointernal.CallWithToken(Hash, "blocks", int(contract.All)).([]*ledger.Block) +} + +// ToArray invokes `+"`toArray`"+` method of contract. +func ToArray(m map[int]string) []testcontract.MyPair { + return neogointernal.CallWithToken(Hash, "toArray", int(contract.All), m).([]testcontract.MyPair) +} + +// ToMap invokes `+"`toMap`"+` method of contract. +func ToMap(a []testcontract.MyPair) map[int]string { + return neogointernal.CallWithToken(Hash, "toMap", int(contract.All), a).(map[int]string) +} +`, string(bs)) +} + +// updateGoMod updates the go.mod file located in the specified directory. +// It sets the module name and replaces the neo-go interop package path with +// the provided one to avoid getting an actual module version. +func updateGoMod(dir, moduleName, neoGoPath string) error { + goModPath := filepath.Join(dir, "go.mod") + data, err := os.ReadFile(goModPath) + if err != nil { + return fmt.Errorf("failed to read go.mod: %w", err) + } + + i := bytes.IndexByte(data, '\n') + if i == -1 { + return fmt.Errorf("unexpected go.mod format") + } + + updatedData := append([]byte("module "+moduleName), data[i:]...) + wd, err := os.Getwd() + if err != nil { + return fmt.Errorf("failed to get working directory: %w", err) + } + + replacementPath := filepath.Join(wd, neoGoPath) + updatedData = append(updatedData, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "+replacementPath+" \n"...) + + if err := os.WriteFile(goModPath, updatedData, os.ModePerm); err != nil { + return fmt.Errorf("failed to write updated go.mod: %w", err) + } + + return nil +} + +func TestDynamicWrapper(t *testing.T) { + // For proper contract init. The actual version as it will be replaced. + smartcontract.ModVersion = "v0.0.0" + + tmpDir := t.TempDir() + e := testcli.NewExecutor(t, true) + + ctrPath := "../smartcontract/testdata" + + verifyHash := testcli.DeployContract(t, e, filepath.Join(ctrPath, "verify.go"), filepath.Join(ctrPath, "verify.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) + + helperContract := `package testcontract + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + verify "myimport.com/testcontract/bindings" +) + +func CallVerifyContract(h interop.Hash160) bool{ + contractInstance := verify.NewContract(h) + return contractInstance.Verify() +}` + + helperDir := filepath.Join(tmpDir, "helper") + e.Run(t, "neo-go", "contract", "init", "--name", helperDir) + + require.NoError(t, updateGoMod(helperDir, "myimport.com/testcontract", "../../pkg/interop")) + require.NoError(t, os.WriteFile(filepath.Join(helperDir, "main.go"), []byte(helperContract), os.ModePerm)) + require.NoError(t, os.Mkdir(filepath.Join(helperDir, "bindings"), os.ModePerm)) + + e.Run(t, "neo-go", "contract", "generate-wrapper", + "--config", filepath.Join(ctrPath, "verify.bindings.yml"), "--manifest", filepath.Join(ctrPath, "verify.manifest.json"), + "--out", filepath.Join(helperDir, "bindings", "testdata.go")) + e.Run(t, "neo-go", "contract", "compile", "--in", filepath.Join(helperDir, "main.go"), "--config", filepath.Join(helperDir, "neo-go.yml")) + helperHash := testcli.DeployContract(t, e, filepath.Join(helperDir, "main.go"), filepath.Join(helperDir, "neo-go.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) + + e.In.WriteString("one\r") + e.Run(t, "neo-go", "contract", "invokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, "--force", "--await", helperHash.StringLE(), "callVerifyContract", verifyHash.StringLE()) + + tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ") + aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application) + require.NoError(t, err) + require.Equal(t, aer[0].Stack[0].Value().(bool), true) +} + +func TestContractInitAndCompile(t *testing.T) { + // For proper contract init. The actual version as it will be replaced. + smartcontract.ModVersion = "v0.0.0" + + tmpDir := t.TempDir() + e := testcli.NewExecutor(t, false) + + t.Run("no path is provided", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "name" not set`, "neo-go", "contract", "init") + }) + t.Run("invalid path", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "init", "--name", "\x00") + }) + + ctrPath := filepath.Join(tmpDir, "testcontract") + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath, "something") + }) + + e.Run(t, "neo-go", "contract", "init", "--name", ctrPath) + + t.Run("don't rewrite existing directory", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "init", "--name", ctrPath) + }) + + ctrRootPath := filepath.Join(ctrPath, "main") + srcPath := ctrRootPath + ".go" + cfgPath := filepath.Join(ctrPath, "neo-go.yml") + nefPath := filepath.Join(tmpDir, "testcontract.nef") + manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json") + cmd := []string{"neo-go", "contract", "compile"} + t.Run("missing source", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...) + }) + + cmd = append(cmd, "--in", srcPath, "--out", nefPath, "--manifest", manifestPath) + t.Run("missing config, but require manifest", func(t *testing.T) { + e.RunWithError(t, cmd...) + }) + t.Run("provided non-existent config", func(t *testing.T) { + cfgName := filepath.Join(ctrPath, "notexists.yml") + e.RunWithError(t, append(cmd, "--config", cfgName)...) + }) + t.Run("provided corrupted config", func(t *testing.T) { + data, err := os.ReadFile(cfgPath) + require.NoError(t, err) + badCfg := filepath.Join(tmpDir, "bad.yml") + require.NoError(t, os.WriteFile(badCfg, data[:len(data)-5], os.ModePerm)) + e.RunWithError(t, append(cmd, "--config", badCfg)...) + }) + + // Replace `pkg/interop` in go.mod to avoid getting an actual module version. + require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop")) + + cmd = append(cmd, "--config", cfgPath) + + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, append(cmd, "something")...) + }) + + e.Run(t, cmd...) + e.CheckEOF(t) + require.FileExists(t, nefPath) + require.FileExists(t, manifestPath) + + t.Run("output hex script with --verbose", func(t *testing.T) { + e.Run(t, append(cmd, "--verbose")...) + e.CheckNextLine(t, "^[0-9a-hA-H]+$") + }) + + t.Run("autocomplete outputs", func(t *testing.T) { + cfg, err := os.ReadFile(cfgPath) + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(ctrPath, "main.yml"), cfg, os.ModePerm)) + e.Run(t, "neo-go", "contract", "compile", "--in", srcPath) + defaultNefPath := ctrRootPath + ".nef" + defaultManifestPath := ctrRootPath + ".manifest.json" + defaultBindingsPath := ctrRootPath + ".bindings.yml" + require.FileExists(t, defaultNefPath) + require.FileExists(t, defaultManifestPath) + require.FileExists(t, defaultBindingsPath) + }) +} + +// Checks that error is returned if GAS available for test-invoke exceeds +// GAS needed to be consumed. +func TestDeployBigContract(t *testing.T) { + e := testcli.NewExecutorWithConfig(t, true, true, func(c *config.Config) { + c.ApplicationConfiguration.RPC.MaxGasInvoke = fixedn.Fixed8(1) + }) + tmpDir := t.TempDir() + + nefName := filepath.Join(tmpDir, "deploy.nef") + manifestName := filepath.Join(tmpDir, "deploy.manifest.json") + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", // compile single file + "--config", "testdata/deploy/neo-go.yml", + "--out", nefName, "--manifest", manifestName) + + e.In.WriteString(testcli.ValidatorPass + "\r") + e.RunWithError(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName) +} + +func TestContractDeployWithData(t *testing.T) { + eCompile := testcli.NewExecutor(t, false) + tmpDir := t.TempDir() + + nefName := filepath.Join(tmpDir, "deploy.nef") + manifestName := filepath.Join(tmpDir, "deploy.manifest.json") + eCompile.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", // compile single file + "--config", "testdata/deploy/neo-go.yml", + "--out", nefName, "--manifest", manifestName) + + deployContract := func(t *testing.T, haveData bool, scope string, await bool) { + e := testcli.NewExecutor(t, true) + cmd := []string{ + "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName, + "--force", + } + + if await { + cmd = append(cmd, "--await") + } + if haveData { + cmd = append(cmd, "[", "key1", "12", "key2", "take_me_to_church", "]") + } + if scope != "" { + cmd = append(cmd, "--", testcli.ValidatorAddr+":"+scope) + } else { + scope = "CalledByEntry" + } + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, cmd...) + var tx *transaction.Transaction + if await { + tx, _ = e.CheckAwaitableTxPersisted(t) + } else { + tx, _ = e.CheckTxPersisted(t) + } + + require.Equal(t, scope, tx.Signers[0].Scopes.String()) + if !haveData { + return + } + + line, err := e.Out.ReadString('\n') + require.NoError(t, err) + line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: ")) + h, err := util.Uint160DecodeStringLE(line) + require.NoError(t, err) + + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + h.StringLE(), + "getValueWithKey", "key1", + ) + + res := new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException) + require.Len(t, res.Stack, 1) + require.Equal(t, []byte{12}, res.Stack[0].Value()) + + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + h.StringLE(), + "getValueWithKey", "key2", + ) + + res = new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException) + require.Len(t, res.Stack, 1) + require.Equal(t, []byte("take_me_to_church"), res.Stack[0].Value()) + } + + deployContract(t, true, "", false) + deployContract(t, false, "Global", false) + deployContract(t, true, "Global", false) + deployContract(t, false, "", true) + deployContract(t, true, "Global", true) + deployContract(t, true, "", true) +} + +func TestDeployWithSigners(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + + nefName := filepath.Join(tmpDir, "deploy.nef") + manifestName := filepath.Join(tmpDir, "deploy.manifest.json") + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", + "--config", "testdata/deploy/neo-go.yml", + "--out", nefName, "--manifest", manifestName) + + t.Run("missing nef", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName) + }) + t.Run("missing manifest", func(t *testing.T) { + e.RunWithErrorCheck(t, "required flag --manifest is empty", "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", "") + }) + t.Run("corrupted data", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName, + "[", "str1") + }) + t.Run("invalid data", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName, + "str1", "str2") + }) + t.Run("missing wallet", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName, + "[", "str1", "str2", "]") + }) + t.Run("missing RPC", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "deploy", + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName, + "[", "str1", "str2", "]") + }) + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName, + "--force", + "--", testcli.ValidatorAddr+":Global") + tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ") + require.Equal(t, transaction.Global, tx.Signers[0].Scopes) +} + +func TestContractUpdate(t *testing.T) { + tmp := t.TempDir() + nefA := filepath.Join(tmp, "a.nef") + mfA := filepath.Join(tmp, "a.manifest.json") + nefB := filepath.Join(tmp, "b.nef") + mfB := filepath.Join(tmp, "b.manifest.json") + + e := testcli.NewExecutor(t, true) + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", + "--config", "testdata/deploy/neo-go.yml", + "--out", nefA, "--manifest", mfA) + + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/updated.go", + "--config", "testdata/deploy/neo-go.yml", + "--out", nefB, "--manifest", mfB) + + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--address", testcli.ValidatorAddr, + "--in", nefA, "--manifest", mfA, + "--force", "--await", + ) + _, _ = e.CheckAwaitableTxPersisted(t) + line, err := e.Out.ReadString('\n') + require.NoError(t, err) + line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: ")) + hash, err := util.Uint160DecodeStringLE(line) + require.NoError(t, err) + + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + hash.StringLE(), + "getValue", + ) + res := new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException) + require.Len(t, res.Stack, 1) + require.Equal(t, string(res.Stack[0].Value().([]byte)), "on create|sub create") + + t.Run("missing hash", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "no smart contract hash was provided, specify one as the first argument", + "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", nefA, "--manifest", mfA, + ) + }) + + t.Run("invalid hash", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "invalid contract hash", + "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", nefA, "--manifest", mfA, + "badhex", + ) + }) + + t.Run("missing nef and manifest files", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "either manifest or .nef required", + "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), + ) + }) + + t.Run("malformed data param", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "unable to parse 'data' parameter: ", + "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", nefA, "--manifest", mfA, + testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), + "[", + ) + }) + + t.Run("too many data params", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "'data' should be represented as a single parameter", + "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", nefA, "--manifest", mfA, + testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), + "one", "two", + ) + }) + + t.Run("missing wallet", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "can't get sender address: ", + "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", nefA, "--manifest", mfA, + testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), + ) + }) + + t.Run("invalid signer", func(t *testing.T) { + e.In.WriteString(testcli.ValidatorPass + "\r") + e.RunWithErrorCheckExit(t, "failed to parse signer", + "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--in", nefB, "--manifest", mfB, + "--force", "--await", + hash.StringLE(), + "--", "badSigner", + ) + }) + + t.Run("good with only manifest", func(t *testing.T) { + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--manifest", "testdata/deploy/update.manifest.json", + "--force", "--await", + hash.StringLE(), + ) + _, _ = e.CheckAwaitableTxPersisted(t) + + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + hash.StringLE(), + "getValueUpdated", + ) + res2 := new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res2)) + require.Equal(t, vmstate.Halt.String(), res2.State, res2.FaultException) + require.Len(t, res2.Stack, 1) + require.Equal(t, + "on update|sub update", + string(res2.Stack[0].Value().([]byte)), + ) + }) + + t.Run("good", func(t *testing.T) { + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "contract", "update", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--in", nefB, "--manifest", mfB, + "--force", "--await", + hash.StringLE(), + ) + _, _ = e.CheckAwaitableTxPersisted(t) + require.NoError(t, err) + + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + hash.StringLE(), + "newMethod", + ) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException) + require.Len(t, res.Stack, 1) + require.Equal(t, int64(42), res.Stack[0].Value().(*big.Int).Int64()) + }) +} + +func TestContractDestroy(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + + nefName := filepath.Join(tmpDir, "destroy.nef") + manifestName := filepath.Join(tmpDir, "destroy.manifest.json") + + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", + "--config", "testdata/deploy/neo-go.yml", + "--out", nefName, "--manifest", manifestName, + ) + + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName, + "--force", "--await", + ) + _, _ = e.CheckAwaitableTxPersisted(t) + + line, err := e.Out.ReadString('\n') + require.NoError(t, err) + line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: ")) + hash, err := util.Uint160DecodeStringLE(line) + require.NoError(t, err) + + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + hash.StringLE(), + "getValue", + ) + res := new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException) + require.Len(t, res.Stack, 1) + require.Equal(t, string(res.Stack[0].Value().([]byte)), "on create|sub create") + + t.Run("missing hash", func(t *testing.T) { + e.RunWithErrorCheckExit(t, + "no smart contract hash was provided, specify one as the first argument", + "neo-go", "contract", "destroy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + ) + }) + + t.Run("invalid hash", func(t *testing.T) { + e.RunWithErrorCheckExit(t, + "invalid contract hash", + "neo-go", "contract", "destroy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "badhex", + ) + }) + + t.Run("missing wallet", func(t *testing.T) { + e.RunWithErrorCheckExit(t, + "can't get sender address:", + "neo-go", "contract", "destroy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), + ) + }) + + t.Run("invalid signer", func(t *testing.T) { + e.In.WriteString(testcli.ValidatorPass + "\r") + e.RunWithErrorCheckExit(t, + "failed to parse signer", + "neo-go", "contract", "destroy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--force", + testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), + "--", + "badSigner", + ) + }) + + t.Run("good", func(t *testing.T) { + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "contract", "destroy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, + "--force", "--await", + hash.StringLE(), + ) + + _, _ = e.CheckAwaitableTxPersisted(t) + require.NoError(t, err) + + e.RunWithErrorCheckExit(t, + fmt.Sprintf("System.Contract.Call failed: called contract %s not found: key not found", hash.StringLE()), + "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + hash.StringLE(), "getValue", + ) + }) +} + +func TestContractManifestGroups(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + + _, err := wallet.NewWalletFromFile(testcli.TestWalletPath) + require.NoError(t, err) + + nefName := filepath.Join(tmpDir, "deploy.nef") + manifestName := filepath.Join(tmpDir, "deploy.manifest.json") + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", // compile single file + "--config", "testdata/deploy/neo-go.yml", + "--out", nefName, "--manifest", manifestName) + + t.Run("missing wallet", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flags "sender, address, nef, manifest" not set`, "neo-go", "contract", "manifest", "add-group") + }) + t.Run("invalid wallet", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "manifest", "add-group", + "--wallet", t.TempDir(), "--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount, + "--nef", nefName, "--manifest", manifestName) + }) + t.Run("invalid sender", func(t *testing.T) { + e.RunWithErrorCheck(t, `invalid value "not-a-sender" for flag -sender: invalid base58 digit ('-')`, "neo-go", "contract", "manifest", "add-group", + "--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount, + "--sender", "not-a-sender", "--nef", nefName, "--manifest", manifestName) + }) + t.Run("invalid NEF file", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "manifest", "add-group", + "--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount, + "--sender", testcli.TestWalletAccount, "--nef", tmpDir, "--manifest", manifestName) + }) + t.Run("corrupted NEF file", func(t *testing.T) { + f := filepath.Join(tmpDir, "invalid.nef") + require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm)) + e.RunWithError(t, "neo-go", "contract", "manifest", "add-group", + "--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount, + "--sender", testcli.TestWalletAccount, "--nef", f, "--manifest", manifestName) + }) + t.Run("invalid manifest file", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "manifest", "add-group", + "--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount, + "--sender", testcli.TestWalletAccount, "--nef", nefName, + "--manifest", tmpDir) + }) + t.Run("corrupted manifest file", func(t *testing.T) { + f := filepath.Join(tmpDir, "invalid.manifest.json") + require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm)) + e.RunWithError(t, "neo-go", "contract", "manifest", "add-group", + "--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount, + "--sender", testcli.TestWalletAccount, "--nef", nefName, + "--manifest", f) + }) + t.Run("unknown account", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "manifest", "add-group", + "--wallet", testcli.TestWalletPath, "--address", util.Uint160{}.StringLE(), + "--sender", testcli.TestWalletAccount, "--nef", nefName, + "--manifest", manifestName) + }) + cmd := []string{"neo-go", "contract", "manifest", "add-group", + "--nef", nefName, "--manifest", manifestName} + + t.Run("excessive parameters", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--wallet", testcli.TestWalletPath, + "--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount, "something")...) + }) + e.In.WriteString("testpass\r") + e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath, + "--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount)...) + + e.In.WriteString("testpass\r") // should override signature with the previous sender + e.Run(t, append(cmd, "--wallet", testcli.TestWalletPath, + "--sender", testcli.ValidatorAddr, "--address", testcli.TestWalletAccount)...) + + e.In.WriteString(testcli.ValidatorPass + "\r") + e.Run(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", nefName, "--manifest", manifestName, + "--force", + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr) +} + +func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 { + return testcli.DeployContract(t, e, "testdata/verify.go", "testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass) +} + +func TestContract_TestInvokeScript(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + badNef := filepath.Join(tmpDir, "invalid.nef") + goodNef := filepath.Join(tmpDir, "deploy.nef") + manifestName := filepath.Join(tmpDir, "deploy.manifest.json") + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", // compile single file + "--config", "testdata/deploy/neo-go.yml", + "--out", goodNef, "--manifest", manifestName) + + t.Run("missing in", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0]) + }) + t.Run("empty in", func(t *testing.T) { + e.RunWithErrorCheck(t, "required flag --in is empty", "neo-go", "contract", "testinvokescript", "-i", "", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0]) + }) + t.Run("empty rpc", func(t *testing.T) { + e.RunWithErrorCheck(t, "required flag --rpc-endpoint is empty", "neo-go", "contract", "testinvokescript", "-i", goodNef, + "--rpc-endpoint", "") + }) + t.Run("unexisting in", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", badNef) + }) + t.Run("invalid nef", func(t *testing.T) { + require.NoError(t, os.WriteFile(badNef, []byte("qwer"), os.ModePerm)) + e.RunWithError(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", badNef) + }) + t.Run("invalid signers", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", goodNef, "--", "not-a-valid-signer") + }) + t.Run("no RPC endpoint", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://123456789", + "--in", goodNef) + }) + t.Run("good", func(t *testing.T) { + e.Run(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", goodNef) + }) + t.Run("good with hashed signer", func(t *testing.T) { + e.Run(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", goodNef, "--", util.Uint160{1, 2, 3}.StringLE()) + }) + t.Run("good with addressed signer", func(t *testing.T) { + e.Run(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--in", goodNef, "--", address.Uint160ToString(util.Uint160{1, 2, 3})) + }) + t.Run("historic, invalid", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--historic", "bad", + "--in", goodNef) + }) + t.Run("historic, index", func(t *testing.T) { + e.Run(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--historic", "0", + "--in", goodNef) + }) + t.Run("historic, hash", func(t *testing.T) { + e.Run(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--historic", e.Chain.GetHeaderHash(0).StringLE(), + "--in", goodNef) + }) +} + +func TestComlileAndInvokeFunction(t *testing.T) { + e := testcli.NewExecutor(t, true) + tmpDir := t.TempDir() + + nefName := filepath.Join(tmpDir, "deploy.nef") + manifestName := filepath.Join(tmpDir, "deploy.manifest.json") + e.Run(t, "neo-go", "contract", "compile", + "--in", "testdata/deploy/main.go", // compile single file + "--config", "testdata/deploy/neo-go.yml", + "--out", nefName, "--manifest", manifestName) + + tmp := t.TempDir() + configPath := filepath.Join(tmp, "config.yaml") + cfg := config.Wallet{ + Path: testcli.ValidatorWallet, + Password: testcli.ValidatorPass, + } + yml, err := yaml.Marshal(cfg) + require.NoError(t, err) + require.NoError(t, os.WriteFile(configPath, yml, 0666)) + e.Run(t, "neo-go", "contract", "deploy", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "--force", + "--wallet-config", configPath, "--address", testcli.ValidatorAddr, + "--in", nefName, "--manifest", manifestName) + + e.CheckTxPersisted(t, "Sent invocation transaction ") + line, err := e.Out.ReadString('\n') + require.NoError(t, err) + line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: ")) + h, err := util.Uint160DecodeStringLE(line) + require.NoError(t, err) + + t.Run("check calc hash", func(t *testing.T) { + // missing sender + e.RunWithErrorCheck(t, `Required flag "sender" not set`, "neo-go", "contract", "calc-hash", + "--in", nefName, + "--manifest", manifestName) + + e.Run(t, "neo-go", "contract", "calc-hash", + "--sender", testcli.ValidatorAddr, "--in", nefName, + "--manifest", manifestName) + e.CheckNextLine(t, h.StringLE()) + }) + + cmd := []string{"neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]} + t.Run("missing hash", func(t *testing.T) { + e.RunWithError(t, cmd...) + }) + t.Run("invalid hash", func(t *testing.T) { + e.RunWithError(t, append(cmd, "notahash")...) + }) + + cmd = append(cmd, h.StringLE()) + t.Run("missing method", func(t *testing.T) { + e.RunWithError(t, cmd...) + }) + + cmd = append(cmd, "getValue") + t.Run("invalid params", func(t *testing.T) { + e.RunWithError(t, append(cmd, "[")...) + }) + t.Run("invalid cosigner", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--", "notahash")...) + }) + t.Run("missing RPC address", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "testinvokefunction", + h.StringLE(), "getValue") + }) + + e.Run(t, cmd...) + + checkGetValueOut := func(str string) { + res := new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException) + require.Len(t, res.Stack, 1) + require.Equal(t, []byte(str), res.Stack[0].Value()) + } + checkGetValueOut("on create|sub create") + + // deploy verification contract + hVerify := deployVerifyContract(t, e) + + t.Run("real invoke", func(t *testing.T) { + cmd := []string{"neo-go", "contract", "invokefunction", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]} + t.Run("missing wallet", func(t *testing.T) { + cmd := append(cmd, h.StringLE(), "getValue") + e.RunWithError(t, cmd...) + }) + t.Run("non-existent wallet", func(t *testing.T) { + cmd := append(cmd, "--wallet", filepath.Join(tmpDir, "not.exists"), + h.StringLE(), "getValue") + e.RunWithError(t, cmd...) + }) + t.Run("corrupted wallet", func(t *testing.T) { + tmp := t.TempDir() + tmpPath := filepath.Join(tmp, "wallet.json") + require.NoError(t, os.WriteFile(tmpPath, []byte("{"), os.ModePerm)) + + cmd := append(cmd, "--wallet", tmpPath, + h.StringLE(), "getValue") + e.RunWithError(t, cmd...) + }) + t.Run("non-existent address", func(t *testing.T) { + cmd := append(cmd, "--wallet", testcli.ValidatorWallet, + "--address", random.Uint160().StringLE(), + h.StringLE(), "getValue") + e.RunWithError(t, cmd...) + }) + t.Run("invalid password", func(t *testing.T) { + e.In.WriteString("invalid_password\r") + cmd := append(cmd, "--wallet", testcli.ValidatorWallet, + h.StringLE(), "getValue") + e.RunWithError(t, cmd...) + }) + t.Run("good: default address", func(t *testing.T) { + e.In.WriteString("one\r") + e.In.WriteString("y\r") + e.Run(t, append(cmd, "--wallet", testcli.ValidatorWallet, h.StringLE(), "getValue")...) + }) + t.Run("good: from wallet config", func(t *testing.T) { + e.In.WriteString("y\r") + e.Run(t, append(cmd, "--wallet-config", configPath, h.StringLE(), "getValue")...) + }) + + cmd = append(cmd, "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr) + t.Run("cancelled", func(t *testing.T) { + e.In.WriteString("one\r") + e.In.WriteString("n\r") + e.RunWithError(t, append(cmd, h.StringLE(), "getValue")...) + }) + t.Run("confirmed", func(t *testing.T) { + e.In.WriteString("one\r") + e.In.WriteString("y\r") + e.Run(t, append(cmd, h.StringLE(), "getValue")...) + }) + + t.Run("failind method", func(t *testing.T) { + e.In.WriteString("one\r") + e.In.WriteString("y\r") + e.RunWithError(t, append(cmd, h.StringLE(), "fail")...) + + e.In.WriteString("one\r") + e.Run(t, append(cmd, "--force", h.StringLE(), "fail")...) + }) + + t.Run("cosigner is deployed contract", func(t *testing.T) { + e.In.WriteString("one\r") + e.In.WriteString("y\r") + e.Run(t, append(cmd, h.StringLE(), "getValue", + "--", testcli.ValidatorAddr, hVerify.StringLE())...) + }) + + t.Run("with await", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, "--force", "--await", h.StringLE(), "getValue")...) + e.CheckAwaitableTxPersisted(t) + }) + }) + + t.Run("real invoke and save tx", func(t *testing.T) { + txout := filepath.Join(tmpDir, "test_contract_tx.json") + + cmd = []string{"neo-go", "contract", "invokefunction", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + "--out", txout, + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + } + + t.Run("without cosigner", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, hVerify.StringLE(), "verify")...) + }) + + t.Run("with cosigner", func(t *testing.T) { + t.Run("cosigner is sender (none)", func(t *testing.T) { + e.In.WriteString("one\r") + e.RunWithError(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":None")...) + }) + t.Run("cosigner is sender (customcontract)", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":CustomContracts:"+h.StringLE())...) + }) + t.Run("cosigner is sender (global)", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, h.StringLE(), "checkSenderWitness", "--", testcli.ValidatorAddr+":Global")...) + }) + + acc, err := wallet.NewAccount() + require.NoError(t, err) + pk, err := keys.NewPrivateKey() + require.NoError(t, err) + err = acc.ConvertMultisig(2, keys.PublicKeys{acc.PublicKey(), pk.PublicKey()}) + require.NoError(t, err) + + t.Run("cosigner is multisig account", func(t *testing.T) { + t.Run("missing in the wallet", func(t *testing.T) { + e.In.WriteString("one\r") + e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", acc.Address)...) + }) + + t.Run("good", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", testcli.MultisigAddr)...) + }) + }) + + t.Run("cosigner is deployed contract", func(t *testing.T) { + t.Run("missing in the wallet", func(t *testing.T) { + e.In.WriteString("one\r") + e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", h.StringLE())...) + }) + + t.Run("good", func(t *testing.T) { + e.In.WriteString("one\r") + e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", hVerify.StringLE())...) + }) + }) + }) + }) + + t.Run("test Storage.Find", func(t *testing.T) { + cmd := []string{"neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://" + e.RPC.Addresses()[0], + h.StringLE(), "testFind"} + + t.Run("keys only", func(t *testing.T) { + e.Run(t, append(cmd, strconv.FormatInt(storage.FindKeysOnly, 10))...) + res := new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State) + require.Len(t, res.Stack, 1) + require.Equal(t, []stackitem.Item{ + stackitem.Make("findkey1"), + stackitem.Make("findkey2"), + }, res.Stack[0].Value()) + }) + t.Run("both", func(t *testing.T) { + e.Run(t, append(cmd, strconv.FormatInt(storage.FindDefault, 10))...) + res := new(result.Invoke) + require.NoError(t, json.Unmarshal(e.Out.Bytes(), res)) + require.Equal(t, vmstate.Halt.String(), res.State) + require.Len(t, res.Stack, 1) + + arr, ok := res.Stack[0].Value().([]stackitem.Item) + require.True(t, ok) + require.Len(t, arr, 2) + require.Equal(t, []stackitem.Item{ + stackitem.Make("findkey1"), stackitem.Make("value1"), + }, arr[0].Value()) + require.Equal(t, []stackitem.Item{ + stackitem.Make("findkey2"), stackitem.Make("value2"), + }, arr[1].Value()) + }) + }) + + var ( + hashBeforeUpdate util.Uint256 + indexBeforeUpdate uint32 + indexAfterUpdate uint32 + stateBeforeUpdate util.Uint256 + ) + t.Run("Update", func(t *testing.T) { + nefName := filepath.Join(tmpDir, "updated.nef") + manifestName := filepath.Join(tmpDir, "updated.manifest.json") + e.Run(t, "neo-go", "contract", "compile", + "--config", "testdata/deploy/neo-go.yml", + "--in", "testdata/deploy/", // compile all files in dir + "--out", nefName, "--manifest", manifestName) + + t.Cleanup(func() { + os.Remove(nefName) + os.Remove(manifestName) + }) + + rawNef, err := os.ReadFile(nefName) + require.NoError(t, err) + rawManifest, err := os.ReadFile(manifestName) + require.NoError(t, err) + + indexBeforeUpdate = e.Chain.BlockHeight() + hashBeforeUpdate = e.Chain.CurrentHeaderHash() + mptBeforeUpdate, err := e.Chain.GetStateRoot(indexBeforeUpdate) + require.NoError(t, err) + stateBeforeUpdate = mptBeforeUpdate.Root + e.In.WriteString("one\r") + e.Run(t, "neo-go", "contract", "invokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, + "--force", + h.StringLE(), "update", + "bytes:"+hex.EncodeToString(rawNef), + "bytes:"+hex.EncodeToString(rawManifest), + "", + ) + e.CheckTxPersisted(t, "Sent invocation transaction ") + + indexAfterUpdate = e.Chain.BlockHeight() + e.In.WriteString("one\r") + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + h.StringLE(), "getValue") + checkGetValueOut("on update|sub update") + }) + t.Run("historic", func(t *testing.T) { + t.Run("bad ref", func(t *testing.T) { + e.RunWithError(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--historic", "bad", + h.StringLE(), "getValue") + }) + for name, ref := range map[string]string{ + "by index": strconv.FormatUint(uint64(indexBeforeUpdate), 10), + "by block hash": hashBeforeUpdate.StringLE(), + "by state hash": stateBeforeUpdate.StringLE(), + } { + t.Run(name, func(t *testing.T) { + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--historic", ref, + h.StringLE(), "getValue") + }) + checkGetValueOut("on create|sub create") + } + t.Run("updated historic", func(t *testing.T) { + e.Run(t, "neo-go", "contract", "testinvokefunction", + "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], + "--historic", strconv.FormatUint(uint64(indexAfterUpdate), 10), + h.StringLE(), "getValue") + checkGetValueOut("on update|sub update") + }) + }) +} + +func TestContractInspect(t *testing.T) { + e := testcli.NewExecutor(t, false) + const srcPath = "testdata/deploy/main.go" + tmpDir := t.TempDir() + + nefName := filepath.Join(tmpDir, "deploy.nef") + manifestName := filepath.Join(tmpDir, "deploy.manifest.json") + e.Run(t, "neo-go", "contract", "compile", + "--in", srcPath, + "--config", "testdata/deploy/neo-go.yml", + "--out", nefName, "--manifest", manifestName) + + cmd := []string{"neo-go", "contract", "inspect"} + t.Run("missing input", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...) + }) + t.Run("with raw '.go'", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--in", srcPath)...) + e.Run(t, append(cmd, "--in", srcPath, "--compile")...) + require.True(t, strings.Contains(e.Out.String(), "SYSCALL")) + }) + t.Run("with nef", func(t *testing.T) { + e.RunWithError(t, append(cmd, "--in", nefName, "--compile")...) + e.RunWithError(t, append(cmd, "--in", filepath.Join(tmpDir, "not.exists"))...) + e.RunWithError(t, append(cmd, "--in", nefName, "something")...) + e.Run(t, append(cmd, "--in", nefName)...) + require.True(t, strings.Contains(e.Out.String(), "SYSCALL")) + }) +} + +func TestCompileExamples(t *testing.T) { + tmpDir := t.TempDir() + const examplePath = "../../examples" + infos, err := os.ReadDir(examplePath) + require.NoError(t, err) + + e := testcli.NewExecutor(t, false) + + for _, info := range infos { + if !info.IsDir() { + // example smart contracts are located in the `/examples` subdirectories, but + // there are also a couple of files inside the `/examples` which doesn't need to be compiled + continue + } + if info.Name() == "zkp" { + // A set of special ZKP-related examples, they have their own tests. + continue + } + t.Run(info.Name(), func(t *testing.T) { + infos, err := os.ReadDir(filepath.Join(examplePath, info.Name())) + require.NoError(t, err) + require.False(t, len(infos) == 0, "detected smart contract folder with no contract in it") + + outF := filepath.Join(tmpDir, info.Name()+".nef") + manifestF := filepath.Join(tmpDir, info.Name()+".manifest.json") + bindingF := filepath.Join(tmpDir, info.Name()+".binding.yml") + wrapperF := filepath.Join(tmpDir, info.Name()+".go") + rpcWrapperF := filepath.Join(tmpDir, info.Name()+".rpc.go") + + cfgName := filterFilename(infos, ".yml") + opts := []string{ + "neo-go", "contract", "compile", + "--in", filepath.Join(examplePath, info.Name()), + "--out", outF, + "--manifest", manifestF, + "--config", filepath.Join(examplePath, info.Name(), cfgName), + "--bindings", bindingF, + } + e.Run(t, opts...) + + if info.Name() == "storage" { + rawM, err := os.ReadFile(manifestF) + require.NoError(t, err) + + m := new(manifest.Manifest) + require.NoError(t, json.Unmarshal(rawM, m)) + + require.Nil(t, m.ABI.GetMethod("getDefault", 0)) + require.NotNil(t, m.ABI.GetMethod("get", 0)) + require.NotNil(t, m.ABI.GetMethod("get", 1)) + + require.Nil(t, m.ABI.GetMethod("putDefault", 1)) + require.NotNil(t, m.ABI.GetMethod("put", 1)) + require.NotNil(t, m.ABI.GetMethod("put", 2)) + } + e.Run(t, "neo-go", "contract", "generate-wrapper", + "--manifest", manifestF, + "--config", bindingF, + "--out", wrapperF, + "--hash", "0x00112233445566778899aabbccddeeff00112233") + e.Run(t, "neo-go", "contract", "generate-rpcwrapper", + "--manifest", manifestF, + "--config", bindingF, + "--out", rpcWrapperF, + "--hash", "0x00112233445566778899aabbccddeeff00112233") + }) + } + + t.Run("invalid manifest", func(t *testing.T) { + const dir = "./testdata/" + for _, name := range []string{"invalid1", "invalid2", "invalid3", "invalid4"} { + outF := filepath.Join(tmpDir, name+".nef") + manifestF := filepath.Join(tmpDir, name+".manifest.json") + e.RunWithError(t, "neo-go", "contract", "compile", + "--in", filepath.Join(dir, name), + "--out", outF, + "--manifest", manifestF, + "--config", filepath.Join(dir, name, "invalid.yml"), + ) + } + }) +} + +func filterFilename(infos []os.DirEntry, ext string) string { + for _, info := range infos { + if !info.IsDir() { + name := info.Name() + if strings.HasSuffix(name, ext) { + return name + } + } + } + return "" +} + +func TestContractCompile_NEFSizeCheck(t *testing.T) { + tmpDir := t.TempDir() + e := testcli.NewExecutor(t, false) + + src := `package nefconstraints + var data = "%s" + + func Main() string { + return data + }` + data := make([]byte, stackitem.MaxSize-10) + for i := range data { + data[i] = byte('a') + } + + in := filepath.Join(tmpDir, "main.go") + cfg := filepath.Join(tmpDir, "main.yml") + require.NoError(t, os.WriteFile(cfg, []byte("name: main"), os.ModePerm)) + require.NoError(t, os.WriteFile(in, []byte(fmt.Sprintf(src, data)), os.ModePerm)) + + e.RunWithError(t, "neo-go", "contract", "compile", "--in", in) + require.NoFileExists(t, filepath.Join(tmpDir, "main.nef")) +} diff --git a/cli/smartcontract/generate.go b/cli/smartcontract/generate.go new file mode 100644 index 0000000..be79a08 --- /dev/null +++ b/cli/smartcontract/generate.go @@ -0,0 +1,124 @@ +package smartcontract + +import ( + "bytes" + "fmt" + "os" + "strings" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" +) + +var generatorFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + Usage: `Configuration bindings file to use (*.yml). Configuration file is + generated by 'contract compile' command with --bindings flag`, + }, + &cli.StringFlag{ + Name: "manifest", + Aliases: []string{"m"}, + Required: true, + Usage: "Read contract manifest (*.manifest.json) file", + Action: cmdargs.EnsureNotEmpty("manifest"), + }, + &cli.StringFlag{ + Name: "out", + Aliases: []string{"o"}, + Required: true, + Usage: "Output of the compiled wrapper", + Action: cmdargs.EnsureNotEmpty("out"), + }, + &cli.StringFlag{ + Name: "hash", + Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage", + }, +} + +var generateWrapperCmd = &cli.Command{ + Name: "generate-wrapper", + Usage: "Generate wrapper to use in other contracts", + UsageText: "neo-go contract generate-wrapper --manifest --out [--hash ] [--config ]", + Description: `Generates a Go wrapper to use it in other smart contracts. If the + --hash flag is provided, CALLT instruction is used for the target contract + invocation as an optimization of the wrapper contract code. If omitted, the + generated wrapper will be designed for dynamic hash usage, allowing + the hash to be specified at runtime. +`, + Action: contractGenerateWrapper, + Flags: generatorFlags, +} + +var generateRPCWrapperCmd = &cli.Command{ + Name: "generate-rpcwrapper", + Usage: "Generate RPC wrapper to use for data reads", + UsageText: "neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]", + Action: contractGenerateRPCWrapper, + Flags: generatorFlags, +} + +func contractGenerateWrapper(ctx *cli.Context) error { + return contractGenerateSomething(ctx, binding.Generate) +} + +func contractGenerateRPCWrapper(ctx *cli.Context) error { + return contractGenerateSomething(ctx, rpcbinding.Generate) +} + +// contractGenerateSomething reads generator parameters and calls the given callback. +func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + var ( + h util.Uint160 + err error + ) + if hStr := ctx.String("hash"); len(hStr) != 0 { + h, err = util.Uint160DecodeStringLE(strings.TrimPrefix(hStr, "0x")) + if err != nil { + return cli.Exit(fmt.Errorf("invalid contract hash: %w", err), 1) + } + } + m, _, err := readManifest(ctx.String("manifest"), h) + if err != nil { + return cli.Exit(fmt.Errorf("can't read contract manifest: %w", err), 1) + } + + cfg := binding.NewConfig() + if cfgPath := ctx.String("config"); cfgPath != "" { + bs, err := os.ReadFile(cfgPath) + if err != nil { + return cli.Exit(fmt.Errorf("can't read config file: %w", err), 1) + } + decoder := yaml.NewDecoder(bytes.NewReader(bs)) + decoder.KnownFields(true) + + err = decoder.Decode(&cfg) + if err != nil { + return cli.Exit(fmt.Errorf("can't parse config file: %w", err), 1) + } + } + cfg.Manifest = m + cfg.Hash = h + + f, err := os.Create(ctx.String("out")) + if err != nil { + return cli.Exit(fmt.Errorf("can't create output file: %w", err), 1) + } + defer f.Close() + + cfg.Output = f + + err = cb(cfg) + if err != nil { + return cli.Exit(fmt.Errorf("error during generation: %w", err), 1) + } + return nil +} diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go new file mode 100644 index 0000000..3824f75 --- /dev/null +++ b/cli/smartcontract/generate_test.go @@ -0,0 +1,701 @@ +package smartcontract_test + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestGenerate(t *testing.T) { + m := manifest.NewManifest("MyContract") + m.ABI.Methods = append(m.ABI.Methods, + manifest.Method{ + Name: manifest.MethodDeploy, + ReturnType: smartcontract.VoidType, + }, + manifest.Method{ + Name: "sum", + Parameters: []manifest.Parameter{ + manifest.NewParameter("first", smartcontract.IntegerType), + manifest.NewParameter("second", smartcontract.IntegerType), + }, + ReturnType: smartcontract.IntegerType, + }, + manifest.Method{ + Name: "sum", // overloaded method + Parameters: []manifest.Parameter{ + manifest.NewParameter("first", smartcontract.IntegerType), + manifest.NewParameter("second", smartcontract.IntegerType), + manifest.NewParameter("third", smartcontract.IntegerType), + }, + ReturnType: smartcontract.IntegerType, + }, + manifest.Method{ + Name: "sum3", + Parameters: []manifest.Parameter{}, + ReturnType: smartcontract.IntegerType, + Safe: true, + }, + manifest.Method{ + Name: "zum", + Parameters: []manifest.Parameter{ + manifest.NewParameter("type", smartcontract.IntegerType), + manifest.NewParameter("typev", smartcontract.IntegerType), + manifest.NewParameter("func", smartcontract.IntegerType), + }, + ReturnType: smartcontract.IntegerType, + }, + manifest.Method{ + Name: "justExecute", + Parameters: []manifest.Parameter{ + manifest.NewParameter("arr", smartcontract.ArrayType), + }, + ReturnType: smartcontract.VoidType, + }, + manifest.Method{ + Name: "getPublicKey", + Parameters: nil, + ReturnType: smartcontract.PublicKeyType, + }, + manifest.Method{ + Name: "otherTypes", + Parameters: []manifest.Parameter{ + manifest.NewParameter("ctr", smartcontract.Hash160Type), + manifest.NewParameter("tx", smartcontract.Hash256Type), + manifest.NewParameter("sig", smartcontract.SignatureType), + manifest.NewParameter("data", smartcontract.AnyType), + }, + ReturnType: smartcontract.BoolType, + }, + manifest.Method{ + Name: "searchStorage", + Parameters: []manifest.Parameter{ + manifest.NewParameter("ctx", smartcontract.InteropInterfaceType), + }, + ReturnType: smartcontract.InteropInterfaceType, + }, + manifest.Method{ + Name: "getFromMap", + Parameters: []manifest.Parameter{ + manifest.NewParameter("intMap", smartcontract.MapType), + manifest.NewParameter("indices", smartcontract.ArrayType), + }, + ReturnType: smartcontract.ArrayType, + }, + manifest.Method{ + Name: "doSomething", + Parameters: []manifest.Parameter{ + manifest.NewParameter("bytes", smartcontract.ByteArrayType), + manifest.NewParameter("str", smartcontract.StringType), + }, + ReturnType: smartcontract.InteropInterfaceType, + }, + manifest.Method{ + Name: "getBlockWrapper", + Parameters: []manifest.Parameter{}, + ReturnType: smartcontract.InteropInterfaceType, + }, + manifest.Method{ + Name: "myFunc", + Parameters: []manifest.Parameter{ + manifest.NewParameter("in", smartcontract.MapType), + }, + ReturnType: smartcontract.ArrayType, + }) + + manifestFile := filepath.Join(t.TempDir(), "manifest.json") + outFile := filepath.Join(t.TempDir(), "out.go") + + rawManifest, err := json.Marshal(m) + require.NoError(t, err) + require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) + + h := util.Uint160{ + 0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01, + 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04, + } + e := testcli.NewExecutor(t, false) + + rawCfg := `package: wrapper +hash: ` + h.StringLE() + ` +overrides: + searchStorage.ctx: storage.Context + searchStorage: iterator.Iterator + getFromMap.intMap: "map[string]int" + getFromMap.indices: "[]string" + getFromMap: "[]int" + getBlockWrapper: ledger.Block + myFunc.in: "map[int]github.com/heyitsme/mycontract.Input" + myFunc: "[]github.com/heyitsme/mycontract.Output" +callflags: + doSomething: ReadStates +` + cfgPath := filepath.Join(t.TempDir(), "binding.yml") + require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm)) + + e.Run(t, []string{"", "contract", "generate-wrapper", + "--manifest", manifestFile, + "--config", cfgPath, + "--out", outFile, + "--hash", h.StringLE(), + }...) + + const expected = `// Code generated by neo-go contract generate-wrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package wrapper contains wrappers for MyContract contract. +package wrapper + +import ( + "github.com/heyitsme/mycontract" + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/iterator" + "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" + "github.com/nspcc-dev/neo-go/pkg/interop/storage" +) + +// Hash contains contract hash in big-endian form. +const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04" + +// Sum invokes ` + "`sum`" + ` method of contract. +func Sum(first int, second int) int { + return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second).(int) +} + +// Sum2 invokes ` + "`sum`" + ` method of contract. +func Sum2(first int, second int, third int) int { + return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second, third).(int) +} + +// Sum3 invokes ` + "`sum3`" + ` method of contract. +func Sum3() int { + return neogointernal.CallWithToken(Hash, "sum3", int(contract.ReadOnly)).(int) +} + +// Zum invokes ` + "`zum`" + ` method of contract. +func Zum(typev int, typev_ int, funcv int) int { + return neogointernal.CallWithToken(Hash, "zum", int(contract.All), typev, typev_, funcv).(int) +} + +// JustExecute invokes ` + "`justExecute`" + ` method of contract. +func JustExecute(arr []any) { + neogointernal.CallWithTokenNoRet(Hash, "justExecute", int(contract.All), arr) +} + +// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract. +func GetPublicKey() interop.PublicKey { + return neogointernal.CallWithToken(Hash, "getPublicKey", int(contract.All)).(interop.PublicKey) +} + +// OtherTypes invokes ` + "`otherTypes`" + ` method of contract. +func OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data any) bool { + return neogointernal.CallWithToken(Hash, "otherTypes", int(contract.All), ctr, tx, sig, data).(bool) +} + +// SearchStorage invokes ` + "`searchStorage`" + ` method of contract. +func SearchStorage(ctx storage.Context) iterator.Iterator { + return neogointernal.CallWithToken(Hash, "searchStorage", int(contract.All), ctx).(iterator.Iterator) +} + +// GetFromMap invokes ` + "`getFromMap`" + ` method of contract. +func GetFromMap(intMap map[string]int, indices []string) []int { + return neogointernal.CallWithToken(Hash, "getFromMap", int(contract.All), intMap, indices).([]int) +} + +// DoSomething invokes ` + "`doSomething`" + ` method of contract. +func DoSomething(bytes []byte, str string) any { + return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(any) +} + +// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract. +func GetBlockWrapper() ledger.Block { + return neogointernal.CallWithToken(Hash, "getBlockWrapper", int(contract.All)).(ledger.Block) +} + +// MyFunc invokes ` + "`myFunc`" + ` method of contract. +func MyFunc(in map[int]mycontract.Input) []mycontract.Output { + return neogointernal.CallWithToken(Hash, "myFunc", int(contract.All), in).([]mycontract.Output) +} +` + + data, err := os.ReadFile(outFile) + require.NoError(t, err) + require.Equal(t, expected, string(data)) + + e.Run(t, []string{"", "contract", "generate-wrapper", + "--manifest", manifestFile, + "--config", cfgPath, + "--out", outFile, + }...) + expectedWithDynamicHash := `// Code generated by neo-go contract generate-wrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package wrapper contains wrappers for MyContract contract. +package wrapper + +import ( + "github.com/heyitsme/mycontract" + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/iterator" + "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" + "github.com/nspcc-dev/neo-go/pkg/interop/storage" +) + +// Contract represents the MyContract smart contract. +type Contract struct { + Hash interop.Hash160 +} + +// NewContract returns a new Contract instance with the specified hash. +func NewContract(hash interop.Hash160) Contract { + return Contract{Hash: hash} +} + +// Sum invokes ` + "`sum`" + ` method of contract. +func (c Contract) Sum(first int, second int) int { + return contract.Call(c.Hash, "sum", contract.All, first, second).(int) +} + +// Sum2 invokes ` + "`sum`" + ` method of contract. +func (c Contract) Sum2(first int, second int, third int) int { + return contract.Call(c.Hash, "sum", contract.All, first, second, third).(int) +} + +// Sum3 invokes ` + "`sum3`" + ` method of contract. +func (c Contract) Sum3() int { + return contract.Call(c.Hash, "sum3", contract.ReadOnly).(int) +} + +// Zum invokes ` + "`zum`" + ` method of contract. +func (c Contract) Zum(typev int, typev_ int, funcv int) int { + return contract.Call(c.Hash, "zum", contract.All, typev, typev_, funcv).(int) +} + +// JustExecute invokes ` + "`justExecute`" + ` method of contract. +func (c Contract) JustExecute(arr []any) { + contract.Call(c.Hash, "justExecute", contract.All, arr) +} + +// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract. +func (c Contract) GetPublicKey() interop.PublicKey { + return contract.Call(c.Hash, "getPublicKey", contract.All).(interop.PublicKey) +} + +// OtherTypes invokes ` + "`otherTypes`" + ` method of contract. +func (c Contract) OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data any) bool { + return contract.Call(c.Hash, "otherTypes", contract.All, ctr, tx, sig, data).(bool) +} + +// SearchStorage invokes ` + "`searchStorage`" + ` method of contract. +func (c Contract) SearchStorage(ctx storage.Context) iterator.Iterator { + return contract.Call(c.Hash, "searchStorage", contract.All, ctx).(iterator.Iterator) +} + +// GetFromMap invokes ` + "`getFromMap`" + ` method of contract. +func (c Contract) GetFromMap(intMap map[string]int, indices []string) []int { + return contract.Call(c.Hash, "getFromMap", contract.All, intMap, indices).([]int) +} + +// DoSomething invokes ` + "`doSomething`" + ` method of contract. +func (c Contract) DoSomething(bytes []byte, str string) any { + return contract.Call(c.Hash, "doSomething", contract.ReadStates, bytes, str).(any) +} + +// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract. +func (c Contract) GetBlockWrapper() ledger.Block { + return contract.Call(c.Hash, "getBlockWrapper", contract.All).(ledger.Block) +} + +// MyFunc invokes ` + "`myFunc`" + ` method of contract. +func (c Contract) MyFunc(in map[int]mycontract.Input) []mycontract.Output { + return contract.Call(c.Hash, "myFunc", contract.All, in).([]mycontract.Output) +} +` + data, err = os.ReadFile(outFile) + require.NoError(t, err) + require.Equal(t, expectedWithDynamicHash, string(data)) +} + +func TestGenerateValidPackageName(t *testing.T) { + m := manifest.NewManifest("My space\tcontract") + m.ABI.Methods = append(m.ABI.Methods, + manifest.Method{ + Name: "get", + Parameters: []manifest.Parameter{}, + ReturnType: smartcontract.IntegerType, + Safe: true, + }, + ) + + manifestFile := filepath.Join(t.TempDir(), "manifest.json") + outFile := filepath.Join(t.TempDir(), "out.go") + + rawManifest, err := json.Marshal(m) + require.NoError(t, err) + require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) + + h := util.Uint160{ + 0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01, + 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04, + } + e := testcli.NewExecutor(t, false) + e.Run(t, []string{"", "contract", "generate-wrapper", + "--manifest", manifestFile, + "--out", outFile, + "--hash", "0x" + h.StringLE(), + }...) + + data, err := os.ReadFile(outFile) + require.NoError(t, err) + require.Equal(t, `// Code generated by neo-go contract generate-wrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package myspacecontract contains wrappers for My space contract contract. +package myspacecontract + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" +) + +// Hash contains contract hash in big-endian form. +const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04" + +// Get invokes `+"`get`"+` method of contract. +func Get() int { + return neogointernal.CallWithToken(Hash, "get", int(contract.ReadOnly)).(int) +} +`, string(data)) + e.Run(t, []string{"", "contract", "generate-rpcwrapper", + "--manifest", manifestFile, + "--out", outFile, + "--hash", "0x" + h.StringLE(), + }...) + + data, err = os.ReadFile(outFile) + require.NoError(t, err) + require.Equal(t, `// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package myspacecontract contains RPC wrappers for My space contract contract. +package myspacecontract + +import ( + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/util" + "math/big" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x0, 0x1, 0xca, 0xfe, 0xba, 0xbe, 0xde, 0xad, 0xbe, 0xef, 0x3, 0x4} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + invoker Invoker + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{invoker, hash} +} + +// Get invokes `+"`get`"+` method of contract. +func (c *ContractReader) Get() (*big.Int, error) { + return unwrap.BigInt(c.invoker.Call(c.hash, "get")) +} +`, string(data)) +} + +// rewriteExpectedOutputs denotes whether expected output files should be rewritten +// for TestGenerateRPCBindings and TestAssistedRPCBindings. +const rewriteExpectedOutputs = false + +func TestGenerateRPCBindings(t *testing.T) { + tmpDir := t.TempDir() + e := testcli.NewExecutor(t, false) + + var checkBinding = func(manifest string, hash string, good string) { + t.Run(manifest, func(t *testing.T) { + outFile := filepath.Join(tmpDir, "out.go") + e.Run(t, []string{"", "contract", "generate-rpcwrapper", + "--manifest", manifest, + "--out", outFile, + "--hash", hash, + }...) + + data, err := os.ReadFile(outFile) + require.NoError(t, err) + data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. + if rewriteExpectedOutputs { + require.NoError(t, os.WriteFile(good, data, os.ModePerm)) + } else { + expected, err := os.ReadFile(good) + require.NoError(t, err) + expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. + require.Equal(t, string(expected), string(data)) + } + }) + } + + checkBinding(filepath.Join("testdata", "nex", "nex.manifest.json"), + "0xa2a67f09e8cf22c6bfd5cea24adc0f4bf0a11aa8", + filepath.Join("testdata", "nex", "nex.go")) + checkBinding(filepath.Join("testdata", "nameservice", "nns.manifest.json"), + "0x50ac1c37690cc2cfc594472833cf57505d5f46de", + filepath.Join("testdata", "nameservice", "nns.go")) + checkBinding(filepath.Join("testdata", "gas", "gas.manifest.json"), + "0xd2a4cff31913016155e38e474a2c06d08be276cf", + filepath.Join("testdata", "gas", "gas.go")) + checkBinding(filepath.Join("testdata", "verifyrpc", "verify.manifest.json"), + "0x00112233445566778899aabbccddeeff00112233", + filepath.Join("rpcbindings", "verify", "rpcbindings_test.go")) + checkBinding(filepath.Join("testdata", "nonepiter", "iter.manifest.json"), + "0x00112233445566778899aabbccddeeff00112233", + filepath.Join("testdata", "nonepiter", "iter.go")) + + require.False(t, rewriteExpectedOutputs) +} + +func TestAssistedRPCBindings(t *testing.T) { + tmpDir := t.TempDir() + e := testcli.NewExecutor(t, false) + + var checkBinding = func(source, configFile, expectedFile string, hasDefinedHash, guessEventTypes bool, suffix ...string) { + testName := source + if len(suffix) != 0 { + testName += suffix[0] + } + testName += fmt.Sprintf(", predefined hash: %t", hasDefinedHash) + t.Run(testName, func(t *testing.T) { + outFile := filepath.Join(tmpDir, "out.go") + if configFile == "" { + if len(suffix) != 0 { + configFile = filepath.Join(source, "config_"+suffix[0]+".yml") + } else { + configFile = filepath.Join(source, "config.yml") + } + } + if expectedFile == "" { + // Expected bindings are moved out of testdata to be able to perform static code analysis on them. + expectedRoot := strings.Replace(source, "testdata", ".", 1) + expectedFile = filepath.Join(expectedRoot, "rpcbindings", "rpcbindings_test.go") + if len(suffix) != 0 { + expectedFile = filepath.Join(expectedRoot, "rpcbindings", suffix[0], "rpcbindings_test.go") + } else if !hasDefinedHash { + expectedFile = filepath.Join(expectedRoot, "rpcbindings", "dynamic_hash", "rpcbindings_test.go") + } + } + manifestF := filepath.Join(tmpDir, "manifest.json") + bindingF := filepath.Join(tmpDir, "binding.yml") + nefF := filepath.Join(tmpDir, "out.nef") + cmd := []string{"", "contract", "compile", + "--in", source, + "--config", configFile, + "--manifest", manifestF, + "--bindings", bindingF, + "--out", nefF, + } + if guessEventTypes { + cmd = append(cmd, "--guess-eventtypes") + } + e.Run(t, cmd...) + + cmds := []string{"", "contract", "generate-rpcwrapper", + "--config", bindingF, + "--manifest", manifestF, + "--out", outFile, + } + if hasDefinedHash { + cmds = append(cmds, "--hash", "0x00112233445566778899aabbccddeeff00112233") + } + e.Run(t, cmds...) + + data, err := os.ReadFile(outFile) + require.NoError(t, err) + data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. + if rewriteExpectedOutputs { + require.NoError(t, os.WriteFile(expectedFile, data, os.ModePerm)) + } else { + expected, err := os.ReadFile(expectedFile) + require.NoError(t, err) + expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. + require.Equal(t, string(expected), string(data)) + } + }) + } + + for _, hasDefinedHash := range []bool{true, false} { + checkBinding(filepath.Join("testdata", "rpcbindings", "types"), "", "", hasDefinedHash, false) + checkBinding(filepath.Join("testdata", "rpcbindings", "structs"), "", "", hasDefinedHash, false) + checkBinding(filepath.Join("testdata", "rpcbindings", "royalty"), "", "", hasDefinedHash, false) + } + checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, false) + checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, false, "extended") + checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, true, "guessed") + + checkBinding(filepath.Join("..", "..", "examples", "nft-d"), filepath.Join("..", "..", "examples", "nft-d", "nft.yml"), filepath.Join("rpcbindings", "nft-d", "dynamic_hash", "rpcbindings_test.go"), false, false) + checkBinding(filepath.Join("..", "..", "examples", "nft-d"), filepath.Join("..", "..", "examples", "nft-d", "nft.yml"), filepath.Join("rpcbindings", "nft-d", "rpcbindings_test.go"), true, true) + checkBinding(filepath.Join("..", "..", "examples", "nft-nd"), filepath.Join("..", "..", "examples", "nft-nd", "nft.yml"), filepath.Join("rpcbindings", "nft-nd", "dynamic_hash", "rpcbindings_test.go"), false, false) + checkBinding(filepath.Join("..", "..", "examples", "nft-nd"), filepath.Join("..", "..", "examples", "nft-nd", "nft.yml"), filepath.Join("rpcbindings", "nft-nd", "rpcbindings_test.go"), true, true) + + require.False(t, rewriteExpectedOutputs) +} + +func TestGenerate_Errors(t *testing.T) { + e := testcli.NewExecutor(t, false) + args := []string{"neo-go", "contract", "generate-wrapper"} + + t.Run("invalid hash", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "invalid contract hash", append(args, "--hash", "xxx", "--manifest", "yyy", "--out", "zzz")...) + }) + t.Run("missing manifest argument", func(t *testing.T) { + e.RunWithErrorCheck(t, `Required flag "manifest" not set`, append(args, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...) + }) + t.Run("missing manifest file", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "can't read contract manifest", append(args, "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...) + }) + t.Run("empty manifest", func(t *testing.T) { + manifestFile := filepath.Join(t.TempDir(), "invalid.json") + require.NoError(t, os.WriteFile(manifestFile, []byte("[]"), os.ModePerm)) + e.RunWithErrorCheckExit(t, "json: cannot unmarshal array into Go value of type manifest.Manifest", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...) + }) + t.Run("invalid manifest", func(t *testing.T) { + manifestFile := filepath.Join(t.TempDir(), "invalid.json") + m := manifest.NewManifest("MyContract") // no methods + rawManifest, err := json.Marshal(m) + require.NoError(t, err) + require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) + e.RunWithErrorCheckExit(t, "ABI: no methods", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...) + }) + + manifestFile := filepath.Join(t.TempDir(), "manifest.json") + m := manifest.NewManifest("MyContract") + m.ABI.Methods = append(m.ABI.Methods, manifest.Method{ + Name: "method0", + Offset: 0, + ReturnType: smartcontract.AnyType, + Safe: true, + }) + rawManifest, err := json.Marshal(m) + require.NoError(t, err) + require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm)) + + t.Run("missing config", func(t *testing.T) { + e.RunWithErrorCheckExit(t, "can't read config file", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), + "--config", filepath.Join(t.TempDir(), "not.exists.yml"), "--out", "zzz")...) + }) + t.Run("invalid config", func(t *testing.T) { + rawCfg := `package: wrapper +callflags: + someFunc: ReadSometimes +` + cfgPath := filepath.Join(t.TempDir(), "binding.yml") + require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm)) + + e.RunWithErrorCheckExit(t, "can't parse config file", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), + "--config", cfgPath, "--out", "zzz")...) + }) +} + +func TestCompile_GuessEventTypes(t *testing.T) { + e := testcli.NewExecutor(t, false) + check := func(t *testing.T, source string, expectedErrText string) { + tmpDir := t.TempDir() + configFile := filepath.Join(source, "invalid.yml") + manifestF := filepath.Join(tmpDir, "invalid.manifest.json") + bindingF := filepath.Join(tmpDir, "invalid.binding.yml") + nefF := filepath.Join(tmpDir, "invalid.out.nef") + cmd := []string{"", "contract", "compile", + "--in", source, + "--config", configFile, + "--manifest", manifestF, + "--bindings", bindingF, + "--out", nefF, + "--guess-eventtypes", + } + e.RunWithErrorCheckExit(t, expectedErrText, cmd...) + } + + t.Run("not declared in manifest", func(t *testing.T) { + check(t, filepath.Join("testdata", "rpcbindings", "invalid1"), "inconsistent usages of event `Non declared event`: not declared in the contract config") + }) + t.Run("invalid number of params", func(t *testing.T) { + check(t, filepath.Join("testdata", "rpcbindings", "invalid2"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1") + }) + /* + // TODO: this on is a controversial one. If event information is provided in the config file, then conversion code + // will be emitted by the compiler according to the parameter type provided via config. Thus, we can be sure that + // either event parameter has the type specified in the config file or the execution of the contract will fail. + // Thus, this testcase is always failing (no compilation error occurs). + // Question: do we want to compare `RealType` of the emitted parameter with the one expected in the manifest? + t.Run("SC parameter type mismatch", func(t *testing.T) { + check(t, filepath.Join("testdata", "rpcbindings", "invalid3"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1") + }) + */ + t.Run("extended types mismatch", func(t *testing.T) { + check(t, filepath.Join("testdata", "rpcbindings", "invalid4"), "inconsistent usages of event `SomeEvent`: extended type of param #0 mismatch") + }) + t.Run("named types redeclare", func(t *testing.T) { + check(t, filepath.Join("testdata", "rpcbindings", "invalid5"), "configured declared named type intersects with the contract's one: `invalid5.NamedStruct`") + }) +} + +func TestGenerateRPCBindings_Errors(t *testing.T) { + e := testcli.NewExecutor(t, false) + t.Run("duplicating resulting fields", func(t *testing.T) { + check := func(t *testing.T, packageName string, autogen bool, expectedError string) { + tmpDir := t.TempDir() + source := filepath.Join("testdata", "rpcbindings", packageName) + configFile := filepath.Join(source, "invalid.yml") + out := filepath.Join(tmpDir, "rpcbindings.out") + manifestF := filepath.Join(tmpDir, "manifest.json") + bindingF := filepath.Join(tmpDir, "binding.yml") + nefF := filepath.Join(tmpDir, "out.nef") + cmd := []string{"", "contract", "compile", + "--in", source, + "--config", configFile, + "--manifest", manifestF, + "--bindings", bindingF, + "--out", nefF, + } + if autogen { + cmd = append(cmd, "--guess-eventtypes") + } + e.Run(t, cmd...) + + cmds := []string{"", "contract", "generate-rpcwrapper", + "--config", bindingF, + "--manifest", manifestF, + "--out", out, + } + e.RunWithErrorCheckExit(t, expectedError, cmds...) + } + + t.Run("event", func(t *testing.T) { + check(t, "invalid6", false, "error during generation: named type `SomeStruct` has two fields with identical resulting binding name `Field`") + }) + t.Run("autogen event", func(t *testing.T) { + check(t, "invalid7", true, "error during generation: named type `invalid7.SomeStruct` has two fields with identical resulting binding name `Field`") + }) + t.Run("struct", func(t *testing.T) { + check(t, "invalid8", false, "error during generation: named type `invalid8.SomeStruct` has two fields with identical resulting binding name `Field`") + }) + }) +} diff --git a/cli/smartcontract/manifest.go b/cli/smartcontract/manifest.go new file mode 100644 index 0000000..8d65fcc --- /dev/null +++ b/cli/smartcontract/manifest.go @@ -0,0 +1,104 @@ +package smartcontract + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/urfave/cli/v2" +) + +func manifestAddGroup(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + sender := ctx.Generic("sender").(*flags.Address) + nf, _, err := readNEFFile(ctx.String("nef")) + if err != nil { + return cli.Exit(fmt.Errorf("can't read NEF file: %w", err), 1) + } + + mPath := ctx.String("manifest") + m, _, err := readManifest(mPath, util.Uint160{}) + if err != nil { + return cli.Exit(fmt.Errorf("can't read contract manifest: %w", err), 1) + } + + h := state.CreateContractHash(sender.Uint160(), nf.Checksum, m.Name) + + gAcc, w, err := options.GetAccFromContext(ctx) + if err != nil { + return cli.Exit(fmt.Errorf("can't get account to sign group with: %w", err), 1) + } + defer w.Close() + + var found bool + + sig := gAcc.PrivateKey().Sign(h.BytesBE()) + pub := gAcc.PublicKey() + for i := range m.Groups { + if m.Groups[i].PublicKey.Equal(pub) { + m.Groups[i].Signature = sig + found = true + break + } + } + if !found { + m.Groups = append(m.Groups, manifest.Group{ + PublicKey: pub, + Signature: sig, + }) + } + + rawM, err := json.Marshal(m) + if err != nil { + return cli.Exit(fmt.Errorf("can't marshal manifest: %w", err), 1) + } + + err = os.WriteFile(mPath, rawM, os.ModePerm) + if err != nil { + return cli.Exit(fmt.Errorf("can't write manifest file: %w", err), 1) + } + return nil +} + +func readNEFFile(filename string) (*nef.File, []byte, error) { + f, err := os.ReadFile(filename) + if err != nil { + return nil, nil, err + } + + nefFile, err := nef.FileFromBytes(f) + if err != nil { + return nil, nil, fmt.Errorf("can't parse NEF file: %w", err) + } + + return &nefFile, f, nil +} + +// readManifest unmarshalls manifest got from the provided filename and checks +// it for validness against the provided contract hash. If empty hash is specified +// then no hash-related manifest groups check is performed. +func readManifest(filename string, hash util.Uint160) (*manifest.Manifest, []byte, error) { + manifestBytes, err := os.ReadFile(filename) + if err != nil { + return nil, nil, err + } + + m := new(manifest.Manifest) + err = json.Unmarshal(manifestBytes, m) + if err != nil { + return nil, nil, err + } + if err := m.IsValid(hash, true); err != nil { + return nil, nil, fmt.Errorf("manifest is invalid: %w", err) + } + return m, manifestBytes, nil +} diff --git a/cli/smartcontract/permission.go b/cli/smartcontract/permission.go new file mode 100644 index 0000000..9f10edf --- /dev/null +++ b/cli/smartcontract/permission.go @@ -0,0 +1,130 @@ +package smartcontract + +import ( + "errors" + "fmt" + + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/util" + "gopkg.in/yaml.v3" +) + +type permission manifest.Permission + +const ( + permHashKey = "hash" + permGroupKey = "group" + permMethodKey = "methods" +) + +func (p permission) MarshalYAML() (any, error) { + m := yaml.Node{Kind: yaml.MappingNode} + switch p.Contract.Type { + case manifest.PermissionWildcard: + case manifest.PermissionHash: + m.Content = append(m.Content, + &yaml.Node{Kind: yaml.ScalarNode, Value: permHashKey}, + &yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(util.Uint160).StringLE()}) + case manifest.PermissionGroup: + m.Content = append(m.Content, + &yaml.Node{Kind: yaml.ScalarNode, Value: permGroupKey}, + &yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(*keys.PublicKey).StringCompressed()}) + default: + return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type) + } + + var val any = "*" + if !p.Methods.IsWildcard() { + val = p.Methods.Value + } + + n := &yaml.Node{Kind: yaml.ScalarNode} + err := n.Encode(val) + if err != nil { + return nil, err + } + m.Content = append(m.Content, + &yaml.Node{Kind: yaml.ScalarNode, Value: permMethodKey}, + n) + + return m, nil +} + +func (p *permission) UnmarshalYAML(node *yaml.Node) error { + var m map[string]any + if err := node.Decode(&m); err != nil { + return err + } + + if err := p.fillType(m); err != nil { + return err + } + + return p.fillMethods(m) +} + +func (p *permission) fillType(m map[string]any) error { + vh, ok1 := m[permHashKey] + vg, ok2 := m[permGroupKey] + switch { + case ok1 && ok2: + return errors.New("permission must have either 'hash' or 'group' field") + case ok1: + s, ok := vh.(string) + if !ok { + return errors.New("invalid 'hash' type") + } + + u, err := util.Uint160DecodeStringLE(s) + if err != nil { + return err + } + + p.Contract.Type = manifest.PermissionHash + p.Contract.Value = u + case ok2: + s, ok := vg.(string) + if !ok { + return errors.New("invalid 'hash' type") + } + + pub, err := keys.NewPublicKeyFromString(s) + if err != nil { + return err + } + + p.Contract.Type = manifest.PermissionGroup + p.Contract.Value = pub + default: + p.Contract.Type = manifest.PermissionWildcard + } + return nil +} + +func (p *permission) fillMethods(m map[string]any) error { + methods, ok := m[permMethodKey] + if !ok { + return errors.New("'methods' field is missing from permission") + } + + switch mt := methods.(type) { + case string: + if mt == "*" { + p.Methods.Value = nil + return nil + } + case []any: + ms := make([]string, len(mt)) + for i := range mt { + ms[i], ok = mt[i].(string) + if !ok { + return errors.New("invalid permission method name") + } + } + p.Methods.Value = ms + return nil + default: + } + return errors.New("'methods' field is invalid") +} diff --git a/cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go new file mode 100644 index 0000000..01b8de7 --- /dev/null +++ b/cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go @@ -0,0 +1,60 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nft contains RPC wrappers for NeoFS Object NFT contract. +package nft + +import ( + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep22" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep31" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// NEP22Contract is an alias for nep22.Contract. +type NEP22Contract = nep22.Contract + +// NEP31Contract is an alias for nep31.Contract. +type NEP31Contract = nep31.Contract + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep11.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep11.Actor +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep11.DivisibleReader + nep24.RoyaltyReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep11.DivisibleWriter + NEP22Contract + NEP31Contract + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. +func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { + return &ContractReader{*nep11.NewDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using provided contract hash and the given Actor. +func New(actor Actor, hash util.Uint160) *Contract { + var nep11dt = nep11.NewDivisible(actor, hash) + var nep24t = nep24.NewRoyaltyReader(actor, hash) + return &Contract{ContractReader{nep11dt.DivisibleReader, *nep24t, actor, hash}, nep11dt.DivisibleWriter, NEP22Contract(*nep22.NewContract(actor, hash)), NEP31Contract(*nep31.NewContract(actor, hash)), actor, hash} +} diff --git a/cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go b/cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go new file mode 100644 index 0000000..d6bac79 --- /dev/null +++ b/cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go @@ -0,0 +1,65 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nft contains RPC wrappers for NeoFS Object NFT contract. +package nft + +import ( + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep22" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep31" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// NEP22Contract is an alias for nep22.Contract. +type NEP22Contract = nep22.Contract + +// NEP31Contract is an alias for nep31.Contract. +type NEP31Contract = nep31.Contract + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep11.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep11.Actor +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep11.DivisibleReader + nep24.RoyaltyReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep11.DivisibleWriter + NEP22Contract + NEP31Contract + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{*nep11.NewDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + var nep11dt = nep11.NewDivisible(actor, hash) + var nep24t = nep24.NewRoyaltyReader(actor, hash) + return &Contract{ContractReader{nep11dt.DivisibleReader, *nep24t, actor, hash}, nep11dt.DivisibleWriter, NEP22Contract(*nep22.NewContract(actor, hash)), NEP31Contract(*nep31.NewContract(actor, hash)), actor, hash} +} diff --git a/cli/smartcontract/rpcbindings/nft-nd/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/nft-nd/dynamic_hash/rpcbindings_test.go new file mode 100644 index 0000000..d5bebf5 --- /dev/null +++ b/cli/smartcontract/rpcbindings/nft-nd/dynamic_hash/rpcbindings_test.go @@ -0,0 +1,234 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nft contains RPC wrappers for HASHY NFT contract. +package nft + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep22" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep31" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" +) + +// NEP22Contract is an alias for nep22.Contract. +type NEP22Contract = nep22.Contract + +// NEP31Contract is an alias for nep31.Contract. +type NEP31Contract = nep31.Contract + +// NftRoyaltyRecipientShare is a contract-specific nft.RoyaltyRecipientShare type used by its methods. +type NftRoyaltyRecipientShare struct { + Address util.Uint160 + Share *big.Int +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep11.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep11.Actor + + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep11.NonDivisibleReader + nep24.RoyaltyReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep11.BaseWriter + NEP22Contract + NEP31Contract + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. +func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { + return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using provided contract hash and the given Actor. +func New(actor Actor, hash util.Uint160) *Contract { + var nep11ndt = nep11.NewNonDivisible(actor, hash) + var nep24t = nep24.NewRoyaltyReader(actor, hash) + return &Contract{ContractReader{nep11ndt.NonDivisibleReader, *nep24t, actor, hash}, nep11ndt.BaseWriter, NEP22Contract(*nep22.NewContract(actor, hash)), NEP31Contract(*nep31.NewContract(actor, hash)), actor, hash} +} + +func (c *Contract) scriptForSetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) ([]byte, error) { + return smartcontract.CreateCallWithAssertScript(c.hash, "setRoyaltyInfo", ctx, tokenID, recipients) +} + +// SetRoyaltyInfo creates a transaction invoking `setRoyaltyInfo` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) SetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (util.Uint256, uint32, error) { + script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients) + if err != nil { + return util.Uint256{}, 0, err + } + return c.actor.SendRun(script) +} + +// SetRoyaltyInfoTransaction creates a transaction invoking `setRoyaltyInfo` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) SetRoyaltyInfoTransaction(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) { + script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients) + if err != nil { + return nil, err + } + return c.actor.MakeRun(script) +} + +// SetRoyaltyInfoUnsigned creates a transaction invoking `setRoyaltyInfo` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) SetRoyaltyInfoUnsigned(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) { + script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients) + if err != nil { + return nil, err + } + return c.actor.MakeUnsignedRun(script, nil) +} + +// itemToNftRoyaltyRecipientShare converts stack item into *NftRoyaltyRecipientShare. +// NULL item is returned as nil pointer without error. +func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyaltyRecipientShare, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(NftRoyaltyRecipientShare) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *NftRoyaltyRecipientShare is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&NftRoyaltyRecipientShare{}) + +// Ensure *NftRoyaltyRecipientShare is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&NftRoyaltyRecipientShare{}) + +// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Address, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Address: %w", err) + } + + index++ + res.Share, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Share: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing NftRoyaltyRecipientShare. +// It implements [stackitem.Convertible] interface. +func (res *NftRoyaltyRecipientShare) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray(res.Address.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Address: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Share), error(nil) + if err != nil { + return nil, fmt.Errorf("field Share: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing NftRoyaltyRecipientShare. +// It implements [smartcontract.Convertible] interface so that NftRoyaltyRecipientShare +// could be used with invokers. +func (res *NftRoyaltyRecipientShare) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Address) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Address: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Share) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Share: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} diff --git a/cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go b/cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go new file mode 100644 index 0000000..a22211c --- /dev/null +++ b/cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go @@ -0,0 +1,239 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nft contains RPC wrappers for HASHY NFT contract. +package nft + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep22" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep31" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// NEP22Contract is an alias for nep22.Contract. +type NEP22Contract = nep22.Contract + +// NEP31Contract is an alias for nep31.Contract. +type NEP31Contract = nep31.Contract + +// NftRoyaltyRecipientShare is a contract-specific nft.RoyaltyRecipientShare type used by its methods. +type NftRoyaltyRecipientShare struct { + Address util.Uint160 + Share *big.Int +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep11.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep11.Actor + + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep11.NonDivisibleReader + nep24.RoyaltyReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep11.BaseWriter + NEP22Contract + NEP31Contract + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + var nep11ndt = nep11.NewNonDivisible(actor, hash) + var nep24t = nep24.NewRoyaltyReader(actor, hash) + return &Contract{ContractReader{nep11ndt.NonDivisibleReader, *nep24t, actor, hash}, nep11ndt.BaseWriter, NEP22Contract(*nep22.NewContract(actor, hash)), NEP31Contract(*nep31.NewContract(actor, hash)), actor, hash} +} + +func (c *Contract) scriptForSetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) ([]byte, error) { + return smartcontract.CreateCallWithAssertScript(c.hash, "setRoyaltyInfo", ctx, tokenID, recipients) +} + +// SetRoyaltyInfo creates a transaction invoking `setRoyaltyInfo` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) SetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (util.Uint256, uint32, error) { + script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients) + if err != nil { + return util.Uint256{}, 0, err + } + return c.actor.SendRun(script) +} + +// SetRoyaltyInfoTransaction creates a transaction invoking `setRoyaltyInfo` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) SetRoyaltyInfoTransaction(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) { + script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients) + if err != nil { + return nil, err + } + return c.actor.MakeRun(script) +} + +// SetRoyaltyInfoUnsigned creates a transaction invoking `setRoyaltyInfo` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) SetRoyaltyInfoUnsigned(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) { + script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients) + if err != nil { + return nil, err + } + return c.actor.MakeUnsignedRun(script, nil) +} + +// itemToNftRoyaltyRecipientShare converts stack item into *NftRoyaltyRecipientShare. +// NULL item is returned as nil pointer without error. +func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyaltyRecipientShare, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(NftRoyaltyRecipientShare) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *NftRoyaltyRecipientShare is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&NftRoyaltyRecipientShare{}) + +// Ensure *NftRoyaltyRecipientShare is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&NftRoyaltyRecipientShare{}) + +// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Address, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Address: %w", err) + } + + index++ + res.Share, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Share: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing NftRoyaltyRecipientShare. +// It implements [stackitem.Convertible] interface. +func (res *NftRoyaltyRecipientShare) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray(res.Address.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Address: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Share), error(nil) + if err != nil { + return nil, fmt.Errorf("field Share: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing NftRoyaltyRecipientShare. +// It implements [smartcontract.Convertible] interface so that NftRoyaltyRecipientShare +// could be used with invokers. +func (res *NftRoyaltyRecipientShare) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Address) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Address: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Share) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Share: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} diff --git a/cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go b/cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go new file mode 100755 index 0000000..c0b255a --- /dev/null +++ b/cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go @@ -0,0 +1,746 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// CrazyStruct is a contract-specific crazyStruct type used by its methods. +type CrazyStruct struct { + I *big.Int + B bool +} + +// SimpleStruct is a contract-specific simpleStruct type used by its methods. +type SimpleStruct struct { + I *big.Int +} + +// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract. +type ComplicatedNameEvent struct { + ComplicatedParam string +} + +// SomeMapEvent represents "SomeMap" event emitted by the contract. +type SomeMapEvent struct { + M map[*big.Int]map[string][]util.Uint160 +} + +// SomeStructEvent represents "SomeStruct" event emitted by the contract. +type SomeStructEvent struct { + S *CrazyStruct +} + +// SomeArrayEvent represents "SomeArray" event emitted by the contract. +type SomeArrayEvent struct { + A [][]*big.Int +} + +// SomeUnexportedFieldEvent represents "SomeUnexportedField" event emitted by the contract. +type SomeUnexportedFieldEvent struct { + S *SimpleStruct +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor + hash util.Uint160 +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + return &Contract{actor, hash} +} + +// Array creates a transaction invoking `array` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Array() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "array") +} + +// ArrayTransaction creates a transaction invoking `array` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) ArrayTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "array") +} + +// ArrayUnsigned creates a transaction invoking `array` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) ArrayUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "array", nil) +} + +// CrazyMap creates a transaction invoking `crazyMap` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) CrazyMap() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "crazyMap") +} + +// CrazyMapTransaction creates a transaction invoking `crazyMap` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) CrazyMapTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "crazyMap") +} + +// CrazyMapUnsigned creates a transaction invoking `crazyMap` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) CrazyMapUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "crazyMap", nil) +} + +// Main creates a transaction invoking `main` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Main() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "main") +} + +// MainTransaction creates a transaction invoking `main` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) MainTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "main") +} + +// MainUnsigned creates a transaction invoking `main` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) MainUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "main", nil) +} + +// Struct creates a transaction invoking `struct` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Struct() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "struct") +} + +// StructTransaction creates a transaction invoking `struct` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) StructTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "struct") +} + +// StructUnsigned creates a transaction invoking `struct` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) StructUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "struct", nil) +} + +// UnexportedField creates a transaction invoking `unexportedField` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) UnexportedField() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "unexportedField") +} + +// UnexportedFieldTransaction creates a transaction invoking `unexportedField` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) UnexportedFieldTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "unexportedField") +} + +// UnexportedFieldUnsigned creates a transaction invoking `unexportedField` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "unexportedField", nil) +} + +// itemToCrazyStruct converts stack item into *CrazyStruct. +// NULL item is returned as nil pointer without error. +func itemToCrazyStruct(item stackitem.Item, err error) (*CrazyStruct, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(CrazyStruct) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *CrazyStruct is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&CrazyStruct{}) + +// Ensure *CrazyStruct is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&CrazyStruct{}) + +// FromStackItem retrieves fields of CrazyStruct from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *CrazyStruct) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + index++ + res.B, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field B: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing CrazyStruct. +// It implements [stackitem.Convertible] interface. +func (res *CrazyStruct) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewBool(res.B), error(nil) + if err != nil { + return nil, fmt.Errorf("field B: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing CrazyStruct. +// It implements [smartcontract.Convertible] interface so that CrazyStruct +// could be used with invokers. +func (res *CrazyStruct) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.B) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToSimpleStruct converts stack item into *SimpleStruct. +// NULL item is returned as nil pointer without error. +func itemToSimpleStruct(item stackitem.Item, err error) (*SimpleStruct, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(SimpleStruct) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *SimpleStruct is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&SimpleStruct{}) + +// Ensure *SimpleStruct is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&SimpleStruct{}) + +// FromStackItem retrieves fields of SimpleStruct from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *SimpleStruct) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing SimpleStruct. +// It implements [stackitem.Convertible] interface. +func (res *SimpleStruct) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 1) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing SimpleStruct. +// It implements [smartcontract.Convertible] interface so that SimpleStruct +// could be used with invokers. +func (res *SimpleStruct) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 1) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events +// with "! complicated name %$#" name from the provided [result.ApplicationLog]. +func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*ComplicatedNameEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "! complicated name %$#" { + continue + } + event := new(ComplicatedNameEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or +// returns an error if it's not possible to do to so. +func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.ComplicatedParam, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field ComplicatedParam: %w", err) + } + + return nil +} + +// SomeMapEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeMap" name from the provided [result.ApplicationLog]. +func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeMapEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeMap" { + continue + } + event := new(SomeMapEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or +// returns an error if it's not possible to do to so. +func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.M, err = func(item stackitem.Item) (map[*big.Int]map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int]map[string][]util.Uint160) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) (map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string][]util.Uint160) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field M: %w", err) + } + + return nil +} + +// SomeStructEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeStruct" name from the provided [result.ApplicationLog]. +func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeStructEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeStruct" { + continue + } + event := new(SomeStructEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or +// returns an error if it's not possible to do to so. +func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.S, err = itemToCrazyStruct(arr[index], nil) + if err != nil { + return fmt.Errorf("field S: %w", err) + } + + return nil +} + +// SomeArrayEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeArray" name from the provided [result.ApplicationLog]. +func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeArrayEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeArray" { + continue + } + event := new(SomeArrayEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or +// returns an error if it's not possible to do to so. +func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.A, err = func(item stackitem.Item) ([][]*big.Int, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]*big.Int, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) ([]*big.Int, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*big.Int, len(arr)) + for i := range res { + res[i], err = arr[i].TryInteger() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field A: %w", err) + } + + return nil +} + +// SomeUnexportedFieldEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeUnexportedField" name from the provided [result.ApplicationLog]. +func SomeUnexportedFieldEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeUnexportedFieldEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeUnexportedFieldEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeUnexportedField" { + continue + } + event := new(SomeUnexportedFieldEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeUnexportedFieldEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeUnexportedFieldEvent or +// returns an error if it's not possible to do to so. +func (e *SomeUnexportedFieldEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.S, err = itemToSimpleStruct(arr[index], nil) + if err != nil { + return fmt.Errorf("field S: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go b/cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go new file mode 100755 index 0000000..4b01120 --- /dev/null +++ b/cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go @@ -0,0 +1,759 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// Unnamed is a contract-specific unnamed type used by its methods. +type Unnamed struct { + I *big.Int + B bool +} + +// UnnamedX is a contract-specific unnamedX type used by its methods. +type UnnamedX struct { + I *big.Int +} + +// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract. +type ComplicatedNameEvent struct { + ComplicatedParam string +} + +// SomeMapEvent represents "SomeMap" event emitted by the contract. +type SomeMapEvent struct { + M map[*big.Int][]map[string][]util.Uint160 +} + +// SomeStructEvent represents "SomeStruct" event emitted by the contract. +type SomeStructEvent struct { + S *Unnamed +} + +// SomeArrayEvent represents "SomeArray" event emitted by the contract. +type SomeArrayEvent struct { + A [][]*big.Int +} + +// SomeUnexportedFieldEvent represents "SomeUnexportedField" event emitted by the contract. +type SomeUnexportedFieldEvent struct { + S *UnnamedX +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor + hash util.Uint160 +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + return &Contract{actor, hash} +} + +// Array creates a transaction invoking `array` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Array() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "array") +} + +// ArrayTransaction creates a transaction invoking `array` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) ArrayTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "array") +} + +// ArrayUnsigned creates a transaction invoking `array` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) ArrayUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "array", nil) +} + +// CrazyMap creates a transaction invoking `crazyMap` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) CrazyMap() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "crazyMap") +} + +// CrazyMapTransaction creates a transaction invoking `crazyMap` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) CrazyMapTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "crazyMap") +} + +// CrazyMapUnsigned creates a transaction invoking `crazyMap` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) CrazyMapUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "crazyMap", nil) +} + +// Main creates a transaction invoking `main` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Main() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "main") +} + +// MainTransaction creates a transaction invoking `main` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) MainTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "main") +} + +// MainUnsigned creates a transaction invoking `main` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) MainUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "main", nil) +} + +// Struct creates a transaction invoking `struct` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Struct() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "struct") +} + +// StructTransaction creates a transaction invoking `struct` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) StructTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "struct") +} + +// StructUnsigned creates a transaction invoking `struct` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) StructUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "struct", nil) +} + +// UnexportedField creates a transaction invoking `unexportedField` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) UnexportedField() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "unexportedField") +} + +// UnexportedFieldTransaction creates a transaction invoking `unexportedField` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) UnexportedFieldTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "unexportedField") +} + +// UnexportedFieldUnsigned creates a transaction invoking `unexportedField` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "unexportedField", nil) +} + +// itemToUnnamed converts stack item into *Unnamed. +// NULL item is returned as nil pointer without error. +func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(Unnamed) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *Unnamed is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&Unnamed{}) + +// Ensure *Unnamed is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&Unnamed{}) + +// FromStackItem retrieves fields of Unnamed from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *Unnamed) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + index++ + res.B, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field B: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing Unnamed. +// It implements [stackitem.Convertible] interface. +func (res *Unnamed) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewBool(res.B), error(nil) + if err != nil { + return nil, fmt.Errorf("field B: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing Unnamed. +// It implements [smartcontract.Convertible] interface so that Unnamed +// could be used with invokers. +func (res *Unnamed) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.B) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToUnnamedX converts stack item into *UnnamedX. +// NULL item is returned as nil pointer without error. +func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(UnnamedX) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *UnnamedX is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&UnnamedX{}) + +// Ensure *UnnamedX is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&UnnamedX{}) + +// FromStackItem retrieves fields of UnnamedX from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *UnnamedX) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing UnnamedX. +// It implements [stackitem.Convertible] interface. +func (res *UnnamedX) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 1) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing UnnamedX. +// It implements [smartcontract.Convertible] interface so that UnnamedX +// could be used with invokers. +func (res *UnnamedX) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 1) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events +// with "! complicated name %$#" name from the provided [result.ApplicationLog]. +func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*ComplicatedNameEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "! complicated name %$#" { + continue + } + event := new(ComplicatedNameEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or +// returns an error if it's not possible to do to so. +func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.ComplicatedParam, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field ComplicatedParam: %w", err) + } + + return nil +} + +// SomeMapEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeMap" name from the provided [result.ApplicationLog]. +func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeMapEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeMap" { + continue + } + event := new(SomeMapEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or +// returns an error if it's not possible to do to so. +func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.M, err = func(item stackitem.Item) (map[*big.Int][]map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int][]map[string][]util.Uint160) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) ([]map[string][]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]map[string][]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string][]util.Uint160) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field M: %w", err) + } + + return nil +} + +// SomeStructEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeStruct" name from the provided [result.ApplicationLog]. +func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeStructEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeStruct" { + continue + } + event := new(SomeStructEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or +// returns an error if it's not possible to do to so. +func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.S, err = itemToUnnamed(arr[index], nil) + if err != nil { + return fmt.Errorf("field S: %w", err) + } + + return nil +} + +// SomeArrayEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeArray" name from the provided [result.ApplicationLog]. +func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeArrayEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeArray" { + continue + } + event := new(SomeArrayEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or +// returns an error if it's not possible to do to so. +func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.A, err = func(item stackitem.Item) ([][]*big.Int, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]*big.Int, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) ([]*big.Int, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*big.Int, len(arr)) + for i := range res { + res[i], err = arr[i].TryInteger() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field A: %w", err) + } + + return nil +} + +// SomeUnexportedFieldEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeUnexportedField" name from the provided [result.ApplicationLog]. +func SomeUnexportedFieldEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeUnexportedFieldEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeUnexportedFieldEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeUnexportedField" { + continue + } + event := new(SomeUnexportedFieldEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeUnexportedFieldEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeUnexportedFieldEvent or +// returns an error if it's not possible to do to so. +func (e *SomeUnexportedFieldEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.S, err = itemToUnnamedX(arr[index], nil) + if err != nil { + return fmt.Errorf("field S: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go new file mode 100644 index 0000000..5b72cbd --- /dev/null +++ b/cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go @@ -0,0 +1,500 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "unicode/utf8" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract. +type ComplicatedNameEvent struct { + ComplicatedParam string +} + +// SomeMapEvent represents "SomeMap" event emitted by the contract. +type SomeMapEvent struct { + M map[any]any +} + +// SomeStructEvent represents "SomeStruct" event emitted by the contract. +type SomeStructEvent struct { + S []any +} + +// SomeArrayEvent represents "SomeArray" event emitted by the contract. +type SomeArrayEvent struct { + A []any +} + +// SomeUnexportedFieldEvent represents "SomeUnexportedField" event emitted by the contract. +type SomeUnexportedFieldEvent struct { + S []any +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor + hash util.Uint160 +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + return &Contract{actor, hash} +} + +// Array creates a transaction invoking `array` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Array() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "array") +} + +// ArrayTransaction creates a transaction invoking `array` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) ArrayTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "array") +} + +// ArrayUnsigned creates a transaction invoking `array` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) ArrayUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "array", nil) +} + +// CrazyMap creates a transaction invoking `crazyMap` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) CrazyMap() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "crazyMap") +} + +// CrazyMapTransaction creates a transaction invoking `crazyMap` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) CrazyMapTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "crazyMap") +} + +// CrazyMapUnsigned creates a transaction invoking `crazyMap` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) CrazyMapUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "crazyMap", nil) +} + +// Main creates a transaction invoking `main` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Main() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "main") +} + +// MainTransaction creates a transaction invoking `main` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) MainTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "main") +} + +// MainUnsigned creates a transaction invoking `main` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) MainUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "main", nil) +} + +// Struct creates a transaction invoking `struct` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Struct() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "struct") +} + +// StructTransaction creates a transaction invoking `struct` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) StructTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "struct") +} + +// StructUnsigned creates a transaction invoking `struct` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) StructUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "struct", nil) +} + +// UnexportedField creates a transaction invoking `unexportedField` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) UnexportedField() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "unexportedField") +} + +// UnexportedFieldTransaction creates a transaction invoking `unexportedField` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) UnexportedFieldTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "unexportedField") +} + +// UnexportedFieldUnsigned creates a transaction invoking `unexportedField` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "unexportedField", nil) +} + +// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events +// with "! complicated name %$#" name from the provided [result.ApplicationLog]. +func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*ComplicatedNameEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "! complicated name %$#" { + continue + } + event := new(ComplicatedNameEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or +// returns an error if it's not possible to do to so. +func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.ComplicatedParam, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field ComplicatedParam: %w", err) + } + + return nil +} + +// SomeMapEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeMap" name from the provided [result.ApplicationLog]. +func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeMapEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeMap" { + continue + } + event := new(SomeMapEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or +// returns an error if it's not possible to do to so. +func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.M, err = func(item stackitem.Item) (map[any]any, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[any]any) + for i := range m { + k, err := m[i].Key.Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := m[i].Value.Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field M: %w", err) + } + + return nil +} + +// SomeStructEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeStruct" name from the provided [result.ApplicationLog]. +func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeStructEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeStruct" { + continue + } + event := new(SomeStructEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or +// returns an error if it's not possible to do to so. +func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.S, err = func(item stackitem.Item) ([]any, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]any, len(arr)) + for i := range res { + res[i], err = arr[i].Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field S: %w", err) + } + + return nil +} + +// SomeArrayEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeArray" name from the provided [result.ApplicationLog]. +func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeArrayEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeArray" { + continue + } + event := new(SomeArrayEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or +// returns an error if it's not possible to do to so. +func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.A, err = func(item stackitem.Item) ([]any, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]any, len(arr)) + for i := range res { + res[i], err = arr[i].Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field A: %w", err) + } + + return nil +} + +// SomeUnexportedFieldEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeUnexportedField" name from the provided [result.ApplicationLog]. +func SomeUnexportedFieldEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeUnexportedFieldEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeUnexportedFieldEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeUnexportedField" { + continue + } + event := new(SomeUnexportedFieldEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeUnexportedFieldEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SomeUnexportedFieldEvent or +// returns an error if it's not possible to do to so. +func (e *SomeUnexportedFieldEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.S, err = func(item stackitem.Item) ([]any, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]any, len(arr)) + for i := range res { + res[i], err = arr[i].Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field S: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go new file mode 100644 index 0000000..9ead45f --- /dev/null +++ b/cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go @@ -0,0 +1,53 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package royalty contains RPC wrappers for Test royalty contract. +package royalty + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/util" + "math/big" +) + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor + hash util.Uint160 +} + +// New creates an instance of Contract using provided contract hash and the given Actor. +func New(actor Actor, hash util.Uint160) *Contract { + return &Contract{actor, hash} +} + +// RoyaltiesTransferred creates a transaction invoking `royaltiesTransferred` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) RoyaltiesTransferred(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount) +} + +// RoyaltiesTransferredTransaction creates a transaction invoking `royaltiesTransferred` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) RoyaltiesTransferredTransaction(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount) +} + +// RoyaltiesTransferredUnsigned creates a transaction invoking `royaltiesTransferred` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) RoyaltiesTransferredUnsigned(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "royaltiesTransferred", nil, royaltyToken, royaltyRecipient, buyer, tokenId, amount) +} diff --git a/cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go new file mode 100644 index 0000000..ad3fe68 --- /dev/null +++ b/cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go @@ -0,0 +1,57 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package royalty contains RPC wrappers for Test royalty contract. +package royalty + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/util" + "math/big" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor + hash util.Uint160 +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + return &Contract{actor, hash} +} + +// RoyaltiesTransferred creates a transaction invoking `royaltiesTransferred` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) RoyaltiesTransferred(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount) +} + +// RoyaltiesTransferredTransaction creates a transaction invoking `royaltiesTransferred` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) RoyaltiesTransferredTransaction(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount) +} + +// RoyaltiesTransferredUnsigned creates a transaction invoking `royaltiesTransferred` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) RoyaltiesTransferredUnsigned(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "royaltiesTransferred", nil, royaltyToken, royaltyRecipient, buyer, tokenId, amount) +} diff --git a/cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go new file mode 100755 index 0000000..d98de5d --- /dev/null +++ b/cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go @@ -0,0 +1,2860 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package structs contains RPC wrappers for Types contract. +package structs + +import ( + "crypto/elliptic" + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + PrimaryIndex *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// ManagementABI is a contract-specific management.ABI type used by its methods. +type ManagementABI struct { + Methods []*ManagementMethod + Events []*ManagementEvent +} + +// ManagementContract is a contract-specific management.Contract type used by its methods. +type ManagementContract struct { + ID *big.Int + UpdateCounter *big.Int + Hash util.Uint160 + NEF []byte + Manifest *ManagementManifest +} + +// ManagementEvent is a contract-specific management.Event type used by its methods. +type ManagementEvent struct { + Name string + Params []*ManagementParameter +} + +// ManagementGroup is a contract-specific management.Group type used by its methods. +type ManagementGroup struct { + PublicKey *keys.PublicKey + Signature []byte +} + +// ManagementManifest is a contract-specific management.Manifest type used by its methods. +type ManagementManifest struct { + Name string + Groups []*ManagementGroup + Features map[string]string + SupportedStandards []string + ABI *ManagementABI + Permissions []*ManagementPermission + Trusts []util.Uint160 + Extra any +} + +// ManagementMethod is a contract-specific management.Method type used by its methods. +type ManagementMethod struct { + Name string + Params []*ManagementParameter + ReturnType *big.Int + Offset *big.Int + Safe bool +} + +// ManagementParameter is a contract-specific management.Parameter type used by its methods. +type ManagementParameter struct { + Name string + Type *big.Int +} + +// ManagementPermission is a contract-specific management.Permission type used by its methods. +type ManagementPermission struct { + Contract util.Uint160 + Methods []string +} + +// StructsInternal is a contract-specific structs.Internal type used by its methods. +type StructsInternal struct { + Bool bool + Int *big.Int + Bytes []byte + String string + H160 util.Uint160 + H256 util.Uint256 + PK *keys.PublicKey + PubKey *keys.PublicKey + Sign []byte + ArrOfBytes [][]byte + ArrOfH160 []util.Uint160 + Map map[*big.Int]keys.PublicKeys + Struct *StructsInternal + UnexportedField *big.Int +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + invoker Invoker + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. +func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { + return &ContractReader{invoker, hash} +} + +// Block invokes `block` method of contract. +func (c *ContractReader) Block(b *LedgerBlock) (*LedgerBlock, error) { + return itemToLedgerBlock(unwrap.Item(c.invoker.Call(c.hash, "block", b))) +} + +// Contract invokes `contract` method of contract. +func (c *ContractReader) Contract(mc *ManagementContract) (*ManagementContract, error) { + return itemToManagementContract(unwrap.Item(c.invoker.Call(c.hash, "contract", mc))) +} + +// Struct invokes `struct` method of contract. +func (c *ContractReader) Struct(s *StructsInternal) (*StructsInternal, error) { + return itemToStructsInternal(unwrap.Item(c.invoker.Call(c.hash, "struct", s))) +} + +// Transaction invokes `transaction` method of contract. +func (c *ContractReader) Transaction(t *LedgerTransaction) (*LedgerTransaction, error) { + return itemToLedgerTransaction(unwrap.Item(c.invoker.Call(c.hash, "transaction", t))) +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +// NULL item is returned as nil pointer without error. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *LedgerBlock is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&LedgerBlock{}) + +// Ensure *LedgerBlock is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&LedgerBlock{}) + +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.PrimaryIndex, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field PrimaryIndex: %w", err) + } + + index++ + res.NextConsensus, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing LedgerBlock. +// It implements [stackitem.Convertible] interface. +func (res *LedgerBlock) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 10) + ) + itm, err = stackitem.NewByteArray(res.Hash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Hash: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Version), error(nil) + if err != nil { + return nil, fmt.Errorf("field Version: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.PrevHash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PrevHash: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.MerkleRoot.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field MerkleRoot: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Timestamp), error(nil) + if err != nil { + return nil, fmt.Errorf("field Timestamp: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Nonce), error(nil) + if err != nil { + return nil, fmt.Errorf("field Nonce: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Index), error(nil) + if err != nil { + return nil, fmt.Errorf("field Index: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.PrimaryIndex), error(nil) + if err != nil { + return nil, fmt.Errorf("field PrimaryIndex: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.NextConsensus.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field NextConsensus: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.TransactionsLength), error(nil) + if err != nil { + return nil, fmt.Errorf("field TransactionsLength: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing LedgerBlock. +// It implements [smartcontract.Convertible] interface so that LedgerBlock +// could be used with invokers. +func (res *LedgerBlock) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 10) + ) + prm, err = smartcontract.NewParameterFromValue(res.Hash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Hash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Version) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Version: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PrevHash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PrevHash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.MerkleRoot) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field MerkleRoot: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Timestamp) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Timestamp: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Nonce) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Nonce: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Index) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Index: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PrimaryIndex) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PrimaryIndex: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.NextConsensus) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field NextConsensus: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.TransactionsLength) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field TransactionsLength: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +// NULL item is returned as nil pointer without error. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *LedgerTransaction is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&LedgerTransaction{}) + +// Ensure *LedgerTransaction is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&LedgerTransaction{}) + +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Sender, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing LedgerTransaction. +// It implements [stackitem.Convertible] interface. +func (res *LedgerTransaction) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 8) + ) + itm, err = stackitem.NewByteArray(res.Hash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Hash: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Version), error(nil) + if err != nil { + return nil, fmt.Errorf("field Version: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Nonce), error(nil) + if err != nil { + return nil, fmt.Errorf("field Nonce: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Sender.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Sender: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.SysFee), error(nil) + if err != nil { + return nil, fmt.Errorf("field SysFee: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.NetFee), error(nil) + if err != nil { + return nil, fmt.Errorf("field NetFee: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.ValidUntilBlock), error(nil) + if err != nil { + return nil, fmt.Errorf("field ValidUntilBlock: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Script), error(nil) + if err != nil { + return nil, fmt.Errorf("field Script: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing LedgerTransaction. +// It implements [smartcontract.Convertible] interface so that LedgerTransaction +// could be used with invokers. +func (res *LedgerTransaction) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 8) + ) + prm, err = smartcontract.NewParameterFromValue(res.Hash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Hash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Version) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Version: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Nonce) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Nonce: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Sender) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Sender: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.SysFee) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field SysFee: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.NetFee) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field NetFee: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.ValidUntilBlock) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ValidUntilBlock: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Script) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Script: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementABI converts stack item into *ManagementABI. +// NULL item is returned as nil pointer without error. +func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementABI) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementABI is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementABI{}) + +// Ensure *ManagementABI is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementABI{}) + +// FromStackItem retrieves fields of ManagementABI from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementABI) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Methods, err = func(item stackitem.Item) ([]*ManagementMethod, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementMethod, len(arr)) + for i := range res { + res[i], err = itemToManagementMethod(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Methods: %w", err) + } + + index++ + res.Events, err = func(item stackitem.Item) ([]*ManagementEvent, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementEvent, len(arr)) + for i := range res { + res[i], err = itemToManagementEvent(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Events: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementABI. +// It implements [stackitem.Convertible] interface. +func (res *ManagementABI) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = func(in []*ManagementMethod) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Methods) + if err != nil { + return nil, fmt.Errorf("field Methods: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementEvent) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Events) + if err != nil { + return nil, fmt.Errorf("field Events: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementABI. +// It implements [smartcontract.Convertible] interface so that ManagementABI +// could be used with invokers. +func (res *ManagementABI) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = func(in []*ManagementMethod) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Methods) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Methods: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementEvent) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Events) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Events: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementContract converts stack item into *ManagementContract. +// NULL item is returned as nil pointer without error. +func itemToManagementContract(item stackitem.Item, err error) (*ManagementContract, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementContract) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementContract is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementContract{}) + +// Ensure *ManagementContract is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementContract{}) + +// FromStackItem retrieves fields of ManagementContract from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementContract) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.ID, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ID: %w", err) + } + + index++ + res.UpdateCounter, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field UpdateCounter: %w", err) + } + + index++ + res.Hash, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.NEF, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field NEF: %w", err) + } + + index++ + res.Manifest, err = itemToManagementManifest(arr[index], nil) + if err != nil { + return fmt.Errorf("field Manifest: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementContract. +// It implements [stackitem.Convertible] interface. +func (res *ManagementContract) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 5) + ) + itm, err = (*stackitem.BigInteger)(res.ID), error(nil) + if err != nil { + return nil, fmt.Errorf("field ID: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.UpdateCounter), error(nil) + if err != nil { + return nil, fmt.Errorf("field UpdateCounter: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Hash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Hash: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.NEF), error(nil) + if err != nil { + return nil, fmt.Errorf("field NEF: %w", err) + } + items = append(items, itm) + + itm, err = res.Manifest.ToStackItem() + if err != nil { + return nil, fmt.Errorf("field Manifest: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementContract. +// It implements [smartcontract.Convertible] interface so that ManagementContract +// could be used with invokers. +func (res *ManagementContract) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 5) + ) + prm, err = smartcontract.NewParameterFromValue(res.ID) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ID: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.UpdateCounter) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field UpdateCounter: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Hash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Hash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.NEF) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field NEF: %w", err) + } + prms = append(prms, prm) + + prm, err = res.Manifest.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Manifest: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementEvent converts stack item into *ManagementEvent. +// NULL item is returned as nil pointer without error. +func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementEvent) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementEvent is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementEvent{}) + +// Ensure *ManagementEvent is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementEvent{}) + +// FromStackItem retrieves fields of ManagementEvent from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementEvent) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Params, err = func(item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Params: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementEvent. +// It implements [stackitem.Convertible] interface. +func (res *ManagementEvent) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementParameter) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Params) + if err != nil { + return nil, fmt.Errorf("field Params: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementEvent. +// It implements [smartcontract.Convertible] interface so that ManagementEvent +// could be used with invokers. +func (res *ManagementEvent) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementParameter) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Params) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Params: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementGroup converts stack item into *ManagementGroup. +// NULL item is returned as nil pointer without error. +func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementGroup) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementGroup is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementGroup{}) + +// Ensure *ManagementGroup is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementGroup{}) + +// FromStackItem retrieves fields of ManagementGroup from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementGroup) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.PublicKey, err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PublicKey: %w", err) + } + + index++ + res.Signature, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Signature: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementGroup. +// It implements [stackitem.Convertible] interface. +func (res *ManagementGroup) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray(res.PublicKey.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PublicKey: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Signature), error(nil) + if err != nil { + return nil, fmt.Errorf("field Signature: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementGroup. +// It implements [smartcontract.Convertible] interface so that ManagementGroup +// could be used with invokers. +func (res *ManagementGroup) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.PublicKey) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PublicKey: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Signature) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Signature: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementManifest converts stack item into *ManagementManifest. +// NULL item is returned as nil pointer without error. +func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManifest, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementManifest) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementManifest is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementManifest{}) + +// Ensure *ManagementManifest is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementManifest{}) + +// FromStackItem retrieves fields of ManagementManifest from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Groups, err = func(item stackitem.Item) ([]*ManagementGroup, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementGroup, len(arr)) + for i := range res { + res[i], err = itemToManagementGroup(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Groups: %w", err) + } + + index++ + res.Features, err = func(item stackitem.Item) (map[string]string, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string]string) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Features: %w", err) + } + + index++ + res.SupportedStandards, err = func(item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field SupportedStandards: %w", err) + } + + index++ + res.ABI, err = itemToManagementABI(arr[index], nil) + if err != nil { + return fmt.Errorf("field ABI: %w", err) + } + + index++ + res.Permissions, err = func(item stackitem.Item) ([]*ManagementPermission, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementPermission, len(arr)) + for i := range res { + res[i], err = itemToManagementPermission(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Permissions: %w", err) + } + + index++ + res.Trusts, err = func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Trusts: %w", err) + } + + index++ + res.Extra, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Extra: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementManifest. +// It implements [stackitem.Convertible] interface. +func (res *ManagementManifest) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 8) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementGroup) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Groups) + if err != nil { + return nil, fmt.Errorf("field Groups: %w", err) + } + items = append(items, itm) + + itm, err = func(in map[string]string) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var m = stackitem.NewMap() + for k, v := range in { + iKey, err := stackitem.NewByteArray([]byte(k)), error(nil) + if err != nil { + return nil, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := stackitem.NewByteArray([]byte(v)), error(nil) + if err != nil { + return nil, fmt.Errorf("key %v, wrong value: %w", k, err) + } + m.Add(iKey, iVal) + } + return m, nil + }(res.Features) + if err != nil { + return nil, fmt.Errorf("field Features: %w", err) + } + items = append(items, itm) + + itm, err = func(in []string) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray([]byte(v)), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.SupportedStandards) + if err != nil { + return nil, fmt.Errorf("field SupportedStandards: %w", err) + } + items = append(items, itm) + + itm, err = res.ABI.ToStackItem() + if err != nil { + return nil, fmt.Errorf("field ABI: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementPermission) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Permissions) + if err != nil { + return nil, fmt.Errorf("field Permissions: %w", err) + } + items = append(items, itm) + + itm, err = func(in []util.Uint160) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Trusts) + if err != nil { + return nil, fmt.Errorf("field Trusts: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.TryMake(res.Extra) + if err != nil { + return nil, fmt.Errorf("field Extra: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementManifest. +// It implements [smartcontract.Convertible] interface so that ManagementManifest +// could be used with invokers. +func (res *ManagementManifest) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 8) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementGroup) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Groups) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Groups: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in map[string]string) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.ParameterPair, 0, len(in)) + for k, v := range in { + iKey, err := smartcontract.NewParameterFromValue(k) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v, wrong value: %w", k, err) + } + prms = append(prms, smartcontract.ParameterPair{Key: iKey, Value: iVal}) + } + return smartcontract.Parameter{Type: smartcontract.MapType, Value: prms}, nil + }(res.Features) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Features: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []string) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.SupportedStandards) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field SupportedStandards: %w", err) + } + prms = append(prms, prm) + + prm, err = res.ABI.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ABI: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementPermission) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Permissions) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Permissions: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []util.Uint160) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Trusts) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Trusts: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Extra) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Extra: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementMethod converts stack item into *ManagementMethod. +// NULL item is returned as nil pointer without error. +func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementMethod) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementMethod is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementMethod{}) + +// Ensure *ManagementMethod is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementMethod{}) + +// FromStackItem retrieves fields of ManagementMethod from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Params, err = func(item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Params: %w", err) + } + + index++ + res.ReturnType, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ReturnType: %w", err) + } + + index++ + res.Offset, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Offset: %w", err) + } + + index++ + res.Safe, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field Safe: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementMethod. +// It implements [stackitem.Convertible] interface. +func (res *ManagementMethod) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 5) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementParameter) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Params) + if err != nil { + return nil, fmt.Errorf("field Params: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.ReturnType), error(nil) + if err != nil { + return nil, fmt.Errorf("field ReturnType: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Offset), error(nil) + if err != nil { + return nil, fmt.Errorf("field Offset: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewBool(res.Safe), error(nil) + if err != nil { + return nil, fmt.Errorf("field Safe: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementMethod. +// It implements [smartcontract.Convertible] interface so that ManagementMethod +// could be used with invokers. +func (res *ManagementMethod) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 5) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementParameter) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Params) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Params: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.ReturnType) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ReturnType: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Offset) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Offset: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Safe) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Safe: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementParameter converts stack item into *ManagementParameter. +// NULL item is returned as nil pointer without error. +func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParameter, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementParameter) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementParameter is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementParameter{}) + +// Ensure *ManagementParameter is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementParameter{}) + +// FromStackItem retrieves fields of ManagementParameter from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementParameter) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementParameter. +// It implements [stackitem.Convertible] interface. +func (res *ManagementParameter) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Type), error(nil) + if err != nil { + return nil, fmt.Errorf("field Type: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementParameter. +// It implements [smartcontract.Convertible] interface so that ManagementParameter +// could be used with invokers. +func (res *ManagementParameter) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Type) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Type: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementPermission converts stack item into *ManagementPermission. +// NULL item is returned as nil pointer without error. +func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPermission, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementPermission) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementPermission is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementPermission{}) + +// Ensure *ManagementPermission is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementPermission{}) + +// FromStackItem retrieves fields of ManagementPermission from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementPermission) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Contract, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Contract: %w", err) + } + + index++ + res.Methods, err = func(item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Methods: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementPermission. +// It implements [stackitem.Convertible] interface. +func (res *ManagementPermission) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray(res.Contract.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Contract: %w", err) + } + items = append(items, itm) + + itm, err = func(in []string) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray([]byte(v)), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Methods) + if err != nil { + return nil, fmt.Errorf("field Methods: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementPermission. +// It implements [smartcontract.Convertible] interface so that ManagementPermission +// could be used with invokers. +func (res *ManagementPermission) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Contract) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Contract: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []string) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Methods) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Methods: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToStructsInternal converts stack item into *StructsInternal. +// NULL item is returned as nil pointer without error. +func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(StructsInternal) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *StructsInternal is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&StructsInternal{}) + +// Ensure *StructsInternal is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&StructsInternal{}) + +// FromStackItem retrieves fields of StructsInternal from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *StructsInternal) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 14 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Bool, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field Bool: %w", err) + } + + index++ + res.Int, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Int: %w", err) + } + + index++ + res.Bytes, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Bytes: %w", err) + } + + index++ + res.String, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field String: %w", err) + } + + index++ + res.H160, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field H160: %w", err) + } + + index++ + res.H256, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field H256: %w", err) + } + + index++ + res.PK, err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PK: %w", err) + } + + index++ + res.PubKey, err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PubKey: %w", err) + } + + index++ + res.Sign, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Sign: %w", err) + } + + index++ + res.ArrOfBytes, err = func(item stackitem.Item) ([][]byte, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]byte, len(arr)) + for i := range res { + res[i], err = arr[i].TryBytes() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field ArrOfBytes: %w", err) + } + + index++ + res.ArrOfH160, err = func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field ArrOfH160: %w", err) + } + + index++ + res.Map, err = func(item stackitem.Item) (map[*big.Int]keys.PublicKeys, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int]keys.PublicKeys) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Map: %w", err) + } + + index++ + res.Struct, err = itemToStructsInternal(arr[index], nil) + if err != nil { + return fmt.Errorf("field Struct: %w", err) + } + + index++ + res.UnexportedField, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field UnexportedField: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing StructsInternal. +// It implements [stackitem.Convertible] interface. +func (res *StructsInternal) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 14) + ) + itm, err = stackitem.NewBool(res.Bool), error(nil) + if err != nil { + return nil, fmt.Errorf("field Bool: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Int), error(nil) + if err != nil { + return nil, fmt.Errorf("field Int: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Bytes), error(nil) + if err != nil { + return nil, fmt.Errorf("field Bytes: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray([]byte(res.String)), error(nil) + if err != nil { + return nil, fmt.Errorf("field String: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.H160.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field H160: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.H256.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field H256: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.PK.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PK: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.PubKey.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PubKey: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Sign), error(nil) + if err != nil { + return nil, fmt.Errorf("field Sign: %w", err) + } + items = append(items, itm) + + itm, err = func(in [][]byte) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.ArrOfBytes) + if err != nil { + return nil, fmt.Errorf("field ArrOfBytes: %w", err) + } + items = append(items, itm) + + itm, err = func(in []util.Uint160) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.ArrOfH160) + if err != nil { + return nil, fmt.Errorf("field ArrOfH160: %w", err) + } + items = append(items, itm) + + itm, err = func(in map[*big.Int]keys.PublicKeys) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var m = stackitem.NewMap() + for k, v := range in { + iKey, err := (*stackitem.BigInteger)(k), error(nil) + if err != nil { + return nil, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := func(in keys.PublicKeys) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(v) + if err != nil { + return nil, fmt.Errorf("key %v, wrong value: %w", k, err) + } + m.Add(iKey, iVal) + } + return m, nil + }(res.Map) + if err != nil { + return nil, fmt.Errorf("field Map: %w", err) + } + items = append(items, itm) + + itm, err = res.Struct.ToStackItem() + if err != nil { + return nil, fmt.Errorf("field Struct: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.UnexportedField), error(nil) + if err != nil { + return nil, fmt.Errorf("field UnexportedField: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing StructsInternal. +// It implements [smartcontract.Convertible] interface so that StructsInternal +// could be used with invokers. +func (res *StructsInternal) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 14) + ) + prm, err = smartcontract.NewParameterFromValue(res.Bool) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Bool: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Int) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Int: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Bytes) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Bytes: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.String) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field String: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.H160) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field H160: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.H256) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field H256: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PK) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PK: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PubKey) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PubKey: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Sign) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Sign: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in [][]byte) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.ArrOfBytes) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ArrOfBytes: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []util.Uint160) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.ArrOfH160) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ArrOfH160: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in map[*big.Int]keys.PublicKeys) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.ParameterPair, 0, len(in)) + for k, v := range in { + iKey, err := smartcontract.NewParameterFromValue(k) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := func(in keys.PublicKeys) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v, wrong value: %w", k, err) + } + prms = append(prms, smartcontract.ParameterPair{Key: iKey, Value: iVal}) + } + return smartcontract.Parameter{Type: smartcontract.MapType, Value: prms}, nil + }(res.Map) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Map: %w", err) + } + prms = append(prms, prm) + + prm, err = res.Struct.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Struct: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.UnexportedField) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field UnexportedField: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} diff --git a/cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go new file mode 100644 index 0000000..9dcb21c --- /dev/null +++ b/cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go @@ -0,0 +1,2864 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package structs contains RPC wrappers for Types contract. +package structs + +import ( + "crypto/elliptic" + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + PrimaryIndex *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// ManagementABI is a contract-specific management.ABI type used by its methods. +type ManagementABI struct { + Methods []*ManagementMethod + Events []*ManagementEvent +} + +// ManagementContract is a contract-specific management.Contract type used by its methods. +type ManagementContract struct { + ID *big.Int + UpdateCounter *big.Int + Hash util.Uint160 + NEF []byte + Manifest *ManagementManifest +} + +// ManagementEvent is a contract-specific management.Event type used by its methods. +type ManagementEvent struct { + Name string + Params []*ManagementParameter +} + +// ManagementGroup is a contract-specific management.Group type used by its methods. +type ManagementGroup struct { + PublicKey *keys.PublicKey + Signature []byte +} + +// ManagementManifest is a contract-specific management.Manifest type used by its methods. +type ManagementManifest struct { + Name string + Groups []*ManagementGroup + Features map[string]string + SupportedStandards []string + ABI *ManagementABI + Permissions []*ManagementPermission + Trusts []util.Uint160 + Extra any +} + +// ManagementMethod is a contract-specific management.Method type used by its methods. +type ManagementMethod struct { + Name string + Params []*ManagementParameter + ReturnType *big.Int + Offset *big.Int + Safe bool +} + +// ManagementParameter is a contract-specific management.Parameter type used by its methods. +type ManagementParameter struct { + Name string + Type *big.Int +} + +// ManagementPermission is a contract-specific management.Permission type used by its methods. +type ManagementPermission struct { + Contract util.Uint160 + Methods []string +} + +// StructsInternal is a contract-specific structs.Internal type used by its methods. +type StructsInternal struct { + Bool bool + Int *big.Int + Bytes []byte + String string + H160 util.Uint160 + H256 util.Uint256 + PK *keys.PublicKey + PubKey *keys.PublicKey + Sign []byte + ArrOfBytes [][]byte + ArrOfH160 []util.Uint160 + Map map[*big.Int]keys.PublicKeys + Struct *StructsInternal + UnexportedField *big.Int +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + invoker Invoker + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{invoker, hash} +} + +// Block invokes `block` method of contract. +func (c *ContractReader) Block(b *LedgerBlock) (*LedgerBlock, error) { + return itemToLedgerBlock(unwrap.Item(c.invoker.Call(c.hash, "block", b))) +} + +// Contract invokes `contract` method of contract. +func (c *ContractReader) Contract(mc *ManagementContract) (*ManagementContract, error) { + return itemToManagementContract(unwrap.Item(c.invoker.Call(c.hash, "contract", mc))) +} + +// Struct invokes `struct` method of contract. +func (c *ContractReader) Struct(s *StructsInternal) (*StructsInternal, error) { + return itemToStructsInternal(unwrap.Item(c.invoker.Call(c.hash, "struct", s))) +} + +// Transaction invokes `transaction` method of contract. +func (c *ContractReader) Transaction(t *LedgerTransaction) (*LedgerTransaction, error) { + return itemToLedgerTransaction(unwrap.Item(c.invoker.Call(c.hash, "transaction", t))) +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +// NULL item is returned as nil pointer without error. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *LedgerBlock is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&LedgerBlock{}) + +// Ensure *LedgerBlock is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&LedgerBlock{}) + +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.PrimaryIndex, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field PrimaryIndex: %w", err) + } + + index++ + res.NextConsensus, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing LedgerBlock. +// It implements [stackitem.Convertible] interface. +func (res *LedgerBlock) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 10) + ) + itm, err = stackitem.NewByteArray(res.Hash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Hash: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Version), error(nil) + if err != nil { + return nil, fmt.Errorf("field Version: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.PrevHash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PrevHash: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.MerkleRoot.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field MerkleRoot: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Timestamp), error(nil) + if err != nil { + return nil, fmt.Errorf("field Timestamp: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Nonce), error(nil) + if err != nil { + return nil, fmt.Errorf("field Nonce: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Index), error(nil) + if err != nil { + return nil, fmt.Errorf("field Index: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.PrimaryIndex), error(nil) + if err != nil { + return nil, fmt.Errorf("field PrimaryIndex: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.NextConsensus.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field NextConsensus: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.TransactionsLength), error(nil) + if err != nil { + return nil, fmt.Errorf("field TransactionsLength: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing LedgerBlock. +// It implements [smartcontract.Convertible] interface so that LedgerBlock +// could be used with invokers. +func (res *LedgerBlock) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 10) + ) + prm, err = smartcontract.NewParameterFromValue(res.Hash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Hash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Version) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Version: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PrevHash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PrevHash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.MerkleRoot) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field MerkleRoot: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Timestamp) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Timestamp: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Nonce) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Nonce: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Index) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Index: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PrimaryIndex) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PrimaryIndex: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.NextConsensus) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field NextConsensus: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.TransactionsLength) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field TransactionsLength: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +// NULL item is returned as nil pointer without error. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *LedgerTransaction is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&LedgerTransaction{}) + +// Ensure *LedgerTransaction is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&LedgerTransaction{}) + +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Sender, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing LedgerTransaction. +// It implements [stackitem.Convertible] interface. +func (res *LedgerTransaction) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 8) + ) + itm, err = stackitem.NewByteArray(res.Hash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Hash: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Version), error(nil) + if err != nil { + return nil, fmt.Errorf("field Version: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Nonce), error(nil) + if err != nil { + return nil, fmt.Errorf("field Nonce: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Sender.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Sender: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.SysFee), error(nil) + if err != nil { + return nil, fmt.Errorf("field SysFee: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.NetFee), error(nil) + if err != nil { + return nil, fmt.Errorf("field NetFee: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.ValidUntilBlock), error(nil) + if err != nil { + return nil, fmt.Errorf("field ValidUntilBlock: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Script), error(nil) + if err != nil { + return nil, fmt.Errorf("field Script: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing LedgerTransaction. +// It implements [smartcontract.Convertible] interface so that LedgerTransaction +// could be used with invokers. +func (res *LedgerTransaction) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 8) + ) + prm, err = smartcontract.NewParameterFromValue(res.Hash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Hash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Version) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Version: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Nonce) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Nonce: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Sender) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Sender: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.SysFee) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field SysFee: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.NetFee) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field NetFee: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.ValidUntilBlock) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ValidUntilBlock: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Script) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Script: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementABI converts stack item into *ManagementABI. +// NULL item is returned as nil pointer without error. +func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementABI) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementABI is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementABI{}) + +// Ensure *ManagementABI is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementABI{}) + +// FromStackItem retrieves fields of ManagementABI from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementABI) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Methods, err = func(item stackitem.Item) ([]*ManagementMethod, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementMethod, len(arr)) + for i := range res { + res[i], err = itemToManagementMethod(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Methods: %w", err) + } + + index++ + res.Events, err = func(item stackitem.Item) ([]*ManagementEvent, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementEvent, len(arr)) + for i := range res { + res[i], err = itemToManagementEvent(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Events: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementABI. +// It implements [stackitem.Convertible] interface. +func (res *ManagementABI) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = func(in []*ManagementMethod) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Methods) + if err != nil { + return nil, fmt.Errorf("field Methods: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementEvent) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Events) + if err != nil { + return nil, fmt.Errorf("field Events: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementABI. +// It implements [smartcontract.Convertible] interface so that ManagementABI +// could be used with invokers. +func (res *ManagementABI) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = func(in []*ManagementMethod) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Methods) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Methods: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementEvent) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Events) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Events: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementContract converts stack item into *ManagementContract. +// NULL item is returned as nil pointer without error. +func itemToManagementContract(item stackitem.Item, err error) (*ManagementContract, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementContract) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementContract is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementContract{}) + +// Ensure *ManagementContract is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementContract{}) + +// FromStackItem retrieves fields of ManagementContract from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementContract) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.ID, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ID: %w", err) + } + + index++ + res.UpdateCounter, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field UpdateCounter: %w", err) + } + + index++ + res.Hash, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.NEF, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field NEF: %w", err) + } + + index++ + res.Manifest, err = itemToManagementManifest(arr[index], nil) + if err != nil { + return fmt.Errorf("field Manifest: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementContract. +// It implements [stackitem.Convertible] interface. +func (res *ManagementContract) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 5) + ) + itm, err = (*stackitem.BigInteger)(res.ID), error(nil) + if err != nil { + return nil, fmt.Errorf("field ID: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.UpdateCounter), error(nil) + if err != nil { + return nil, fmt.Errorf("field UpdateCounter: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Hash.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Hash: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.NEF), error(nil) + if err != nil { + return nil, fmt.Errorf("field NEF: %w", err) + } + items = append(items, itm) + + itm, err = res.Manifest.ToStackItem() + if err != nil { + return nil, fmt.Errorf("field Manifest: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementContract. +// It implements [smartcontract.Convertible] interface so that ManagementContract +// could be used with invokers. +func (res *ManagementContract) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 5) + ) + prm, err = smartcontract.NewParameterFromValue(res.ID) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ID: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.UpdateCounter) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field UpdateCounter: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Hash) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Hash: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.NEF) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field NEF: %w", err) + } + prms = append(prms, prm) + + prm, err = res.Manifest.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Manifest: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementEvent converts stack item into *ManagementEvent. +// NULL item is returned as nil pointer without error. +func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementEvent) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementEvent is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementEvent{}) + +// Ensure *ManagementEvent is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementEvent{}) + +// FromStackItem retrieves fields of ManagementEvent from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementEvent) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Params, err = func(item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Params: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementEvent. +// It implements [stackitem.Convertible] interface. +func (res *ManagementEvent) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementParameter) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Params) + if err != nil { + return nil, fmt.Errorf("field Params: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementEvent. +// It implements [smartcontract.Convertible] interface so that ManagementEvent +// could be used with invokers. +func (res *ManagementEvent) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementParameter) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Params) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Params: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementGroup converts stack item into *ManagementGroup. +// NULL item is returned as nil pointer without error. +func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementGroup) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementGroup is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementGroup{}) + +// Ensure *ManagementGroup is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementGroup{}) + +// FromStackItem retrieves fields of ManagementGroup from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementGroup) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.PublicKey, err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PublicKey: %w", err) + } + + index++ + res.Signature, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Signature: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementGroup. +// It implements [stackitem.Convertible] interface. +func (res *ManagementGroup) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray(res.PublicKey.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PublicKey: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Signature), error(nil) + if err != nil { + return nil, fmt.Errorf("field Signature: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementGroup. +// It implements [smartcontract.Convertible] interface so that ManagementGroup +// could be used with invokers. +func (res *ManagementGroup) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.PublicKey) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PublicKey: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Signature) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Signature: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementManifest converts stack item into *ManagementManifest. +// NULL item is returned as nil pointer without error. +func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManifest, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementManifest) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementManifest is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementManifest{}) + +// Ensure *ManagementManifest is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementManifest{}) + +// FromStackItem retrieves fields of ManagementManifest from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Groups, err = func(item stackitem.Item) ([]*ManagementGroup, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementGroup, len(arr)) + for i := range res { + res[i], err = itemToManagementGroup(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Groups: %w", err) + } + + index++ + res.Features, err = func(item stackitem.Item) (map[string]string, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string]string) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Features: %w", err) + } + + index++ + res.SupportedStandards, err = func(item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field SupportedStandards: %w", err) + } + + index++ + res.ABI, err = itemToManagementABI(arr[index], nil) + if err != nil { + return fmt.Errorf("field ABI: %w", err) + } + + index++ + res.Permissions, err = func(item stackitem.Item) ([]*ManagementPermission, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementPermission, len(arr)) + for i := range res { + res[i], err = itemToManagementPermission(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Permissions: %w", err) + } + + index++ + res.Trusts, err = func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Trusts: %w", err) + } + + index++ + res.Extra, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Extra: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementManifest. +// It implements [stackitem.Convertible] interface. +func (res *ManagementManifest) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 8) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementGroup) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Groups) + if err != nil { + return nil, fmt.Errorf("field Groups: %w", err) + } + items = append(items, itm) + + itm, err = func(in map[string]string) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var m = stackitem.NewMap() + for k, v := range in { + iKey, err := stackitem.NewByteArray([]byte(k)), error(nil) + if err != nil { + return nil, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := stackitem.NewByteArray([]byte(v)), error(nil) + if err != nil { + return nil, fmt.Errorf("key %v, wrong value: %w", k, err) + } + m.Add(iKey, iVal) + } + return m, nil + }(res.Features) + if err != nil { + return nil, fmt.Errorf("field Features: %w", err) + } + items = append(items, itm) + + itm, err = func(in []string) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray([]byte(v)), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.SupportedStandards) + if err != nil { + return nil, fmt.Errorf("field SupportedStandards: %w", err) + } + items = append(items, itm) + + itm, err = res.ABI.ToStackItem() + if err != nil { + return nil, fmt.Errorf("field ABI: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementPermission) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Permissions) + if err != nil { + return nil, fmt.Errorf("field Permissions: %w", err) + } + items = append(items, itm) + + itm, err = func(in []util.Uint160) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Trusts) + if err != nil { + return nil, fmt.Errorf("field Trusts: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.TryMake(res.Extra) + if err != nil { + return nil, fmt.Errorf("field Extra: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementManifest. +// It implements [smartcontract.Convertible] interface so that ManagementManifest +// could be used with invokers. +func (res *ManagementManifest) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 8) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementGroup) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Groups) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Groups: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in map[string]string) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.ParameterPair, 0, len(in)) + for k, v := range in { + iKey, err := smartcontract.NewParameterFromValue(k) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v, wrong value: %w", k, err) + } + prms = append(prms, smartcontract.ParameterPair{Key: iKey, Value: iVal}) + } + return smartcontract.Parameter{Type: smartcontract.MapType, Value: prms}, nil + }(res.Features) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Features: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []string) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.SupportedStandards) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field SupportedStandards: %w", err) + } + prms = append(prms, prm) + + prm, err = res.ABI.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ABI: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementPermission) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Permissions) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Permissions: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []util.Uint160) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Trusts) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Trusts: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Extra) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Extra: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementMethod converts stack item into *ManagementMethod. +// NULL item is returned as nil pointer without error. +func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementMethod) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementMethod is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementMethod{}) + +// Ensure *ManagementMethod is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementMethod{}) + +// FromStackItem retrieves fields of ManagementMethod from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Params, err = func(item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Params: %w", err) + } + + index++ + res.ReturnType, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ReturnType: %w", err) + } + + index++ + res.Offset, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Offset: %w", err) + } + + index++ + res.Safe, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field Safe: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementMethod. +// It implements [stackitem.Convertible] interface. +func (res *ManagementMethod) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 5) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = func(in []*ManagementParameter) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := v.ToStackItem() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Params) + if err != nil { + return nil, fmt.Errorf("field Params: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.ReturnType), error(nil) + if err != nil { + return nil, fmt.Errorf("field ReturnType: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Offset), error(nil) + if err != nil { + return nil, fmt.Errorf("field Offset: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewBool(res.Safe), error(nil) + if err != nil { + return nil, fmt.Errorf("field Safe: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementMethod. +// It implements [smartcontract.Convertible] interface so that ManagementMethod +// could be used with invokers. +func (res *ManagementMethod) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 5) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []*ManagementParameter) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := v.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Params) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Params: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.ReturnType) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ReturnType: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Offset) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Offset: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Safe) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Safe: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementParameter converts stack item into *ManagementParameter. +// NULL item is returned as nil pointer without error. +func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParameter, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementParameter) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementParameter is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementParameter{}) + +// Ensure *ManagementParameter is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementParameter{}) + +// FromStackItem retrieves fields of ManagementParameter from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementParameter) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + res.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementParameter. +// It implements [stackitem.Convertible] interface. +func (res *ManagementParameter) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray([]byte(res.Name)), error(nil) + if err != nil { + return nil, fmt.Errorf("field Name: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Type), error(nil) + if err != nil { + return nil, fmt.Errorf("field Type: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementParameter. +// It implements [smartcontract.Convertible] interface so that ManagementParameter +// could be used with invokers. +func (res *ManagementParameter) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Name) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Name: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Type) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Type: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToManagementPermission converts stack item into *ManagementPermission. +// NULL item is returned as nil pointer without error. +func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPermission, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(ManagementPermission) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *ManagementPermission is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&ManagementPermission{}) + +// Ensure *ManagementPermission is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&ManagementPermission{}) + +// FromStackItem retrieves fields of ManagementPermission from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *ManagementPermission) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Contract, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Contract: %w", err) + } + + index++ + res.Methods, err = func(item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Methods: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing ManagementPermission. +// It implements [stackitem.Convertible] interface. +func (res *ManagementPermission) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = stackitem.NewByteArray(res.Contract.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field Contract: %w", err) + } + items = append(items, itm) + + itm, err = func(in []string) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray([]byte(v)), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.Methods) + if err != nil { + return nil, fmt.Errorf("field Methods: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing ManagementPermission. +// It implements [smartcontract.Convertible] interface so that ManagementPermission +// could be used with invokers. +func (res *ManagementPermission) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.Contract) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Contract: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []string) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.Methods) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Methods: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToStructsInternal converts stack item into *StructsInternal. +// NULL item is returned as nil pointer without error. +func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(StructsInternal) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *StructsInternal is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&StructsInternal{}) + +// Ensure *StructsInternal is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&StructsInternal{}) + +// FromStackItem retrieves fields of StructsInternal from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *StructsInternal) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 14 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Bool, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field Bool: %w", err) + } + + index++ + res.Int, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Int: %w", err) + } + + index++ + res.Bytes, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Bytes: %w", err) + } + + index++ + res.String, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field String: %w", err) + } + + index++ + res.H160, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field H160: %w", err) + } + + index++ + res.H256, err = func(item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field H256: %w", err) + } + + index++ + res.PK, err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PK: %w", err) + } + + index++ + res.PubKey, err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field PubKey: %w", err) + } + + index++ + res.Sign, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Sign: %w", err) + } + + index++ + res.ArrOfBytes, err = func(item stackitem.Item) ([][]byte, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]byte, len(arr)) + for i := range res { + res[i], err = arr[i].TryBytes() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field ArrOfBytes: %w", err) + } + + index++ + res.ArrOfH160, err = func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field ArrOfH160: %w", err) + } + + index++ + res.Map, err = func(item stackitem.Item) (map[*big.Int]keys.PublicKeys, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int]keys.PublicKeys) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Map: %w", err) + } + + index++ + res.Struct, err = itemToStructsInternal(arr[index], nil) + if err != nil { + return fmt.Errorf("field Struct: %w", err) + } + + index++ + res.UnexportedField, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field UnexportedField: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing StructsInternal. +// It implements [stackitem.Convertible] interface. +func (res *StructsInternal) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 14) + ) + itm, err = stackitem.NewBool(res.Bool), error(nil) + if err != nil { + return nil, fmt.Errorf("field Bool: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.Int), error(nil) + if err != nil { + return nil, fmt.Errorf("field Int: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Bytes), error(nil) + if err != nil { + return nil, fmt.Errorf("field Bytes: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray([]byte(res.String)), error(nil) + if err != nil { + return nil, fmt.Errorf("field String: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.H160.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field H160: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.H256.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("field H256: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.PK.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PK: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.PubKey.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("field PubKey: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewByteArray(res.Sign), error(nil) + if err != nil { + return nil, fmt.Errorf("field Sign: %w", err) + } + items = append(items, itm) + + itm, err = func(in [][]byte) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.ArrOfBytes) + if err != nil { + return nil, fmt.Errorf("field ArrOfBytes: %w", err) + } + items = append(items, itm) + + itm, err = func(in []util.Uint160) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v.BytesBE()), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(res.ArrOfH160) + if err != nil { + return nil, fmt.Errorf("field ArrOfH160: %w", err) + } + items = append(items, itm) + + itm, err = func(in map[*big.Int]keys.PublicKeys) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var m = stackitem.NewMap() + for k, v := range in { + iKey, err := (*stackitem.BigInteger)(k), error(nil) + if err != nil { + return nil, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := func(in keys.PublicKeys) (stackitem.Item, error) { + if in == nil { + return stackitem.Null{}, nil + } + + var items = make([]stackitem.Item, 0, len(in)) + for i, v := range in { + itm, err := stackitem.NewByteArray(v.Bytes()), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + items = append(items, itm) + } + return stackitem.NewArray(items), nil + }(v) + if err != nil { + return nil, fmt.Errorf("key %v, wrong value: %w", k, err) + } + m.Add(iKey, iVal) + } + return m, nil + }(res.Map) + if err != nil { + return nil, fmt.Errorf("field Map: %w", err) + } + items = append(items, itm) + + itm, err = res.Struct.ToStackItem() + if err != nil { + return nil, fmt.Errorf("field Struct: %w", err) + } + items = append(items, itm) + + itm, err = (*stackitem.BigInteger)(res.UnexportedField), error(nil) + if err != nil { + return nil, fmt.Errorf("field UnexportedField: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing StructsInternal. +// It implements [smartcontract.Convertible] interface so that StructsInternal +// could be used with invokers. +func (res *StructsInternal) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 14) + ) + prm, err = smartcontract.NewParameterFromValue(res.Bool) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Bool: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Int) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Int: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Bytes) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Bytes: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.String) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field String: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.H160) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field H160: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.H256) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field H256: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PK) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PK: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.PubKey) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field PubKey: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.Sign) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Sign: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in [][]byte) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.ArrOfBytes) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ArrOfBytes: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in []util.Uint160) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(res.ArrOfH160) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field ArrOfH160: %w", err) + } + prms = append(prms, prm) + + prm, err = func(in map[*big.Int]keys.PublicKeys) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.ParameterPair, 0, len(in)) + for k, v := range in { + iKey, err := smartcontract.NewParameterFromValue(k) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v: %w", k, err) + } + iVal, err := func(in keys.PublicKeys) (smartcontract.Parameter, error) { + if in == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var prms = make([]smartcontract.Parameter, 0, len(in)) + for i, v := range in { + prm, err := smartcontract.NewParameterFromValue(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("item %d: %w", i, err) + } + prms = append(prms, prm) + } + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil + }(v) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("key %v, wrong value: %w", k, err) + } + prms = append(prms, smartcontract.ParameterPair{Key: iKey, Value: iVal}) + } + return smartcontract.Parameter{Type: smartcontract.MapType, Value: prms}, nil + }(res.Map) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Map: %w", err) + } + prms = append(prms, prm) + + prm, err = res.Struct.ToSCParameter() + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field Struct: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.UnexportedField) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field UnexportedField: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} diff --git a/cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go new file mode 100755 index 0000000..cab0cb5 --- /dev/null +++ b/cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go @@ -0,0 +1,563 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package types contains RPC wrappers for Types contract. +package types + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// Unnamed is a contract-specific unnamed type used by its methods. +type Unnamed struct { + I *big.Int +} + +// UnnamedX is a contract-specific unnamedX type used by its methods. +type UnnamedX struct { + I *big.Int + B bool +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + invoker Invoker + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. +func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { + return &ContractReader{invoker, hash} +} + +// AAAStrings invokes `aAAStrings` method of contract. +func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) { + return func(item stackitem.Item, err error) ([][][]string, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) ([][][]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][][]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) ([][]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "aAAStrings", s))) +} + +// Any invokes `any` method of contract. +func (c *ContractReader) Any(a any) (any, error) { + return func(item stackitem.Item, err error) (any, error) { + if err != nil { + return nil, err + } + return item.Value(), error(nil) + }(unwrap.Item(c.invoker.Call(c.hash, "any", a))) +} + +// AnyMaps invokes `anyMaps` method of contract. +func (c *ContractReader) AnyMaps(m map[*big.Int]any) (map[*big.Int]any, error) { + return func(item stackitem.Item, err error) (map[*big.Int]any, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) (map[*big.Int]any, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int]any) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := m[i].Value.Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "anyMaps", m))) +} + +// Bool invokes `bool` method of contract. +func (c *ContractReader) Bool(b bool) (bool, error) { + return unwrap.Bool(c.invoker.Call(c.hash, "bool", b)) +} + +// Bools invokes `bools` method of contract. +func (c *ContractReader) Bools(b []bool) ([]bool, error) { + return unwrap.ArrayOfBools(c.invoker.Call(c.hash, "bools", b)) +} + +// Bytes invokes `bytes` method of contract. +func (c *ContractReader) Bytes(b []byte) ([]byte, error) { + return unwrap.Bytes(c.invoker.Call(c.hash, "bytes", b)) +} + +// Bytess invokes `bytess` method of contract. +func (c *ContractReader) Bytess(b [][]byte) ([][]byte, error) { + return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "bytess", b)) +} + +// CrazyMaps invokes `crazyMaps` method of contract. +func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) (map[*big.Int][]map[string][]util.Uint160, error) { + return func(item stackitem.Item, err error) (map[*big.Int][]map[string][]util.Uint160, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) (map[*big.Int][]map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int][]map[string][]util.Uint160) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) ([]map[string][]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]map[string][]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string][]util.Uint160) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "crazyMaps", m))) +} + +// Hash160 invokes `hash160` method of contract. +func (c *ContractReader) Hash160(h util.Uint160) (util.Uint160, error) { + return unwrap.Uint160(c.invoker.Call(c.hash, "hash160", h)) +} + +// Hash160s invokes `hash160s` method of contract. +func (c *ContractReader) Hash160s(h []util.Uint160) ([]util.Uint160, error) { + return unwrap.ArrayOfUint160(c.invoker.Call(c.hash, "hash160s", h)) +} + +// Hash256 invokes `hash256` method of contract. +func (c *ContractReader) Hash256(h util.Uint256) (util.Uint256, error) { + return unwrap.Uint256(c.invoker.Call(c.hash, "hash256", h)) +} + +// Hash256s invokes `hash256s` method of contract. +func (c *ContractReader) Hash256s(h []util.Uint256) ([]util.Uint256, error) { + return unwrap.ArrayOfUint256(c.invoker.Call(c.hash, "hash256s", h)) +} + +// Int invokes `int` method of contract. +func (c *ContractReader) Int(i *big.Int) (*big.Int, error) { + return unwrap.BigInt(c.invoker.Call(c.hash, "int", i)) +} + +// Ints invokes `ints` method of contract. +func (c *ContractReader) Ints(i []*big.Int) ([]*big.Int, error) { + return unwrap.ArrayOfBigInts(c.invoker.Call(c.hash, "ints", i)) +} + +// Maps invokes `maps` method of contract. +func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) { + return func(item stackitem.Item, err error) (map[string]string, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) (map[string]string, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string]string) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "maps", m))) +} + +// PublicKey invokes `publicKey` method of contract. +func (c *ContractReader) PublicKey(k *keys.PublicKey) (*keys.PublicKey, error) { + return unwrap.PublicKey(c.invoker.Call(c.hash, "publicKey", k)) +} + +// PublicKeys invokes `publicKeys` method of contract. +func (c *ContractReader) PublicKeys(k keys.PublicKeys) (keys.PublicKeys, error) { + return unwrap.ArrayOfPublicKeys(c.invoker.Call(c.hash, "publicKeys", k)) +} + +// Signature invokes `signature` method of contract. +func (c *ContractReader) Signature(s []byte) ([]byte, error) { + return unwrap.Bytes(c.invoker.Call(c.hash, "signature", s)) +} + +// Signatures invokes `signatures` method of contract. +func (c *ContractReader) Signatures(s [][]byte) ([][]byte, error) { + return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "signatures", s)) +} + +// String invokes `string` method of contract. +func (c *ContractReader) String(s string) (string, error) { + return unwrap.UTF8String(c.invoker.Call(c.hash, "string", s)) +} + +// Strings invokes `strings` method of contract. +func (c *ContractReader) Strings(s []string) ([]string, error) { + return unwrap.ArrayOfUTF8Strings(c.invoker.Call(c.hash, "strings", s)) +} + +// UnnamedStructs invokes `unnamedStructs` method of contract. +func (c *ContractReader) UnnamedStructs() (*Unnamed, error) { + return itemToUnnamed(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructs"))) +} + +// UnnamedStructsX invokes `unnamedStructsX` method of contract. +func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) { + return itemToUnnamedX(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructsX"))) +} + +// itemToUnnamed converts stack item into *Unnamed. +// NULL item is returned as nil pointer without error. +func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(Unnamed) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *Unnamed is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&Unnamed{}) + +// Ensure *Unnamed is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&Unnamed{}) + +// FromStackItem retrieves fields of Unnamed from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *Unnamed) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing Unnamed. +// It implements [stackitem.Convertible] interface. +func (res *Unnamed) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 1) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing Unnamed. +// It implements [smartcontract.Convertible] interface so that Unnamed +// could be used with invokers. +func (res *Unnamed) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 1) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToUnnamedX converts stack item into *UnnamedX. +// NULL item is returned as nil pointer without error. +func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(UnnamedX) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *UnnamedX is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&UnnamedX{}) + +// Ensure *UnnamedX is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&UnnamedX{}) + +// FromStackItem retrieves fields of UnnamedX from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *UnnamedX) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + index++ + res.B, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field B: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing UnnamedX. +// It implements [stackitem.Convertible] interface. +func (res *UnnamedX) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewBool(res.B), error(nil) + if err != nil { + return nil, fmt.Errorf("field B: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing UnnamedX. +// It implements [smartcontract.Convertible] interface so that UnnamedX +// could be used with invokers. +func (res *UnnamedX) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.B) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} diff --git a/cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go new file mode 100644 index 0000000..8db5528 --- /dev/null +++ b/cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go @@ -0,0 +1,567 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package types contains RPC wrappers for Types contract. +package types + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// Unnamed is a contract-specific unnamed type used by its methods. +type Unnamed struct { + I *big.Int +} + +// UnnamedX is a contract-specific unnamedX type used by its methods. +type UnnamedX struct { + I *big.Int + B bool +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + invoker Invoker + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{invoker, hash} +} + +// AAAStrings invokes `aAAStrings` method of contract. +func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) { + return func(item stackitem.Item, err error) ([][][]string, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) ([][][]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][][]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) ([][]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "aAAStrings", s))) +} + +// Any invokes `any` method of contract. +func (c *ContractReader) Any(a any) (any, error) { + return func(item stackitem.Item, err error) (any, error) { + if err != nil { + return nil, err + } + return item.Value(), error(nil) + }(unwrap.Item(c.invoker.Call(c.hash, "any", a))) +} + +// AnyMaps invokes `anyMaps` method of contract. +func (c *ContractReader) AnyMaps(m map[*big.Int]any) (map[*big.Int]any, error) { + return func(item stackitem.Item, err error) (map[*big.Int]any, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) (map[*big.Int]any, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int]any) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := m[i].Value.Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "anyMaps", m))) +} + +// Bool invokes `bool` method of contract. +func (c *ContractReader) Bool(b bool) (bool, error) { + return unwrap.Bool(c.invoker.Call(c.hash, "bool", b)) +} + +// Bools invokes `bools` method of contract. +func (c *ContractReader) Bools(b []bool) ([]bool, error) { + return unwrap.ArrayOfBools(c.invoker.Call(c.hash, "bools", b)) +} + +// Bytes invokes `bytes` method of contract. +func (c *ContractReader) Bytes(b []byte) ([]byte, error) { + return unwrap.Bytes(c.invoker.Call(c.hash, "bytes", b)) +} + +// Bytess invokes `bytess` method of contract. +func (c *ContractReader) Bytess(b [][]byte) ([][]byte, error) { + return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "bytess", b)) +} + +// CrazyMaps invokes `crazyMaps` method of contract. +func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) (map[*big.Int][]map[string][]util.Uint160, error) { + return func(item stackitem.Item, err error) (map[*big.Int][]map[string][]util.Uint160, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) (map[*big.Int][]map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int][]map[string][]util.Uint160) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) ([]map[string][]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]map[string][]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string][]util.Uint160) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "crazyMaps", m))) +} + +// Hash160 invokes `hash160` method of contract. +func (c *ContractReader) Hash160(h util.Uint160) (util.Uint160, error) { + return unwrap.Uint160(c.invoker.Call(c.hash, "hash160", h)) +} + +// Hash160s invokes `hash160s` method of contract. +func (c *ContractReader) Hash160s(h []util.Uint160) ([]util.Uint160, error) { + return unwrap.ArrayOfUint160(c.invoker.Call(c.hash, "hash160s", h)) +} + +// Hash256 invokes `hash256` method of contract. +func (c *ContractReader) Hash256(h util.Uint256) (util.Uint256, error) { + return unwrap.Uint256(c.invoker.Call(c.hash, "hash256", h)) +} + +// Hash256s invokes `hash256s` method of contract. +func (c *ContractReader) Hash256s(h []util.Uint256) ([]util.Uint256, error) { + return unwrap.ArrayOfUint256(c.invoker.Call(c.hash, "hash256s", h)) +} + +// Int invokes `int` method of contract. +func (c *ContractReader) Int(i *big.Int) (*big.Int, error) { + return unwrap.BigInt(c.invoker.Call(c.hash, "int", i)) +} + +// Ints invokes `ints` method of contract. +func (c *ContractReader) Ints(i []*big.Int) ([]*big.Int, error) { + return unwrap.ArrayOfBigInts(c.invoker.Call(c.hash, "ints", i)) +} + +// Maps invokes `maps` method of contract. +func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) { + return func(item stackitem.Item, err error) (map[string]string, error) { + if err != nil { + return nil, err + } + return func(item stackitem.Item) (map[string]string, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string]string) + for i := range m { + k, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Key) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + }(item) + }(unwrap.Item(c.invoker.Call(c.hash, "maps", m))) +} + +// PublicKey invokes `publicKey` method of contract. +func (c *ContractReader) PublicKey(k *keys.PublicKey) (*keys.PublicKey, error) { + return unwrap.PublicKey(c.invoker.Call(c.hash, "publicKey", k)) +} + +// PublicKeys invokes `publicKeys` method of contract. +func (c *ContractReader) PublicKeys(k keys.PublicKeys) (keys.PublicKeys, error) { + return unwrap.ArrayOfPublicKeys(c.invoker.Call(c.hash, "publicKeys", k)) +} + +// Signature invokes `signature` method of contract. +func (c *ContractReader) Signature(s []byte) ([]byte, error) { + return unwrap.Bytes(c.invoker.Call(c.hash, "signature", s)) +} + +// Signatures invokes `signatures` method of contract. +func (c *ContractReader) Signatures(s [][]byte) ([][]byte, error) { + return unwrap.ArrayOfBytes(c.invoker.Call(c.hash, "signatures", s)) +} + +// String invokes `string` method of contract. +func (c *ContractReader) String(s string) (string, error) { + return unwrap.UTF8String(c.invoker.Call(c.hash, "string", s)) +} + +// Strings invokes `strings` method of contract. +func (c *ContractReader) Strings(s []string) ([]string, error) { + return unwrap.ArrayOfUTF8Strings(c.invoker.Call(c.hash, "strings", s)) +} + +// UnnamedStructs invokes `unnamedStructs` method of contract. +func (c *ContractReader) UnnamedStructs() (*Unnamed, error) { + return itemToUnnamed(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructs"))) +} + +// UnnamedStructsX invokes `unnamedStructsX` method of contract. +func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) { + return itemToUnnamedX(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructsX"))) +} + +// itemToUnnamed converts stack item into *Unnamed. +// NULL item is returned as nil pointer without error. +func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(Unnamed) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *Unnamed is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&Unnamed{}) + +// Ensure *Unnamed is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&Unnamed{}) + +// FromStackItem retrieves fields of Unnamed from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *Unnamed) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing Unnamed. +// It implements [stackitem.Convertible] interface. +func (res *Unnamed) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 1) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing Unnamed. +// It implements [smartcontract.Convertible] interface so that Unnamed +// could be used with invokers. +func (res *Unnamed) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 1) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} + +// itemToUnnamedX converts stack item into *UnnamedX. +// NULL item is returned as nil pointer without error. +func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) { + if err != nil { + return nil, err + } + _, null := item.(stackitem.Null) + if null { + return nil, nil + } + var res = new(UnnamedX) + err = res.FromStackItem(item) + return res, err +} + +// Ensure *UnnamedX is a proper [stackitem.Convertible]. +var _ = stackitem.Convertible(&UnnamedX{}) + +// Ensure *UnnamedX is a proper [smartcontract.Convertible]. +var _ = smartcontract.Convertible(&UnnamedX{}) + +// FromStackItem retrieves fields of UnnamedX from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. +// It implements [stackitem.Convertible] interface. +func (res *UnnamedX) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + index++ + res.B, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field B: %w", err) + } + + return nil +} + +// ToStackItem creates [stackitem.Item] representing UnnamedX. +// It implements [stackitem.Convertible] interface. +func (res *UnnamedX) ToStackItem() (stackitem.Item, error) { + if res == nil { + return stackitem.Null{}, nil + } + + var ( + err error + itm stackitem.Item + items = make([]stackitem.Item, 0, 2) + ) + itm, err = (*stackitem.BigInteger)(res.I), error(nil) + if err != nil { + return nil, fmt.Errorf("field I: %w", err) + } + items = append(items, itm) + + itm, err = stackitem.NewBool(res.B), error(nil) + if err != nil { + return nil, fmt.Errorf("field B: %w", err) + } + items = append(items, itm) + + return stackitem.NewStruct(items), nil +} + +// ToSCParameter creates [smartcontract.Parameter] representing UnnamedX. +// It implements [smartcontract.Convertible] interface so that UnnamedX +// could be used with invokers. +func (res *UnnamedX) ToSCParameter() (smartcontract.Parameter, error) { + if res == nil { + return smartcontract.Parameter{Type: smartcontract.AnyType}, nil + } + + var ( + err error + prm smartcontract.Parameter + prms = make([]smartcontract.Parameter, 0, 2) + ) + prm, err = smartcontract.NewParameterFromValue(res.I) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err) + } + prms = append(prms, prm) + + prm, err = smartcontract.NewParameterFromValue(res.B) + if err != nil { + return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err) + } + prms = append(prms, prm) + + return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil +} diff --git a/cli/smartcontract/rpcbindings/verify/rpcbindings_test.go b/cli/smartcontract/rpcbindings/verify/rpcbindings_test.go new file mode 100644 index 0000000..59e82a5 --- /dev/null +++ b/cli/smartcontract/rpcbindings/verify/rpcbindings_test.go @@ -0,0 +1,147 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package verify contains RPC wrappers for verify contract. +package verify + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// HelloWorldEvent represents "Hello world!" event emitted by the contract. +type HelloWorldEvent struct { + Args []any +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor + hash util.Uint160 +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + return &Contract{actor, hash} +} + +func (c *Contract) scriptForVerify() ([]byte, error) { + return smartcontract.CreateCallWithAssertScript(c.hash, "verify") +} + +// Verify creates a transaction invoking `verify` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Verify() (util.Uint256, uint32, error) { + script, err := c.scriptForVerify() + if err != nil { + return util.Uint256{}, 0, err + } + return c.actor.SendRun(script) +} + +// VerifyTransaction creates a transaction invoking `verify` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) { + script, err := c.scriptForVerify() + if err != nil { + return nil, err + } + return c.actor.MakeRun(script) +} + +// VerifyUnsigned creates a transaction invoking `verify` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) { + script, err := c.scriptForVerify() + if err != nil { + return nil, err + } + return c.actor.MakeUnsignedRun(script, nil) +} + +// HelloWorldEventsFromApplicationLog retrieves a set of all emitted events +// with "Hello world!" name from the provided [result.ApplicationLog]. +func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWorldEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*HelloWorldEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Hello world!" { + continue + } + event := new(HelloWorldEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize HelloWorldEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to HelloWorldEvent or +// returns an error if it's not possible to do to so. +func (e *HelloWorldEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 1 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.Args, err = func(item stackitem.Item) ([]any, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]any, len(arr)) + for i := range res { + res[i], err = arr[i].Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Args: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go new file mode 100644 index 0000000..605b37a --- /dev/null +++ b/cli/smartcontract/smart_contract.go @@ -0,0 +1,993 @@ +package smartcontract + +import ( + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/cli/txctx" + "github.com/nspcc-dev/neo-go/pkg/compiler" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" +) + +// addressFlagName and addressFlagAlias are a flag name and its alias +// used for address-related operations. It should be the same within +// the smartcontract package, thus, use this constant. +const ( + addressFlagName = "address" + addressFlagAlias = "a" +) + +var ( + errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag") + errNoMethod = errors.New("no method specified for function invocation command") + errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument") + errFileExist = errors.New("file with given smart-contract name already exists") + addressFlag = &flags.AddressFlag{ + Name: addressFlagName, + Aliases: []string{addressFlagAlias}, + Usage: "Address to use as transaction signee (and gas source)", + } + inFlag = &cli.StringFlag{ + Name: "in", + Aliases: []string{"i"}, + Usage: "Input file for the smart contract (*.nef)", + Action: cmdargs.EnsureNotEmpty("in"), + } + manifestFlag = &cli.StringFlag{ + Name: "manifest", + Aliases: []string{"m"}, + Usage: "Manifest input file (*.manifest.json)", + Action: cmdargs.EnsureNotEmpty("manifest"), + } +) + +// ModVersion contains `pkg/interop` module version +// suitable to be used in go.mod. +var ModVersion string + +const ( + // smartContractTmpl is written to a file when used with `init` command. + // %s is parsed to be the smartContractName. + smartContractTmpl = `package %s + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +var notificationName string + +// init initializes notificationName before calling any other smart-contract method +func init() { + notificationName = "Hello world!" +} + +// RuntimeNotify sends runtime notification with "Hello world!" name +func RuntimeNotify(args []any) { + runtime.Notify(notificationName, args) +}` +) + +// NewCommands returns 'contract' command. +func NewCommands() []*cli.Command { + testInvokeScriptFlags := []cli.Flag{ + &cli.StringFlag{ + Name: "in", + Aliases: []string{"i"}, + Required: true, + Usage: "Input location of the .nef file that needs to be invoked", + Action: cmdargs.EnsureNotEmpty("in"), + }, + options.Historic, + } + testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...) + testInvokeFunctionFlags := []cli.Flag{options.Historic} + testInvokeFunctionFlags = append(testInvokeFunctionFlags, options.RPC...) + invokeFunctionFlags := []cli.Flag{ + addressFlag, + txctx.GasFlag, + txctx.SysGasFlag, + txctx.OutFlag, + txctx.ForceFlag, + txctx.AwaitFlag, + } + invokeFunctionFlags = append(invokeFunctionFlags, options.Wallet...) + invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...) + deployFlags := append(invokeFunctionFlags, + cloneFlag(inFlag, true), + cloneFlag(manifestFlag, true), + ) + updateFlags := append([]cli.Flag{ + cloneFlag(inFlag, false), + cloneFlag(manifestFlag, false), + &cli.BoolFlag{ + Name: "strict", + Usage: "Fail immediately on NEP-22 nil-data call failure (skip 2-param fallback attempt)", + }, + }, invokeFunctionFlags...) + manifestAddGroupFlags := append([]cli.Flag{ + &flags.AddressFlag{ + Name: "sender", + Aliases: []string{"s"}, + Required: true, + Usage: "Deploy transaction sender", + }, + &flags.AddressFlag{ + Name: addressFlagName, // use the same name for handler code unification. + Aliases: []string{addressFlagAlias}, + Required: true, + Usage: "Account to sign group with", + }, + &cli.StringFlag{ + Name: "nef", + Aliases: []string{"n"}, + Required: true, + Usage: "Path to the NEF file", + Action: cmdargs.EnsureNotEmpty("nef"), + }, + &cli.StringFlag{ + Name: "manifest", + Aliases: []string{"m"}, + Required: true, + Usage: "Path to the manifest", + Action: cmdargs.EnsureNotEmpty("manifest"), + }, + }, options.Wallet...) + return []*cli.Command{{ + Name: "contract", + Usage: "Compile - debug - deploy smart contracts", + Subcommands: []*cli.Command{ + { + Name: "compile", + Usage: "Compile a smart contract to a .nef file", + UsageText: "neo-go contract compile -i path [-o nef] [-v] [-d] [-m manifest] [-c yaml] [--bindings file] [--no-standards] [--no-events] [--no-permissions] [--guess-eventtypes]", + Description: `Compiles given smart contract to a .nef file and emits other associated + information (manifest, bindings configuration, debug information files) if + asked to. If none of --out, --manifest, --config, --bindings flags are specified, + then the output filenames for these flags will be guessed using the contract + name or path provided via --in option by trimming/adding corresponding suffixes + to the common part of the path. In the latter case the configuration filepath + will be guessed from the --in option using the same rule. +`, + Action: contractCompile, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "in", + Aliases: []string{"i"}, + Required: true, + Usage: "Input file for the smart contract to be compiled (*.go file or directory)", + Action: cmdargs.EnsureNotEmpty("in"), + }, + &cli.StringFlag{ + Name: "out", + Aliases: []string{"o"}, + Usage: "Output of the compiled contract", + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Print out additional information after a compiling", + }, + &cli.StringFlag{ + Name: "debug", + Aliases: []string{"d"}, + Usage: "Emit debug info in a separate file", + }, + &cli.StringFlag{ + Name: "manifest", + Aliases: []string{"m"}, + Usage: "Emit contract manifest (*.manifest.json) file into separate file using configuration input file (*.yml)", + }, + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + Usage: "Configuration input file (*.yml)", + }, + &cli.BoolFlag{ + Name: "no-standards", + Usage: "Do not check compliance with supported standards", + }, + &cli.BoolFlag{ + Name: "no-events", + Usage: "Do not check emitted events with the manifest", + }, + &cli.BoolFlag{ + Name: "no-permissions", + Usage: "Do not check if invoked contracts are allowed in manifest", + }, + &cli.BoolFlag{ + Name: "guess-eventtypes", + Usage: "Guess event types for smart-contract bindings configuration from the code usages", + }, + &cli.StringFlag{ + Name: "bindings", + Usage: "Output file for smart-contract bindings configuration", + }, + }, + }, + { + Name: "deploy", + Usage: "Deploy a smart contract (.nef with description)", + UsageText: "neo-go contract deploy -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] --in contract.nef --manifest contract.manifest.json [--out file] [--force] [--await] [data]", + Description: `Deploys given contract into the chain. The gas parameter is for additional + gas to be added as a network fee to prioritize the transaction. The data + parameter is an optional parameter to be passed to '_deploy' method. When + --await flag is specified, it waits for the transaction to be included + in a block. +`, + Action: contractDeploy, + Flags: deployFlags, + }, + { + Name: "update", + Usage: "Update deployed smart contract on the blockchain", + UsageText: "neo-go contract update -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] --in contract.nef --manifest contract.manifest.json [--out file] [--force] [--await] [--strict] scripthash [data...] [--] [signers...]", + Description: `Updates deployed contract on the chain. The gas parameter is for additional + gas to be added as a network fee to prioritize the transaction. The data + parameter is an optional parameter to be passed to '_deploy' method. When + --await flag is specified, it waits for the transaction to be included + in a block. +`, + Action: contractUpdate, + Flags: updateFlags, + }, + { + Name: "destroy", + Usage: "Destroy deployed smart contract", + UsageText: "neo-go contract destroy -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] [--out file] [--force] [--await] scripthash [--] [signers...]", + Description: `Destroys deployed contract. The gas parameter is for additional + gas to be added as a network fee to prioritize the transaction. When --await flag is + specified, it waits for the transaction to be included in a block. +`, + Action: contractDestroy, + Flags: invokeFunctionFlags, + }, + generateWrapperCmd, + generateRPCWrapperCmd, + { + Name: "invokefunction", + Usage: "Invoke deployed contract on the blockchain", + UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] [--out file] [--force] [--await] scripthash [method] [arguments...] [--] [signers...]", + Description: `Executes given (as a script hash) deployed script with the given method, + arguments and signers. Sender is included in the list of signers by default + with None witness scope. If you'd like to change default sender's scope, + specify it via signers parameter. See testinvokefunction documentation for + the details about parameters. It differs from testinvokefunction in that this + command sends an invocation transaction to the network. When --await flag is + specified, it waits for the transaction to be included in a block. +`, + Action: invokeFunction, + Flags: invokeFunctionFlags, + }, + { + Name: "testinvokefunction", + Usage: "Invoke deployed contract on the blockchain (test mode)", + UsageText: "neo-go contract testinvokefunction -r endpoint [--historic index/hash] scripthash [method] [arguments...] [--] [signers...]", + Description: `Executes given (as a script hash) deployed script with the given method, + arguments and signers (sender is not included by default). If no method is given + "" is passed to the script, if no arguments are given, an empty array is + passed, if no signers are given no array is passed. If signers are specified, + the first one of them is treated as a sender. All of the given arguments are + encapsulated into array before invoking the script. The script thus should + follow the regular convention of smart contract arguments (method string and + an array of other arguments). + +` + cmdargs.ParamsParsingDoc + ` + +` + cmdargs.SignersParsingDoc + ` +`, + Action: testInvokeFunction, + Flags: testInvokeFunctionFlags, + }, + { + Name: "testinvokescript", + Usage: "Invoke compiled AVM code in NEF format on the blockchain (test mode, not creating a transaction for it)", + UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [--historic index/hash] [signers...]", + Description: `Executes given compiled AVM instructions in NEF format with the given set of + signers not included sender by default. See testinvokefunction documentation + for the details about parameters. +`, + Action: testInvokeScript, + Flags: testInvokeScriptFlags, + }, + { + Name: "init", + Usage: "Initialize a new smart-contract in a directory with boiler plate code", + UsageText: "neo-go contract init -n name [--skip-details]", + Action: initSmartContract, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Aliases: []string{"n"}, + Required: true, + Usage: "Name of the smart-contract to be initialized", + Action: cmdargs.EnsureNotEmpty("name"), + }, + &cli.BoolFlag{ + Name: "skip-details", + Aliases: []string{"skip"}, + Usage: "Skip filling in the projects and contract details", + }, + }, + }, + { + Name: "inspect", + Usage: "Creates a user readable dump of the program instructions", + UsageText: "neo-go contract inspect -i file [-c]", + Action: inspect, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "compile", + Aliases: []string{"c"}, + Usage: "Compile input file (it should be go code then)", + }, + &cli.StringFlag{ + Name: "in", + Aliases: []string{"i"}, + Required: true, + Usage: "Input file of the program (either .go or .nef)", + Action: cmdargs.EnsureNotEmpty("in"), + }, + }, + }, + { + Name: "calc-hash", + Usage: "Calculates hash of a contract after deployment", + UsageText: "neo-go contract calc-hash -i nef -m manifest -s address", + Action: calcHash, + Flags: []cli.Flag{ + &flags.AddressFlag{ + Name: "sender", + Aliases: []string{"s"}, + Required: true, + Usage: "Sender script hash or address", + }, + &cli.StringFlag{ + Name: "in", + Required: true, + Usage: "Path to NEF file", + Action: cmdargs.EnsureNotEmpty("in"), + }, + &cli.StringFlag{ + Name: "manifest", + Aliases: []string{"m"}, + Required: true, + Usage: "Path to manifest file", + Action: cmdargs.EnsureNotEmpty("manifest"), + }, + }, + }, + { + Name: "manifest", + Usage: "Manifest-related commands", + Subcommands: []*cli.Command{ + { + Name: "add-group", + Usage: "Adds group to the manifest", + UsageText: "neo-go contract manifest add-group -w wallet [--wallet-config path] -n nef -m manifest -a address -s address", + Action: manifestAddGroup, + Flags: manifestAddGroupFlags, + }, + }, + }, + }, + }} +} + +// initSmartContract initializes a given directory with some boiler plate code. +func initSmartContract(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + contractName := ctx.String("name") + + // Check if the file already exists, if yes, exit + if _, err := os.Stat(contractName); err == nil { + return cli.Exit(errFileExist, 1) + } + + basePath := contractName + contractName = filepath.Base(contractName) + fileName := "main.go" + + // create base directory + if err := os.Mkdir(basePath, os.ModePerm); err != nil { + return cli.Exit(err, 1) + } + + m := ProjectConfig{ + Name: contractName, + SourceURL: "http://example.com/", + SupportedStandards: []string{}, + SafeMethods: []string{}, + Events: []compiler.HybridEvent{ + { + Name: "Hello world!", + Parameters: []compiler.HybridParameter{ + { + Parameter: manifest.Parameter{ + Name: "args", + Type: smartcontract.ArrayType, + }, + }, + }, + }, + }, + Permissions: []permission{permission(*manifest.NewPermission(manifest.PermissionWildcard))}, + } + b, err := yaml.Marshal(m) + if err != nil { + return cli.Exit(err, 1) + } + if err := os.WriteFile(filepath.Join(basePath, "neo-go.yml"), b, 0644); err != nil { + return cli.Exit(err, 1) + } + + ver := ModVersion + if ver == "" { + ver = "latest" + } + + gm := []byte("module " + contractName + ` + +go 1.24 + +require ( + github.com/nspcc-dev/neo-go/pkg/interop ` + ver + ` +)`) + if err := os.WriteFile(filepath.Join(basePath, "go.mod"), gm, 0644); err != nil { + return cli.Exit(err, 1) + } + + data := []byte(fmt.Sprintf(smartContractTmpl, contractName)) + if err := os.WriteFile(filepath.Join(basePath, fileName), data, 0644); err != nil { + return cli.Exit(err, 1) + } + + fmt.Fprintf(ctx.App.Writer, "Successfully initialized smart contract [%s]\n", contractName) + + return nil +} + +func contractCompile(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + src := ctx.String("in") + manifestFile := ctx.String("manifest") + confFile := ctx.String("config") + debugFile := ctx.String("debug") + out := ctx.String("out") + bindings := ctx.String("bindings") + if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 0) { + return cli.Exit(errNoConfFile, 1) + } + autocomplete := len(manifestFile) == 0 && + len(confFile) == 0 && + len(out) == 0 && + len(bindings) == 0 + if autocomplete { + var root string + fileInfo, err := os.Stat(src) + if err != nil { + return cli.Exit(fmt.Errorf("failed to stat source file or directory: %w", err), 1) + } + if fileInfo.IsDir() { + base := filepath.Base(fileInfo.Name()) + if base == string(filepath.Separator) { + base = "contract" + } + root = filepath.Join(src, base) + } else { + root = strings.TrimSuffix(src, ".go") + } + manifestFile = root + ".manifest.json" + confFile = root + ".yml" + out = root + ".nef" + bindings = root + ".bindings.yml" + } + + o := &compiler.Options{ + Outfile: out, + + DebugInfo: debugFile, + ManifestFile: manifestFile, + BindingsFile: bindings, + + NoStandardCheck: ctx.Bool("no-standards"), + NoEventsCheck: ctx.Bool("no-events"), + NoPermissionsCheck: ctx.Bool("no-permissions"), + + GuessEventTypes: ctx.Bool("guess-eventtypes"), + } + + if len(confFile) != 0 { + conf, err := ParseContractConfig(confFile) + if err != nil { + return err + } + o.Name = conf.Name + o.SourceURL = conf.SourceURL + o.ContractEvents = conf.Events + o.DeclaredNamedTypes = conf.NamedTypes + o.ContractSupportedStandards = conf.SupportedStandards + o.Permissions = make([]manifest.Permission, len(conf.Permissions)) + for i := range conf.Permissions { + o.Permissions[i] = manifest.Permission(conf.Permissions[i]) + } + o.SafeMethods = conf.SafeMethods + o.Overloads = conf.Overloads + } + + result, err := compiler.CompileAndSave(src, o) + if err != nil { + return cli.Exit(err, 1) + } + if ctx.Bool("verbose") { + fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(result)) + } + + return nil +} + +func calcHash(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + sender := ctx.Generic("sender").(*flags.Address) + p := ctx.String("in") + mpath := ctx.String("manifest") + + f, err := os.ReadFile(p) + if err != nil { + return cli.Exit(fmt.Errorf("can't read .nef file: %w", err), 1) + } + nefFile, err := nef.FileFromBytes(f) + if err != nil { + return cli.Exit(fmt.Errorf("can't unmarshal .nef file: %w", err), 1) + } + manifestBytes, err := os.ReadFile(mpath) + if err != nil { + return cli.Exit(fmt.Errorf("failed to read manifest file: %w", err), 1) + } + m := &manifest.Manifest{} + err = json.Unmarshal(manifestBytes, m) + if err != nil { + return cli.Exit(fmt.Errorf("failed to restore manifest file: %w", err), 1) + } + fmt.Fprintln(ctx.App.Writer, "Contract hash:", state.CreateContractHash(sender.Uint160(), nefFile.Checksum, m.Name).StringLE()) + return nil +} + +func testInvokeFunction(ctx *cli.Context) error { + return invokeInternal(ctx, false) +} + +func invokeFunction(ctx *cli.Context) error { + return invokeInternal(ctx, true) +} + +func invokeInternal(ctx *cli.Context, signAndPush bool) error { + var ( + err error + exitErr cli.ExitCoder + operation string + params []any + paramsStart = 1 + scParams []smartcontract.Parameter + cosigners []transaction.Signer + cosignersOffset = 0 + ) + + args := ctx.Args() + if !args.Present() { + return cli.Exit(errNoScriptHash, 1) + } + argsSlice := args.Slice() + script, err := flags.ParseAddress(argsSlice[0]) + if err != nil { + return cli.Exit(fmt.Errorf("incorrect script hash: %w", err), 1) + } + if len(argsSlice) <= 1 { + return cli.Exit(errNoMethod, 1) + } + operation = argsSlice[1] + paramsStart++ + + if len(argsSlice) > paramsStart { + cosignersOffset, scParams, err = cmdargs.ParseParams(argsSlice[paramsStart:], true) + if err != nil { + return cli.Exit(err, 1) + } + params = make([]any, len(scParams)) + for i := range scParams { + params[i] = scParams[i] + } + } + + cosignersStart := paramsStart + cosignersOffset + cosigners, exitErr = cmdargs.GetSignersFromContext(ctx, cosignersStart) + if exitErr != nil { + return exitErr + } + + var ( + acc *wallet.Account + w *wallet.Wallet + ) + if signAndPush { + acc, w, err = options.GetAccFromContext(ctx) + if err != nil { + return cli.Exit(err, 1) + } + defer w.Close() + } + + return invokeWithArgs(ctx, acc, w, script, operation, params, cosigners) +} + +func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []any, cosigners []transaction.Signer) error { + var ( + err error + signersAccounts []actor.SignerAccount + resp *result.Invoke + signAndPush = acc != nil + inv *invoker.Invoker + act *actor.Actor + ) + if signAndPush { + signersAccounts, err = cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.None) + if err != nil { + return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1) + } + } + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + if signAndPush { + _, act, err = options.GetRPCWithActor(gctx, ctx, signersAccounts) + if err != nil { + return err + } + inv = &act.Invoker + } else { + _, inv, err = options.GetRPCWithInvoker(gctx, ctx, cosigners) + if err != nil { + return err + } + } + out := ctx.String("out") + resp, err = inv.Call(script, operation, params...) + if err != nil { + return cli.Exit(err, 1) + } + if resp.State != "HALT" { + errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s", resp.State, resp.FaultException) + if !signAndPush { + return cli.Exit(errText, 1) + } + + action := "send" + process := "Sending" + if out != "" { + action = "save" + process = "Saving" + } + if !ctx.Bool("force") { + return cli.Exit(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1) + } + fmt.Fprintln(ctx.App.Writer, errText+".\n"+process+" transaction...") + } + if !signAndPush { + b, err := json.MarshalIndent(resp, "", " ") + if err != nil { + return cli.Exit(err, 1) + } + + fmt.Fprintln(ctx.App.Writer, string(b)) + return nil + } + if len(resp.Script) == 0 { + return cli.Exit(errors.New("no script returned from the RPC node"), 1) + } + tx, err := act.MakeUnsignedUncheckedRun(resp.Script, resp.GasConsumed, nil) + if err != nil { + return cli.Exit(fmt.Errorf("failed to create tx: %w", err), 1) + } + return txctx.SignAndSend(ctx, act, acc, tx) +} + +func testInvokeScript(ctx *cli.Context) error { + src := ctx.String("in") + b, err := os.ReadFile(src) + if err != nil { + return cli.Exit(err, 1) + } + nefFile, err := nef.FileFromBytes(b) + if err != nil { + return cli.Exit(fmt.Errorf("failed to restore .nef file: %w", err), 1) + } + + signers, exitErr := cmdargs.GetSignersFromContext(ctx, 0) + if exitErr != nil { + return exitErr + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + _, inv, err := options.GetRPCWithInvoker(gctx, ctx, signers) + if err != nil { + return err + } + + resp, err := inv.Run(nefFile.Script) + if err != nil { + return cli.Exit(err, 1) + } + + b, err = json.MarshalIndent(resp, "", " ") + if err != nil { + return cli.Exit(err, 1) + } + + fmt.Fprintln(ctx.App.Writer, string(b)) + + return nil +} + +// ProjectConfig contains project metadata. +type ProjectConfig struct { + Name string + SourceURL string + SafeMethods []string + SupportedStandards []string + Events []compiler.HybridEvent + Permissions []permission + Overloads map[string]string `yaml:"overloads,omitempty"` + NamedTypes map[string]binding.ExtendedType `yaml:"namedtypes,omitempty"` +} + +func inspect(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + in := ctx.String("in") + compile := ctx.Bool("compile") + var ( + b []byte + err error + ) + if compile { + b, err = compiler.Compile(in, nil) + if err != nil { + return cli.Exit(fmt.Errorf("failed to compile: %w", err), 1) + } + } else { + f, err := os.ReadFile(in) + if err != nil { + return cli.Exit(fmt.Errorf("failed to read .nef file: %w", err), 1) + } + nefFile, err := nef.FileFromBytes(f) + if err != nil { + return cli.Exit(fmt.Errorf("failed to restore .nef file: %w", err), 1) + } + b = nefFile.Script + } + v := vm.New() + v.LoadScript(b) + v.PrintOps(ctx.App.Writer) + + return nil +} + +// contractDeploy deploys contract. +func contractDeploy(ctx *cli.Context) error { + nefFile, f, err := readNEFFile(ctx.String("in")) + if err != nil { + return cli.Exit(err, 1) + } + + m, manifestBytes, err := readManifest(ctx.String("manifest"), util.Uint160{}) + if err != nil { + return cli.Exit(fmt.Errorf("failed to read manifest file: %w", err), 1) + } + + var appCallParams = []any{f, manifestBytes} + + signOffset, data, err := cmdargs.ParseParams(ctx.Args().Slice(), true) + if err != nil { + return cli.Exit(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1) + } + if len(data) > 1 { + return cli.Exit("'data' should be represented as a single parameter", 1) + } + if len(data) != 0 { + appCallParams = append(appCallParams, data[0]) + } + + acc, w, err := options.GetAccFromContext(ctx) + if err != nil { + return cli.Exit(fmt.Errorf("can't get sender address: %w", err), 1) + } + defer w.Close() + sender := acc.ScriptHash() + + cosigners, sgnErr := cmdargs.GetSignersFromContext(ctx, signOffset) + if sgnErr != nil { + return err + } else if len(cosigners) == 0 { + cosigners = []transaction.Signer{{ + Account: acc.Contract.ScriptHash(), + Scopes: transaction.CalledByEntry, + }} + } + + extErr := invokeWithArgs(ctx, acc, w, management.Hash, "deploy", appCallParams, cosigners) + if extErr != nil { + return extErr + } + + hash := state.CreateContractHash(sender, nefFile.Checksum, m.Name) + fmt.Fprintf(ctx.App.Writer, "Contract: %s\n", hash.StringLE()) + return nil +} + +// contractUpdate updates an existing smart contract. +func contractUpdate(ctx *cli.Context) error { + var ( + paramsStart = 0 + args []string + nefBytes []byte + manifestBytes []byte + nefParam any + manifestParam any + ) + args = ctx.Args().Slice() + if len(args) < 1 { + return cli.Exit(errNoScriptHash, 1) + } + contractHash, err := flags.ParseAddress(args[0]) + if err != nil { + return cli.Exit(fmt.Errorf("invalid contract hash '%s': %w", args[0], err), 1) + } + paramsStart++ + + if ctx.String("in") != "" { + _, nefBytes, err = readNEFFile(ctx.String("in")) + if err != nil { + return cli.Exit(fmt.Errorf("failed to read .nef file: %w", err), 1) + } + nefParam = nefBytes + } + if ctx.String("manifest") != "" { + _, manifestBytes, err = readManifest(ctx.String("manifest"), contractHash) + if err != nil { + return cli.Exit(fmt.Errorf("failed to read manifest file: %w", err), 1) + } + manifestParam = manifestBytes + } + if nefBytes == nil && manifestBytes == nil { + return cli.Exit(fmt.Errorf("either manifest or .nef required"), 1) + } + + params := []any{nefParam, manifestParam} + + signOffset, data, err := cmdargs.ParseParams(args[paramsStart:], true) + if err != nil { + return cli.Exit(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1) + } + if len(data) > 1 { + return cli.Exit("'data' should be represented as a single parameter", 1) + } + + acc, w, err := options.GetAccFromContext(ctx) + if err != nil { + return cli.Exit(fmt.Errorf("can't get sender address: %w", err), 1) + } + defer w.Close() + + cosigners, exitErr := cmdargs.GetSignersFromContext(ctx, paramsStart+signOffset) + if exitErr != nil { + return exitErr + } + if len(cosigners) == 0 { + cosigners = []transaction.Signer{{ + Account: acc.Contract.ScriptHash(), + Scopes: transaction.CalledByEntry, + }} + } + if len(data) != 0 { + params = append(params, data[0]) + return invokeWithArgs(ctx, acc, w, contractHash, "update", params, cosigners) + } + + // First, try the NEP-22-style update (script, manifest, data) + // (or pass nil as “data” for NEP-22 compatibility). + params = append(params, nil) + err = invokeWithArgs(ctx, acc, w, contractHash, "update", params, cosigners) + // If that fails (and --strict isn’t set), fallback to the original format + // which expects only (script, manifest) with no extra data. + if err == nil || ctx.Bool("strict") || + !strings.Contains(err.Error(), "System.Contract.Call failed: method not found: update/3.") && + !strings.Contains(err.Error(), `Method "update" with 3 parameter(s) doesn't exist in the contract`) { + return err + } + return invokeWithArgs(ctx, acc, w, contractHash, "update", params[:2], cosigners) +} + +// contractDestroy destroys an existing smart contract. +func contractDestroy(ctx *cli.Context) error { + var ( + signOffset = 1 + paramsStart = 0 + args []string + ) + args = ctx.Args().Slice() + if len(args) < 1 { + return cli.Exit(errNoScriptHash, 1) + } + contractHash, err := flags.ParseAddress(args[0]) + if err != nil { + return cli.Exit(fmt.Errorf("invalid contract hash '%s': %w", args[0], err), 1) + } + paramsStart++ + + acc, w, err := options.GetAccFromContext(ctx) + if err != nil { + return cli.Exit(fmt.Errorf("can't get sender address: %w", err), 1) + } + defer w.Close() + + cosigners, exitErr := cmdargs.GetSignersFromContext(ctx, paramsStart+signOffset) + if exitErr != nil { + return exitErr + } + if len(cosigners) == 0 { + cosigners = []transaction.Signer{{ + Account: acc.Contract.ScriptHash(), + Scopes: transaction.CalledByEntry, + }} + } + + return invokeWithArgs(ctx, acc, w, contractHash, "destroy", nil, cosigners) +} + +// ParseContractConfig reads contract configuration file (.yaml) and returns unmarshalled ProjectConfig. +func ParseContractConfig(confFile string) (ProjectConfig, error) { + conf := ProjectConfig{} + confBytes, err := os.ReadFile(confFile) + if err != nil { + return conf, cli.Exit(err, 1) + } + + err = yaml.Unmarshal(confBytes, &conf) + if err != nil { + return conf, cli.Exit(fmt.Errorf("bad config: %w", err), 1) + } + return conf, nil +} + +func cloneFlag(p *cli.StringFlag, required bool) *cli.StringFlag { + f := *p + f.Required = required + return &f +} diff --git a/cli/smartcontract/smart_contract_test.go b/cli/smartcontract/smart_contract_test.go new file mode 100644 index 0000000..7bab336 --- /dev/null +++ b/cli/smartcontract/smart_contract_test.go @@ -0,0 +1,138 @@ +package smartcontract + +import ( + "flag" + "os" + "testing" + + "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v3" +) + +func TestInitSmartContract(t *testing.T) { + d := t.TempDir() + testWD, err := os.Getwd() + require.NoError(t, err) + err = os.Chdir(d) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, os.Chdir(testWD)) }) + contractName := "testContract" + + set := flag.NewFlagSet("flagSet", flag.ExitOnError) + set.String("name", contractName, "") + ctx := cli.NewContext(cli.NewApp(), set, nil) + require.NoError(t, initSmartContract(ctx)) + dirInfo, err := os.Stat(contractName) + require.NoError(t, err) + require.True(t, dirInfo.IsDir()) + files, err := os.ReadDir(contractName) + require.NoError(t, err) + require.Equal(t, 3, len(files)) + require.Equal(t, "go.mod", files[0].Name()) + require.Equal(t, "main.go", files[1].Name()) + require.Equal(t, "neo-go.yml", files[2].Name()) + main, err := os.ReadFile(contractName + "/" + files[1].Name()) + require.NoError(t, err) + require.Equal(t, + `package `+contractName+` + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +var notificationName string + +// init initializes notificationName before calling any other smart-contract method +func init() { + notificationName = "Hello world!" +} + +// RuntimeNotify sends runtime notification with "Hello world!" name +func RuntimeNotify(args []any) { + runtime.Notify(notificationName, args) +}`, string(main)) + + manifest, err := os.ReadFile(contractName + "/" + files[2].Name()) + require.NoError(t, err) + expected := `name: testContract +sourceurl: http://example.com/ +safemethods: [] +supportedstandards: [] +events: + - name: Hello world! + parameters: + - name: args + type: Array +permissions: + - methods: '*' +` + require.Equal(t, expected, string(manifest)) +} + +func testPermissionMarshal(t *testing.T, p *manifest.Permission, expected string) { + out, err := yaml.Marshal((*permission)(p)) + require.NoError(t, err) + require.Equal(t, expected, string(out)) + + t.Run("Unmarshal", func(t *testing.T) { + actual := new(permission) + require.NoError(t, yaml.Unmarshal(out, actual)) + require.Equal(t, p, (*manifest.Permission)(actual)) + }) +} + +func TestPermissionMarshal(t *testing.T) { + t.Run("Wildcard", func(t *testing.T) { + p := manifest.NewPermission(manifest.PermissionWildcard) + testPermissionMarshal(t, p, "methods: '*'\n") + }) + t.Run("no allowed methods", func(t *testing.T) { + p := manifest.NewPermission(manifest.PermissionWildcard) + p.Methods.Restrict() + testPermissionMarshal(t, p, "methods: []\n") + }) + t.Run("hash", func(t *testing.T) { + h := random.Uint160() + p := manifest.NewPermission(manifest.PermissionHash, h) + testPermissionMarshal(t, p, + "hash: "+h.StringLE()+"\n"+ + "methods: '*'\n") + }) + t.Run("group with some methods", func(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + p := manifest.NewPermission(manifest.PermissionGroup, priv.PublicKey()) + p.Methods.Add("abc") + p.Methods.Add("lamao") + testPermissionMarshal(t, p, + "group: "+priv.PublicKey().StringCompressed()+"\n"+ + "methods:\n - abc\n - lamao\n") + }) +} + +func TestPermissionUnmarshalInvalid(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + pub := priv.PublicKey().StringCompressed() + u160 := random.Uint160().StringLE() + testCases := []string{ + "hash: []\nmethods: '*'\n", // invalid hash type + "hash: notahex\nmethods: '*'\n", // invalid hash + "group: []\nmethods: '*'\n", // invalid group type + "group: notahex\nmethods: '*'\n", // invalid group + "hash: " + u160 + "\n", // missing methods + "group: " + pub + "\nhash: " + u160 + "\nmethods: '*'", // hash/group conflict + "hash: " + u160 + "\nmethods:\n a: b\n", // invalid methods type + "hash: " + u160 + "\nmethods:\n- []\n", // methods array, invalid single + } + + for _, tc := range testCases { + t.Run(tc, func(t *testing.T) { + require.Error(t, yaml.Unmarshal([]byte(tc), new(permission))) + }) + } +} diff --git a/cli/smartcontract/testdata/deploy/main.go b/cli/smartcontract/testdata/deploy/main.go new file mode 100644 index 0000000..0017e0b --- /dev/null +++ b/cli/smartcontract/testdata/deploy/main.go @@ -0,0 +1,91 @@ +package deploy + +import ( + "github.com/nspcc-dev/neo-go/cli/smartcontract/testdata/deploy/sub" + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/iterator" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/interop/storage" +) + +var key = "key" + +const mgmtKey = "mgmt" + +func _deploy(data any, isUpdate bool) { + var value string + + ctx := storage.GetContext() + if isUpdate { + value = "on update" + } else { + value = "on create" + sh := runtime.GetCallingScriptHash() + storage.Put(ctx, mgmtKey, sh) + + if data != nil { + arr := data.([]any) + for i := 0; i < len(arr)-1; i += 2 { + storage.Put(ctx, arr[i], arr[i+1]) + } + } + } + + storage.Put(ctx, key, value) +} + +// Fail just fails. +func Fail() { + panic("as expected") +} + +// CheckSenderWitness checks sender's witness. +func CheckSenderWitness() { + tx := runtime.GetScriptContainer() + if !runtime.CheckWitness(tx.Sender) { + panic("not witnessed") + } +} + +// Update updates the contract with a new one. +func Update(script, manifest []byte, data any) { + ctx := storage.GetReadOnlyContext() + mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) + contract.Call(mgmt, "update", contract.All, script, manifest, data) +} + +// GetValue returns the stored value. +func GetValue() string { + ctx := storage.GetReadOnlyContext() + val1 := storage.Get(ctx, key) + val2 := storage.Get(ctx, sub.Key) + return val1.(string) + "|" + val2.(string) +} + +// GetValueWithKey returns the stored value with the specified key. +func GetValueWithKey(key string) string { + ctx := storage.GetReadOnlyContext() + return storage.Get(ctx, key).(string) +} + +// TestFind finds items with the specified prefix. +func TestFind(f storage.FindFlags) []any { + ctx := storage.GetContext() + storage.Put(ctx, "findkey1", "value1") + storage.Put(ctx, "findkey2", "value2") + + var result []any + iter := storage.Find(ctx, "findkey", f) + for iterator.Next(iter) { + result = append(result, iterator.Value(iter)) + } + return result +} + +// Destroy destroys the contract. +func Destroy() { + ctx := storage.GetReadOnlyContext() + mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) + contract.Call(mgmt, "destroy", contract.All) +} diff --git a/cli/smartcontract/testdata/deploy/neo-go.yml b/cli/smartcontract/testdata/deploy/neo-go.yml new file mode 100644 index 0000000..a50fc6e --- /dev/null +++ b/cli/smartcontract/testdata/deploy/neo-go.yml @@ -0,0 +1,4 @@ +name: Test deploy +permissions: + - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd + methods: ["update", "destroy"] diff --git a/cli/smartcontract/testdata/deploy/sub/put.go b/cli/smartcontract/testdata/deploy/sub/put.go new file mode 100644 index 0000000..08f6347 --- /dev/null +++ b/cli/smartcontract/testdata/deploy/sub/put.go @@ -0,0 +1,14 @@ +package sub + +import "github.com/nspcc-dev/neo-go/pkg/interop/storage" + +var Key = "sub" + +func _deploy(data any, isUpdate bool) { + ctx := storage.GetContext() + value := "sub create" + if isUpdate { + value = "sub update" + } + storage.Put(ctx, Key, value) +} diff --git a/cli/smartcontract/testdata/deploy/update.manifest.json b/cli/smartcontract/testdata/deploy/update.manifest.json new file mode 100755 index 0000000..5fcf685 --- /dev/null +++ b/cli/smartcontract/testdata/deploy/update.manifest.json @@ -0,0 +1 @@ +{"name":"Test deploy","abi":{"methods":[{"name":"_initialize","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"_deploy","offset":15,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"checkSenderWitness","offset":201,"parameters":[],"returntype":"Void","safe":false},{"name":"fail","offset":186,"parameters":[],"returntype":"Void","safe":false},{"name":"getValueUpdated","offset":294,"parameters":[],"returntype":"String","safe":false},{"name":"getValueWithKey","offset":343,"parameters":[{"name":"key","type":"String"}],"returntype":"String","safe":false},{"name":"testFind","offset":368,"parameters":[{"name":"f","type":"InteropInterface"}],"returntype":"Array","safe":false},{"name":"update","offset":238,"parameters":[{"name":"script","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"0xfffdc93764dbaddd97c48f252a53ea4643faa3fd","methods":["update"]}],"supportedstandards":[],"trusts":[],"extra":null} diff --git a/cli/smartcontract/testdata/deploy/updated.go b/cli/smartcontract/testdata/deploy/updated.go new file mode 100644 index 0000000..fb6681d --- /dev/null +++ b/cli/smartcontract/testdata/deploy/updated.go @@ -0,0 +1,6 @@ +package deploy + +// NewMethod in updated contract. +func NewMethod() int { + return 42 +} diff --git a/cli/smartcontract/testdata/gas/gas.go b/cli/smartcontract/testdata/gas/gas.go new file mode 100644 index 0000000..b5fb268 --- /dev/null +++ b/cli/smartcontract/testdata/gas/gas.go @@ -0,0 +1,52 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package gastoken contains RPC wrappers for GasToken contract. +package gastoken + +import ( + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep17.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep17.Actor +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep17.TokenReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep17.TokenWriter + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + var nep17t = nep17.New(actor, hash) + return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash} +} diff --git a/cli/smartcontract/testdata/gas/gas.manifest.json b/cli/smartcontract/testdata/gas/gas.manifest.json new file mode 100644 index 0000000..b2421f4 --- /dev/null +++ b/cli/smartcontract/testdata/gas/gas.manifest.json @@ -0,0 +1 @@ +{"name":"GasToken","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"symbol","parameters":[],"returntype":"String","offset":14,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":21,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":28,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null} diff --git a/cli/smartcontract/testdata/invalid1/invalid.go b/cli/smartcontract/testdata/invalid1/invalid.go new file mode 100644 index 0000000..3bdf220 --- /dev/null +++ b/cli/smartcontract/testdata/invalid1/invalid.go @@ -0,0 +1,21 @@ +// invalid is an example of a contract which doesn't pass event check. +package invalid1 + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +// Notify1 emits a correctly typed event. +func Notify1() bool { + runtime.Notify("Event", interop.Hash160{1, 2, 3}) + + return true +} + +// Notify2 emits an invalid event (ByteString instead of Hash160). +func Notify2() bool { + runtime.Notify("Event", []byte{1, 2, 3}) + + return true +} diff --git a/cli/smartcontract/testdata/invalid1/invalid.yml b/cli/smartcontract/testdata/invalid1/invalid.yml new file mode 100644 index 0000000..73839ca --- /dev/null +++ b/cli/smartcontract/testdata/invalid1/invalid.yml @@ -0,0 +1,7 @@ +name: "Invalid example" +supportedstandards: [] +events: + - name: Event + parameters: + - name: address + type: Hash160 diff --git a/cli/smartcontract/testdata/invalid2/invalid.go b/cli/smartcontract/testdata/invalid2/invalid.go new file mode 100644 index 0000000..07f1002 --- /dev/null +++ b/cli/smartcontract/testdata/invalid2/invalid.go @@ -0,0 +1,21 @@ +// invalid is an example of a contract which doesn't pass event check. +package invalid2 + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +// Notify1 emits a correctly typed event. +func Notify1() bool { + runtime.Notify("Event", interop.Hash160{1, 2, 3}) + + return true +} + +// Notify2 emits an invalid event (extra parameter). +func Notify2() bool { + runtime.Notify("Event", interop.Hash160{1, 2, 3}, "extra parameter") + + return true +} diff --git a/cli/smartcontract/testdata/invalid2/invalid.yml b/cli/smartcontract/testdata/invalid2/invalid.yml new file mode 100644 index 0000000..73839ca --- /dev/null +++ b/cli/smartcontract/testdata/invalid2/invalid.yml @@ -0,0 +1,7 @@ +name: "Invalid example" +supportedstandards: [] +events: + - name: Event + parameters: + - name: address + type: Hash160 diff --git a/cli/smartcontract/testdata/invalid3/invalid.go b/cli/smartcontract/testdata/invalid3/invalid.go new file mode 100644 index 0000000..2dc7c96 --- /dev/null +++ b/cli/smartcontract/testdata/invalid3/invalid.go @@ -0,0 +1,21 @@ +// invalid is an example of a contract which doesn't pass event check. +package invalid3 + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +// Notify1 emits a correctly typed event. +func Notify1() bool { + runtime.Notify("Event", interop.Hash160{1, 2, 3}) + + return true +} + +// Notify2 emits an invalid event (missing from manifest). +func Notify2() bool { + runtime.Notify("AnotherEvent", interop.Hash160{1, 2, 3}) + + return true +} diff --git a/cli/smartcontract/testdata/invalid3/invalid.yml b/cli/smartcontract/testdata/invalid3/invalid.yml new file mode 100644 index 0000000..73839ca --- /dev/null +++ b/cli/smartcontract/testdata/invalid3/invalid.yml @@ -0,0 +1,7 @@ +name: "Invalid example" +supportedstandards: [] +events: + - name: Event + parameters: + - name: address + type: Hash160 diff --git a/cli/smartcontract/testdata/invalid4/invalid.go b/cli/smartcontract/testdata/invalid4/invalid.go new file mode 100644 index 0000000..acb769b --- /dev/null +++ b/cli/smartcontract/testdata/invalid4/invalid.go @@ -0,0 +1,5 @@ +package invalid4 + +func Verify() bool { + return true +} diff --git a/cli/smartcontract/testdata/invalid4/invalid.yml b/cli/smartcontract/testdata/invalid4/invalid.yml new file mode 100644 index 0000000..636d914 --- /dev/null +++ b/cli/smartcontract/testdata/invalid4/invalid.yml @@ -0,0 +1,2 @@ +name: Test bad source url +sourceurl: http://example.com/with/some/huge/path/that/cant/be/ever/normally/reached/but/we/need/to/test/for/it/anyway/because/there/is/some/path/in/the/code/that/does/this/check/and/it/should/be/triggered/to/ensure/proper/coverage/in/this/particular/component/of/our/perfect/compiler diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go new file mode 100644 index 0000000..5205e4d --- /dev/null +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -0,0 +1,511 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nameservice contains RPC wrappers for NameService contract. +package nameservice + +import ( + "errors" + "fmt" + "github.com/google/uuid" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x47, 0x94, 0xc5, 0xcf, 0xc2, 0xc, 0x69, 0x37, 0x1c, 0xac, 0x50} + +// SetAdminEvent represents "SetAdmin" event emitted by the contract. +type SetAdminEvent struct { + Name string + OldAdmin util.Uint160 + NewAdmin util.Uint160 +} + +// RenewEvent represents "Renew" event emitted by the contract. +type RenewEvent struct { + Name string + OldExpiration *big.Int + NewExpiration *big.Int +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep11.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep11.Actor + + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep11.NonDivisibleReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep11.BaseWriter + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + var nep11ndt = nep11.NewNonDivisible(actor, hash) + return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash} +} + +// Roots invokes `roots` method of contract. +func (c *ContractReader) Roots() (uuid.UUID, result.Iterator, error) { + return unwrap.SessionIterator(c.invoker.Call(c.hash, "roots")) +} + +// RootsExpanded is similar to Roots (uses the same contract +// method), but can be useful if the server used doesn't support sessions and +// doesn't expand iterators. It creates a script that will get the specified +// number of result items from the iterator right in the VM and return them to +// you. It's only limited by VM stack and GAS available for RPC invocations. +func (c *ContractReader) RootsExpanded(_numOfIteratorItems int) ([]stackitem.Item, error) { + return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "roots", _numOfIteratorItems)) +} + +// GetPrice invokes `getPrice` method of contract. +func (c *ContractReader) GetPrice(length *big.Int) (*big.Int, error) { + return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice", length)) +} + +// IsAvailable invokes `isAvailable` method of contract. +func (c *ContractReader) IsAvailable(name string) (bool, error) { + return unwrap.Bool(c.invoker.Call(c.hash, "isAvailable", name)) +} + +// GetRecord invokes `getRecord` method of contract. +func (c *ContractReader) GetRecord(name string, typev *big.Int) (string, error) { + return unwrap.UTF8String(c.invoker.Call(c.hash, "getRecord", name, typev)) +} + +// GetAllRecords invokes `getAllRecords` method of contract. +func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) { + return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name)) +} + +// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract +// method), but can be useful if the server used doesn't support sessions and +// doesn't expand iterators. It creates a script that will get the specified +// number of result items from the iterator right in the VM and return them to +// you. It's only limited by VM stack and GAS available for RPC invocations. +func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) { + return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name)) +} + +// Resolve invokes `resolve` method of contract. +func (c *ContractReader) Resolve(name string, typev *big.Int) (string, error) { + return unwrap.UTF8String(c.invoker.Call(c.hash, "resolve", name, typev)) +} + +// Update creates a transaction invoking `update` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Update(nef []byte, manifest string) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "update", nef, manifest) +} + +// UpdateTransaction creates a transaction invoking `update` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) UpdateTransaction(nef []byte, manifest string) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "update", nef, manifest) +} + +// UpdateUnsigned creates a transaction invoking `update` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) UpdateUnsigned(nef []byte, manifest string) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest) +} + +// AddRoot creates a transaction invoking `addRoot` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) AddRoot(root string) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "addRoot", root) +} + +// AddRootTransaction creates a transaction invoking `addRoot` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) AddRootTransaction(root string) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "addRoot", root) +} + +// AddRootUnsigned creates a transaction invoking `addRoot` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) AddRootUnsigned(root string) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "addRoot", nil, root) +} + +// SetPrice creates a transaction invoking `setPrice` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) SetPrice(priceList []any) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "setPrice", priceList) +} + +// SetPriceTransaction creates a transaction invoking `setPrice` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) SetPriceTransaction(priceList []any) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "setPrice", priceList) +} + +// SetPriceUnsigned creates a transaction invoking `setPrice` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) SetPriceUnsigned(priceList []any) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "setPrice", nil, priceList) +} + +func (c *Contract) scriptForRegister(name string, owner util.Uint160) ([]byte, error) { + return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner) +} + +// Register creates a transaction invoking `register` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Register(name string, owner util.Uint160) (util.Uint256, uint32, error) { + script, err := c.scriptForRegister(name, owner) + if err != nil { + return util.Uint256{}, 0, err + } + return c.actor.SendRun(script) +} + +// RegisterTransaction creates a transaction invoking `register` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) RegisterTransaction(name string, owner util.Uint160) (*transaction.Transaction, error) { + script, err := c.scriptForRegister(name, owner) + if err != nil { + return nil, err + } + return c.actor.MakeRun(script) +} + +// RegisterUnsigned creates a transaction invoking `register` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) RegisterUnsigned(name string, owner util.Uint160) (*transaction.Transaction, error) { + script, err := c.scriptForRegister(name, owner) + if err != nil { + return nil, err + } + return c.actor.MakeUnsignedRun(script, nil) +} + +// Renew creates a transaction invoking `renew` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Renew(name string) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "renew", name) +} + +// RenewTransaction creates a transaction invoking `renew` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) RenewTransaction(name string) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "renew", name) +} + +// RenewUnsigned creates a transaction invoking `renew` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) RenewUnsigned(name string) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name) +} + +// Renew2 creates a transaction invoking `renew` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Renew2(name string, years *big.Int) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "renew", name, years) +} + +// Renew2Transaction creates a transaction invoking `renew` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) Renew2Transaction(name string, years *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "renew", name, years) +} + +// Renew2Unsigned creates a transaction invoking `renew` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) Renew2Unsigned(name string, years *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name, years) +} + +// SetAdmin creates a transaction invoking `setAdmin` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) SetAdmin(name string, admin util.Uint160) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "setAdmin", name, admin) +} + +// SetAdminTransaction creates a transaction invoking `setAdmin` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) SetAdminTransaction(name string, admin util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "setAdmin", name, admin) +} + +// SetAdminUnsigned creates a transaction invoking `setAdmin` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) SetAdminUnsigned(name string, admin util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, name, admin) +} + +// SetRecord creates a transaction invoking `setRecord` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) SetRecord(name string, typev *big.Int, data string) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "setRecord", name, typev, data) +} + +// SetRecordTransaction creates a transaction invoking `setRecord` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) SetRecordTransaction(name string, typev *big.Int, data string) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "setRecord", name, typev, data) +} + +// SetRecordUnsigned creates a transaction invoking `setRecord` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) SetRecordUnsigned(name string, typev *big.Int, data string) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "setRecord", nil, name, typev, data) +} + +// DeleteRecord creates a transaction invoking `deleteRecord` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) DeleteRecord(name string, typev *big.Int) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "deleteRecord", name, typev) +} + +// DeleteRecordTransaction creates a transaction invoking `deleteRecord` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) DeleteRecordTransaction(name string, typev *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "deleteRecord", name, typev) +} + +// DeleteRecordUnsigned creates a transaction invoking `deleteRecord` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "deleteRecord", nil, name, typev) +} + +// SetAdminEventsFromApplicationLog retrieves a set of all emitted events +// with "SetAdmin" name from the provided [result.ApplicationLog]. +func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SetAdminEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SetAdmin" { + continue + } + event := new(SetAdminEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to SetAdminEvent or +// returns an error if it's not possible to do to so. +func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 3 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + e.OldAdmin, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field OldAdmin: %w", err) + } + + index++ + e.NewAdmin, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field NewAdmin: %w", err) + } + + return nil +} + +// RenewEventsFromApplicationLog retrieves a set of all emitted events +// with "Renew" name from the provided [result.ApplicationLog]. +func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*RenewEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Renew" { + continue + } + event := new(RenewEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to RenewEvent or +// returns an error if it's not possible to do to so. +func (e *RenewEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 3 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.Name, err = func(item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field Name: %w", err) + } + + index++ + e.OldExpiration, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field OldExpiration: %w", err) + } + + index++ + e.NewExpiration, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NewExpiration: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/testdata/nameservice/nns.manifest.json b/cli/smartcontract/testdata/nameservice/nns.manifest.json new file mode 100644 index 0000000..555ab89 --- /dev/null +++ b/cli/smartcontract/testdata/nameservice/nns.manifest.json @@ -0,0 +1,441 @@ + { + "abi" : { + "events" : [ + { + "parameters" : [ + { + "name" : "from", + "type" : "Hash160" + }, + { + "name" : "to", + "type" : "Hash160" + }, + { + "name" : "amount", + "type" : "Integer" + }, + { + "type" : "ByteArray", + "name" : "tokenId" + } + ], + "name" : "Transfer" + }, + { + "parameters" : [ + { + "type" : "String", + "name" : "name" + }, + { + "type" : "Hash160", + "name" : "oldAdmin" + }, + { + "type" : "Hash160", + "name" : "newAdmin" + } + ], + "name" : "SetAdmin" + }, + { + "name" : "Renew", + "parameters" : [ + { + "name" : "name", + "type" : "String" + }, + { + "type" : "Integer", + "name" : "oldExpiration" + }, + { + "name" : "newExpiration", + "type" : "Integer" + } + ] + } + ], + "methods" : [ + { + "safe" : true, + "parameters" : [], + "name" : "symbol", + "returntype" : "String", + "offset" : 0 + }, + { + "parameters" : [], + "name" : "decimals", + "returntype" : "Integer", + "safe" : true, + "offset" : 6 + }, + { + "offset" : 8, + "returntype" : "Integer", + "name" : "totalSupply", + "parameters" : [], + "safe" : true + }, + { + "offset" : 53, + "safe" : true, + "parameters" : [ + { + "type" : "ByteArray", + "name" : "tokenId" + } + ], + "name" : "ownerOf", + "returntype" : "Hash160" + }, + { + "safe" : true, + "name" : "properties", + "returntype" : "Map", + "parameters" : [ + { + "type" : "ByteArray", + "name" : "tokenId" + } + ], + "offset" : 184 + }, + { + "safe" : true, + "returntype" : "Integer", + "name" : "balanceOf", + "parameters" : [ + { + "name" : "owner", + "type" : "Hash160" + } + ], + "offset" : 341 + }, + { + "safe" : true, + "returntype" : "InteropInterface", + "name" : "tokens", + "parameters" : [], + "offset" : 453 + }, + { + "safe" : true, + "name" : "tokensOf", + "returntype" : "InteropInterface", + "parameters" : [ + { + "name" : "owner", + "type" : "Hash160" + } + ], + "offset" : 494 + }, + { + "offset" : 600, + "safe" : false, + "parameters" : [ + { + "type" : "Hash160", + "name" : "to" + }, + { + "name" : "tokenId", + "type" : "ByteArray" + }, + { + "name" : "data", + "type" : "Any" + } + ], + "name" : "transfer", + "returntype" : "Boolean" + }, + { + "name" : "update", + "returntype" : "Void", + "parameters" : [ + { + "type" : "ByteArray", + "name" : "nef" + }, + { + "name" : "manifest", + "type" : "String" + } + ], + "safe" : false, + "offset" : 1121 + }, + { + "offset" : 1291, + "returntype" : "Void", + "name" : "addRoot", + "parameters" : [ + { + "name" : "root", + "type" : "String" + } + ], + "safe" : false + }, + { + "offset" : 1725, + "safe" : true, + "parameters" : [], + "returntype" : "InteropInterface", + "name" : "roots" + }, + { + "offset" : 1757, + "parameters" : [ + { + "type" : "Array", + "name" : "priceList" + } + ], + "name" : "setPrice", + "returntype" : "Void", + "safe" : false + }, + { + "offset" : 1952, + "safe" : true, + "parameters" : [ + { + "name" : "length", + "type" : "Integer" + } + ], + "name" : "getPrice", + "returntype" : "Integer" + }, + { + "offset" : 2017, + "safe" : true, + "parameters" : [ + { + "type" : "String", + "name" : "name" + } + ], + "name" : "isAvailable", + "returntype" : "Boolean" + }, + { + "offset" : 2405, + "parameters" : [ + { + "type" : "String", + "name" : "name" + }, + { + "type" : "Hash160", + "name" : "owner" + } + ], + "name" : "register", + "returntype" : "Boolean", + "safe" : false + }, + { + "name" : "renew", + "returntype" : "Integer", + "parameters" : [ + { + "type" : "String", + "name" : "name" + } + ], + "safe" : false, + "offset" : 3113 + }, + { + "offset" : 3123, + "safe" : false, + "parameters" : [ + { + "name" : "name", + "type" : "String" + }, + { + "name" : "years", + "type" : "Integer" + } + ], + "name" : "renew", + "returntype" : "Integer" + }, + { + "offset" : 3697, + "parameters" : [ + { + "type" : "String", + "name" : "name" + }, + { + "name" : "admin", + "type" : "Hash160" + } + ], + "name" : "setAdmin", + "returntype" : "Void", + "safe" : false + }, + { + "safe" : false, + "returntype" : "Void", + "name" : "setRecord", + "parameters" : [ + { + "name" : "name", + "type" : "String" + }, + { + "type" : "Integer", + "name" : "type" + }, + { + "name" : "data", + "type" : "String" + } + ], + "offset" : 3921 + }, + { + "name" : "getRecord", + "returntype" : "String", + "parameters" : [ + { + "name" : "name", + "type" : "String" + }, + { + "type" : "Integer", + "name" : "type" + } + ], + "safe" : true, + "offset" : 5877 + }, + { + "returntype" : "InteropInterface", + "name" : "getAllRecords", + "parameters" : [ + { + "name" : "name", + "type" : "String" + } + ], + "safe" : true, + "offset" : 6201 + }, + { + "safe" : false, + "returntype" : "Void", + "name" : "deleteRecord", + "parameters" : [ + { + "name" : "name", + "type" : "String" + }, + { + "type" : "Integer", + "name" : "type" + } + ], + "offset" : 6281 + }, + { + "offset" : 6565, + "name" : "resolve", + "returntype" : "String", + "parameters" : [ + { + "name" : "name", + "type" : "String" + }, + { + "type" : "Integer", + "name" : "type" + } + ], + "safe" : true + }, + { + "safe" : false, + "parameters" : [ + { + "name" : "data", + "type" : "Any" + }, + { + "name" : "update", + "type" : "Boolean" + } + ], + "name" : "_deploy", + "returntype" : "Void", + "offset" : 7152 + }, + { + "offset" : 7514, + "parameters" : [], + "returntype" : "Void", + "name" : "_initialize", + "safe" : false + } + ] + }, + "supportedstandards" : [ + "NEP-11" + ], + "permissions" : [ + { + "contract" : "0x726cb6e0cd8628a1350a611384688911ab75f51b", + "methods" : [ + "ripemd160" + ] + }, + { + "contract" : "0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0", + "methods" : [ + "atoi", + "deserialize", + "serialize", + "stringSplit" + ] + }, + { + "contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", + "methods" : [ + "getCommittee" + ] + }, + { + "contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd", + "methods" : [ + "getContract", + "update" + ] + }, + { + "methods" : [ + "onNEP11Payment" + ], + "contract" : "*" + } + ], + "features" : {}, + "name" : "NameService", + "trusts" : [], + "extra" : { + "Author" : "The Neo Project", + "Description" : "Neo Name Service", + "Email" : "dev@neo.org" + }, + "groups" : [] + } diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go new file mode 100644 index 0000000..078c794 --- /dev/null +++ b/cli/smartcontract/testdata/nex/nex.go @@ -0,0 +1,339 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nextoken contains RPC wrappers for NEX Token contract. +package nextoken + +import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xce, 0xd5, 0xbf, 0xc6, 0x22, 0xcf, 0xe8, 0x9, 0x7f, 0xa6, 0xa2} + +// OnMintEvent represents "OnMint" event emitted by the contract. +type OnMintEvent struct { + From util.Uint160 + To util.Uint160 + Amount *big.Int + SwapId *big.Int +} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + nep17.Invoker +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + Invoker + + nep17.Actor + + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + nep17.TokenReader + invoker Invoker + hash util.Uint160 +} + +// Contract implements all contract methods. +type Contract struct { + ContractReader + nep17.TokenWriter + actor Actor + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash} +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + var hash = Hash + var nep17t = nep17.New(actor, hash) + return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash} +} + +// Cap invokes `cap` method of contract. +func (c *ContractReader) Cap() (*big.Int, error) { + return unwrap.BigInt(c.invoker.Call(c.hash, "cap")) +} + +// GetMinter invokes `getMinter` method of contract. +func (c *ContractReader) GetMinter() (*keys.PublicKey, error) { + return unwrap.PublicKey(c.invoker.Call(c.hash, "getMinter")) +} + +// GetOwner invokes `getOwner` method of contract. +func (c *ContractReader) GetOwner() (util.Uint160, error) { + return unwrap.Uint160(c.invoker.Call(c.hash, "getOwner")) +} + +// TotalMinted invokes `totalMinted` method of contract. +func (c *ContractReader) TotalMinted() (*big.Int, error) { + return unwrap.BigInt(c.invoker.Call(c.hash, "totalMinted")) +} + +// ChangeMinter creates a transaction invoking `changeMinter` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) ChangeMinter(newMinter *keys.PublicKey) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "changeMinter", newMinter) +} + +// ChangeMinterTransaction creates a transaction invoking `changeMinter` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) ChangeMinterTransaction(newMinter *keys.PublicKey) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "changeMinter", newMinter) +} + +// ChangeMinterUnsigned creates a transaction invoking `changeMinter` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) ChangeMinterUnsigned(newMinter *keys.PublicKey) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "changeMinter", nil, newMinter) +} + +// ChangeOwner creates a transaction invoking `changeOwner` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) ChangeOwner(newOwner util.Uint160) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "changeOwner", newOwner) +} + +// ChangeOwnerTransaction creates a transaction invoking `changeOwner` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) ChangeOwnerTransaction(newOwner util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "changeOwner", newOwner) +} + +// ChangeOwnerUnsigned creates a transaction invoking `changeOwner` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) ChangeOwnerUnsigned(newOwner util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "changeOwner", nil, newOwner) +} + +// Destroy creates a transaction invoking `destroy` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Destroy() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "destroy") +} + +// DestroyTransaction creates a transaction invoking `destroy` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "destroy") +} + +// DestroyUnsigned creates a transaction invoking `destroy` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "destroy", nil) +} + +// MaxSupply creates a transaction invoking `maxSupply` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) MaxSupply() (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "maxSupply") +} + +// MaxSupplyTransaction creates a transaction invoking `maxSupply` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) MaxSupplyTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "maxSupply") +} + +// MaxSupplyUnsigned creates a transaction invoking `maxSupply` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) MaxSupplyUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "maxSupply", nil) +} + +// Mint creates a transaction invoking `mint` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Mint(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "mint", from, to, amount, swapId, signature, data) +} + +// MintTransaction creates a transaction invoking `mint` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) MintTransaction(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "mint", from, to, amount, swapId, signature, data) +} + +// MintUnsigned creates a transaction invoking `mint` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) MintUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "mint", nil, from, to, amount, swapId, signature, data) +} + +// Update creates a transaction invoking `update` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "update", nef, manifest) +} + +// UpdateTransaction creates a transaction invoking `update` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "update", nef, manifest) +} + +// UpdateUnsigned creates a transaction invoking `update` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest) +} + +// UpdateCap creates a transaction invoking `updateCap` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) UpdateCap(newCap *big.Int) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "updateCap", newCap) +} + +// UpdateCapTransaction creates a transaction invoking `updateCap` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) UpdateCapTransaction(newCap *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "updateCap", newCap) +} + +// UpdateCapUnsigned creates a transaction invoking `updateCap` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "updateCap", nil, newCap) +} + +// OnMintEventsFromApplicationLog retrieves a set of all emitted events +// with "OnMint" name from the provided [result.ApplicationLog]. +func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*OnMintEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "OnMint" { + continue + } + event := new(OnMintEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize OnMintEvent from stackitem (execution #%d, event #%d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to OnMintEvent or +// returns an error if it's not possible to do to so. +func (e *OnMintEvent) FromStackItem(item *stackitem.Array) error { + if item == nil { + return errors.New("nil item") + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 4 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.From, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field From: %w", err) + } + + index++ + e.To, err = func(item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + }(arr[index]) + if err != nil { + return fmt.Errorf("field To: %w", err) + } + + index++ + e.Amount, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Amount: %w", err) + } + + index++ + e.SwapId, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SwapId: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/testdata/nex/nex.manifest.json b/cli/smartcontract/testdata/nex/nex.manifest.json new file mode 100644 index 0000000..83201b3 --- /dev/null +++ b/cli/smartcontract/testdata/nex/nex.manifest.json @@ -0,0 +1,275 @@ + { + "name" : "NEX Token", + "abi" : { + "events" : [ + { + "parameters" : [ + { + "type" : "Hash160", + "name" : "from" + }, + { + "name" : "to", + "type" : "Hash160" + }, + { + "name" : "amount", + "type" : "Integer" + } + ], + "name" : "Transfer" + }, + { + "name" : "OnMint", + "parameters" : [ + { + "name" : "from", + "type" : "Hash160" + }, + { + "type" : "Hash160", + "name" : "to" + }, + { + "type" : "Integer", + "name" : "amount" + }, + { + "name" : "swapId", + "type" : "Integer" + } + ] + } + ], + "methods" : [ + { + "parameters" : [], + "offset" : 0, + "name" : "_initialize", + "safe" : false, + "returntype" : "Void" + }, + { + "parameters" : [ + { + "type" : "Any", + "name" : "data" + }, + { + "name" : "isUpdate", + "type" : "Boolean" + } + ], + "offset" : 3, + "name" : "_deploy", + "safe" : false, + "returntype" : "Void" + }, + { + "parameters" : [ + { + "type" : "Hash160", + "name" : "holder" + } + ], + "offset" : 484, + "returntype" : "Integer", + "safe" : true, + "name" : "balanceOf" + }, + { + "safe" : true, + "returntype" : "Integer", + "name" : "cap", + "offset" : 1881, + "parameters" : [] + }, + { + "name" : "changeMinter", + "safe" : false, + "returntype" : "Void", + "parameters" : [ + { + "name" : "newMinter", + "type" : "PublicKey" + } + ], + "offset" : 1678 + }, + { + "parameters" : [ + { + "type" : "Hash160", + "name" : "newOwner" + } + ], + "offset" : 1659, + "name" : "changeOwner", + "safe" : false, + "returntype" : "Void" + }, + { + "offset" : 466, + "parameters" : [], + "safe" : true, + "name" : "decimals", + "returntype" : "Integer" + }, + { + "returntype" : "Void", + "safe" : false, + "name" : "destroy", + "parameters" : [], + "offset" : 1194 + }, + { + "safe" : true, + "returntype" : "PublicKey", + "name" : "getMinter", + "offset" : 1671, + "parameters" : [] + }, + { + "parameters" : [], + "offset" : 1652, + "name" : "getOwner", + "safe" : true, + "returntype" : "Hash160" + }, + { + "name" : "maxSupply", + "safe" : false, + "returntype" : "Integer", + "parameters" : [], + "offset" : 468 + }, + { + "offset" : 1222, + "parameters" : [ + { + "name" : "from", + "type" : "Hash160" + }, + { + "name" : "to", + "type" : "Hash160" + }, + { + "type" : "Integer", + "name" : "amount" + }, + { + "name" : "swapId", + "type" : "Integer" + }, + { + "name" : "signature", + "type" : "Signature" + }, + { + "name" : "data", + "type" : "Any" + } + ], + "safe" : false, + "name" : "mint", + "returntype" : "Void" + }, + { + "safe" : true, + "name" : "symbol", + "returntype" : "String", + "parameters" : [], + "offset" : 460 + }, + { + "offset" : 1854, + "parameters" : [], + "name" : "totalMinted", + "safe" : true, + "returntype" : "Integer" + }, + { + "offset" : 478, + "parameters" : [], + "name" : "totalSupply", + "safe" : true, + "returntype" : "Integer" + }, + { + "offset" : 543, + "parameters" : [ + { + "name" : "from", + "type" : "Hash160" + }, + { + "name" : "to", + "type" : "Hash160" + }, + { + "type" : "Integer", + "name" : "amount" + }, + { + "name" : "data", + "type" : "Any" + } + ], + "name" : "transfer", + "safe" : false, + "returntype" : "Boolean" + }, + { + "offset" : 1205, + "parameters" : [ + { + "type" : "ByteArray", + "name" : "nef" + }, + { + "name" : "manifest", + "type" : "ByteArray" + } + ], + "safe" : false, + "returntype" : "Void", + "name" : "update" + }, + { + "offset" : 1717, + "parameters" : [ + { + "type" : "Integer", + "name" : "newCap" + } + ], + "returntype" : "Void", + "safe" : false, + "name" : "updateCap" + } + ] + }, + "supportedstandards" : [ + "NEP-17" + ], + "extra" : null, + "trusts" : [], + "features" : {}, + "groups" : [], + "permissions" : [ + { + "methods" : [ + "onNEP17Payment" + ], + "contract" : "*" + }, + { + "methods" : [ + "update", + "destroy" + ], + "contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd" + } + ] + } diff --git a/cli/smartcontract/testdata/nonepiter/iter.go b/cli/smartcontract/testdata/nonepiter/iter.go new file mode 100644 index 0000000..bf8e7c2 --- /dev/null +++ b/cli/smartcontract/testdata/nonepiter/iter.go @@ -0,0 +1,63 @@ +// Code generated by neo-go contract generate-rpcwrapper --manifest --out [--hash ] [--config ]; DO NOT EDIT. + +// Package nonnepxxcontractwithiterators contains RPC wrappers for Non-NEPXX contract with iterators contract. +package nonnepxxcontractwithiterators + +import ( + "github.com/google/uuid" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// Invoker is used by ContractReader to call various safe methods. +type Invoker interface { + Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) + CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) + TerminateSession(sessionID uuid.UUID) error + TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) +} + +// ContractReader implements safe contract methods. +type ContractReader struct { + invoker Invoker + hash util.Uint160 +} + +// NewReader creates an instance of ContractReader using Hash and the given Invoker. +func NewReader(invoker Invoker) *ContractReader { + var hash = Hash + return &ContractReader{invoker, hash} +} + +// Tokens invokes `tokens` method of contract. +func (c *ContractReader) Tokens() (uuid.UUID, result.Iterator, error) { + return unwrap.SessionIterator(c.invoker.Call(c.hash, "tokens")) +} + +// TokensExpanded is similar to Tokens (uses the same contract +// method), but can be useful if the server used doesn't support sessions and +// doesn't expand iterators. It creates a script that will get the specified +// number of result items from the iterator right in the VM and return them to +// you. It's only limited by VM stack and GAS available for RPC invocations. +func (c *ContractReader) TokensExpanded(_numOfIteratorItems int) ([]stackitem.Item, error) { + return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "tokens", _numOfIteratorItems)) +} + +// GetAllRecords invokes `getAllRecords` method of contract. +func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) { + return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name)) +} + +// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract +// method), but can be useful if the server used doesn't support sessions and +// doesn't expand iterators. It creates a script that will get the specified +// number of result items from the iterator right in the VM and return them to +// you. It's only limited by VM stack and GAS available for RPC invocations. +func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) { + return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name)) +} diff --git a/cli/smartcontract/testdata/nonepiter/iter.manifest.json b/cli/smartcontract/testdata/nonepiter/iter.manifest.json new file mode 100644 index 0000000..9f33e27 --- /dev/null +++ b/cli/smartcontract/testdata/nonepiter/iter.manifest.json @@ -0,0 +1,33 @@ +{ + "groups" : [], + "abi" : { + "events" : [], + "methods" : [ + { + "parameters" : [], + "safe" : true, + "name" : "tokens", + "offset" : 0, + "returntype" : "InteropInterface" + }, + { + "offset" : 1, + "returntype" : "InteropInterface", + "safe" : true, + "parameters" : [ + { + "type" : "String", + "name" : "name" + } + ], + "name" : "getAllRecords" + } + ] + }, + "supportedstandards" : [], + "trusts" : [], + "extra" : {}, + "permissions" : [], + "name" : "Non-NEPXX contract with iterators", + "features" : {} +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go new file mode 100644 index 0000000..821a7d8 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go @@ -0,0 +1,7 @@ +package invalid1 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("Non declared event") +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.yml new file mode 100644 index 0000000..eda9482 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.yml @@ -0,0 +1 @@ +name: Test undeclared event \ No newline at end of file diff --git a/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go new file mode 100644 index 0000000..6aa771c --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go @@ -0,0 +1,7 @@ +package invalid2 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("SomeEvent", "p1", "p2") +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.yml new file mode 100644 index 0000000..1393380 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.yml @@ -0,0 +1,6 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: String \ No newline at end of file diff --git a/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go new file mode 100644 index 0000000..7f9298f --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go @@ -0,0 +1,7 @@ +package invalid3 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("SomeEvent", "p1", 5) +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.yml new file mode 100644 index 0000000..a217dee --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.yml @@ -0,0 +1,8 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: String + - name: p2 + type: String \ No newline at end of file diff --git a/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go new file mode 100644 index 0000000..70f887b --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go @@ -0,0 +1,17 @@ +package invalid4 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +type SomeStruct1 struct { + Field1 int +} + +type SomeStruct2 struct { + Field2 string +} + +func Main() { + // Inconsistent event params usages (different named types throughout the usages). + runtime.Notify("SomeEvent", SomeStruct1{Field1: 123}) + runtime.Notify("SomeEvent", SomeStruct2{Field2: "str"}) +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.yml new file mode 100644 index 0000000..6c0c347 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.yml @@ -0,0 +1,6 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: Array \ No newline at end of file diff --git a/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go new file mode 100644 index 0000000..3d31565 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go @@ -0,0 +1,12 @@ +package invalid5 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +type NamedStruct struct { + SomeInt int +} + +func Main() NamedStruct { + runtime.Notify("SomeEvent", []interface{}{123}) + return NamedStruct{SomeInt: 123} +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.yml new file mode 100644 index 0000000..ccd05f4 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.yml @@ -0,0 +1,16 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: Array + extendedtype: + base: Array + name: invalid5.NamedStruct +namedtypes: + invalid5.NamedStruct: + base: Array + name: invalid5.NamedStruct + fields: + - field: SomeInt + base: Integer diff --git a/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go new file mode 100644 index 0000000..2b210e7 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go @@ -0,0 +1,14 @@ +package invalid6 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +type SomeStruct struct { + Field int + // RPC binding generator will convert this field into exported, which matches + // exactly the existing Field. + field int +} + +func Main() { + runtime.Notify("SomeEvent", SomeStruct{Field: 123, field: 123}) +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.yml new file mode 100644 index 0000000..c9fe64b --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.yml @@ -0,0 +1,18 @@ +name: Test duplicating event fields +events: + - name: SomeEvent + parameters: + - name: p1 + type: Struct + extendedtype: + base: Struct + name: SomeStruct +namedtypes: + SomeStruct: + base: Struct + name: SomeStruct + fields: + - field: Field + base: Integer + - field: field + base: Integer diff --git a/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go new file mode 100644 index 0000000..6cb9e6a --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go @@ -0,0 +1,14 @@ +package invalid7 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +type SomeStruct struct { + Field int + // RPC binding generator will convert this field into exported, which matches + // exactly the existing Field. + field int +} + +func Main() { + runtime.Notify("SomeEvent", SomeStruct{Field: 123, field: 123}) +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.yml new file mode 100644 index 0000000..cbc8c56 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.yml @@ -0,0 +1,6 @@ +name: Test duplicating autogenerated event fields +events: + - name: SomeEvent + parameters: + - name: p1 + type: Struct diff --git a/cli/smartcontract/testdata/rpcbindings/invalid8/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid8/invalid.go new file mode 100644 index 0000000..db96523 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid8/invalid.go @@ -0,0 +1,16 @@ +package invalid8 + +type SomeStruct struct { + Field int + // RPC binding generator will convert this field into exported, which matches + // exactly the existing Field. + field int +} + +func Main() SomeStruct { + s := SomeStruct{ + Field: 1, + field: 2, + } + return s +} diff --git a/cli/smartcontract/testdata/rpcbindings/invalid8/invalid.yml b/cli/smartcontract/testdata/rpcbindings/invalid8/invalid.yml new file mode 100644 index 0000000..2b308a5 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/invalid8/invalid.yml @@ -0,0 +1 @@ +name: Test duplicating struct fields diff --git a/cli/smartcontract/testdata/rpcbindings/notifications/config.yml b/cli/smartcontract/testdata/rpcbindings/notifications/config.yml new file mode 100644 index 0000000..40b13bf --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/notifications/config.yml @@ -0,0 +1,23 @@ +name: "Notifications" +sourceurl: https://github.com/nspcc-dev/neo-go/ +events: + - name: "! complicated name %$#" + parameters: + - name: ! complicated param @#$% + type: String + - name: "SomeMap" + parameters: + - name: m + type: Map + - name: "SomeStruct" + parameters: + - name: s + type: Struct + - name: "SomeArray" + parameters: + - name: a + type: Array + - name: "SomeUnexportedField" + parameters: + - name: s + type: Struct \ No newline at end of file diff --git a/cli/smartcontract/testdata/rpcbindings/notifications/config_extended.yml b/cli/smartcontract/testdata/rpcbindings/notifications/config_extended.yml new file mode 100644 index 0000000..6c6e418 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/notifications/config_extended.yml @@ -0,0 +1,60 @@ +name: "Notifications" +sourceurl: https://github.com/nspcc-dev/neo-go/ +events: + - name: "! complicated name %$#" + parameters: + - name: ! complicated param @#$% + type: String + - name: "SomeMap" + parameters: + - name: m + type: Map + extendedtype: + base: Map + key: Integer + value: + base: Map + key: String + value: + base: Array + value: + base: Hash160 + - name: "SomeStruct" + parameters: + - name: s + type: Struct + extendedtype: + base: Struct + name: crazyStruct + - name: "SomeArray" + parameters: + - name: a + type: Array + extendedtype: + base: Array + value: + base: Array + value: + base: Integer + - name: "SomeUnexportedField" + parameters: + - name: s + type: Struct + extendedtype: + base: Struct + name: simpleStruct +namedtypes: + crazyStruct: + base: Struct + name: crazyStruct + fields: + - field: I + base: Integer + - field: B + base: Boolean + simpleStruct: + base: Struct + name: simpleStruct + fields: + - field: i + base: Integer \ No newline at end of file diff --git a/cli/smartcontract/testdata/rpcbindings/notifications/config_guessed.yml b/cli/smartcontract/testdata/rpcbindings/notifications/config_guessed.yml new file mode 100644 index 0000000..40b13bf --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/notifications/config_guessed.yml @@ -0,0 +1,23 @@ +name: "Notifications" +sourceurl: https://github.com/nspcc-dev/neo-go/ +events: + - name: "! complicated name %$#" + parameters: + - name: ! complicated param @#$% + type: String + - name: "SomeMap" + parameters: + - name: m + type: Map + - name: "SomeStruct" + parameters: + - name: s + type: Struct + - name: "SomeArray" + parameters: + - name: a + type: Array + - name: "SomeUnexportedField" + parameters: + - name: s + type: Struct \ No newline at end of file diff --git a/cli/smartcontract/testdata/rpcbindings/notifications/notifications.go b/cli/smartcontract/testdata/rpcbindings/notifications/notifications.go new file mode 100644 index 0000000..e88a413 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/notifications/notifications.go @@ -0,0 +1,33 @@ +package structs + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +func Main() { + runtime.Notify("! complicated name %$#", "str1") +} + +func CrazyMap() { + runtime.Notify("SomeMap", map[int][]map[string][]interop.Hash160{}) +} + +func Struct() { + runtime.Notify("SomeStruct", struct { + I int + B bool + }{I: 123, B: true}) +} + +func Array() { + runtime.Notify("SomeArray", [][]int{}) +} + +// UnexportedField emits notification with unexported field that must be converted +// to exported in the resulting RPC binding. +func UnexportedField() { + runtime.Notify("SomeUnexportedField", struct { + i int + }{i: 123}) +} diff --git a/cli/smartcontract/testdata/rpcbindings/royalty/config.yml b/cli/smartcontract/testdata/rpcbindings/royalty/config.yml new file mode 100644 index 0000000..a121845 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/royalty/config.yml @@ -0,0 +1,16 @@ +name: Test royalty +sourceurl: https://github.com/nspcc-dev/neo-go/ +supportedstandards: ["NEP-24-Payable"] +events: + - name: RoyaltiesTransferred + parameters: + - name: royaltyToken + type: Hash160 + - name: royaltyRecipient + type: Hash160 + - name: buyer + type: Hash160 + - name: tokenId + type: ByteArray + - name: amount + type: Integer diff --git a/cli/smartcontract/testdata/rpcbindings/royalty/royalty.go b/cli/smartcontract/testdata/rpcbindings/royalty/royalty.go new file mode 100644 index 0000000..835a527 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/royalty/royalty.go @@ -0,0 +1,13 @@ +package royalty + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/native/std" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +// RoyaltiesTransferred notifies about royalty payment. This method is called by marketplace +// contract when royalties are transferred. +func RoyaltiesTransferred(royaltyToken, royaltyRecipient, buyer interop.Hash160, tokenId []byte, amount int) { + runtime.Notify("RoyaltiesTransferred", royaltyToken, royaltyRecipient, buyer, std.Deserialize(tokenId), amount) +} diff --git a/cli/smartcontract/testdata/rpcbindings/structs/config.yml b/cli/smartcontract/testdata/rpcbindings/structs/config.yml new file mode 100644 index 0000000..04192ba --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/structs/config.yml @@ -0,0 +1,3 @@ +name: "Types" +sourceurl: https://github.com/nspcc-dev/neo-go/ +safemethods: ["contract", "block", "transaction", "struct"] diff --git a/cli/smartcontract/testdata/rpcbindings/structs/structs.go b/cli/smartcontract/testdata/rpcbindings/structs/structs.go new file mode 100644 index 0000000..cf3fa28 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/structs/structs.go @@ -0,0 +1,40 @@ +package structs + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" + "github.com/nspcc-dev/neo-go/pkg/interop/native/management" +) + +type Internal struct { + Bool bool + Int int + Bytes []byte + String string + H160 interop.Hash160 + H256 interop.Hash256 + PK interop.PublicKey + PubKey interop.PublicKey + Sign interop.Signature + ArrOfBytes [][]byte + ArrOfH160 []interop.Hash160 + Map map[int][]interop.PublicKey + Struct *Internal + unexportedField int // this one should be exported in the resulting RPC binding. +} + +func Contract(mc management.Contract) management.Contract { + return mc +} + +func Block(b *ledger.Block) *ledger.Block { + return b +} + +func Transaction(t *ledger.Transaction) *ledger.Transaction { + return t +} + +func Struct(s *Internal) *Internal { + return s +} diff --git a/cli/smartcontract/testdata/rpcbindings/types/config.yml b/cli/smartcontract/testdata/rpcbindings/types/config.yml new file mode 100644 index 0000000..863043b --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/types/config.yml @@ -0,0 +1,3 @@ +name: "Types" +sourceurl: https://github.com/nspcc-dev/neo-go/ +safemethods: ["bool", "int", "bytes", "string", "any", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps", "anyMaps", "unnamedStructs", "unnamedStructsX"] diff --git a/cli/smartcontract/testdata/rpcbindings/types/types.go b/cli/smartcontract/testdata/rpcbindings/types/types.go new file mode 100644 index 0000000..fdba199 --- /dev/null +++ b/cli/smartcontract/testdata/rpcbindings/types/types.go @@ -0,0 +1,103 @@ +package types + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" +) + +func Bool(b bool) bool { + return false +} + +func Int(i int) int { + return 0 +} + +func Bytes(b []byte) []byte { + return nil +} + +func String(s string) string { + return "" +} + +func Any(a any) any { + return nil +} + +func Hash160(h interop.Hash160) interop.Hash160 { + return nil +} + +func Hash256(h interop.Hash256) interop.Hash256 { + return nil +} + +func PublicKey(k interop.PublicKey) interop.PublicKey { + return nil +} + +func Signature(s interop.Signature) interop.Signature { + return nil +} + +func Bools(b []bool) []bool { + return nil +} + +func Ints(i []int) []int { + return nil +} + +func Bytess(b [][]byte) [][]byte { + return nil +} + +func Strings(s []string) []string { + return nil +} + +func Hash160s(h []interop.Hash160) []interop.Hash160 { + return nil +} + +func Hash256s(h []interop.Hash256) []interop.Hash256 { + return nil +} + +func PublicKeys(k []interop.PublicKey) []interop.PublicKey { + return nil +} + +func Signatures(s []interop.Signature) []interop.Signature { + return nil +} + +func AAAStrings(s [][][]string) [][][]string { + return s +} + +func Maps(m map[string]string) map[string]string { + return m +} + +func CrazyMaps(m map[int][]map[string][]interop.Hash160) map[int][]map[string][]interop.Hash160 { + return m +} + +func AnyMaps(m map[int]any) map[int]any { + return m +} + +func UnnamedStructs() struct{ I int } { + return struct{ I int }{I: 123} +} + +func UnnamedStructsX() struct { + I int + B bool +} { + return struct { + I int + B bool + }{I: 123, B: true} +} diff --git a/cli/smartcontract/testdata/verify.bindings.yml b/cli/smartcontract/testdata/verify.bindings.yml new file mode 100755 index 0000000..927064b --- /dev/null +++ b/cli/smartcontract/testdata/verify.bindings.yml @@ -0,0 +1,297 @@ +package: testdata +hash: "0x0000000000000000000000000000000000000000" +overrides: + burnGas.gas: int + call: any + call.args: '[]any' + call.f: any + call.method: string + call.scriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160 + callWithToken: any + callWithToken.args: '[]any' + callWithToken.flags: int + callWithToken.method: string + callWithToken.scriptHash: string + callWithTokenNoRet.args: '[]any' + callWithTokenNoRet.flags: int + callWithTokenNoRet.method: string + callWithTokenNoRet.scriptHash: string + checkWitness: bool + checkWitness.hashOrKey: '[]byte' + createMultisigAccount: '[]byte' + createMultisigAccount.m: int + createMultisigAccount.pubs: '[]github.com/nspcc-dev/neo-go/pkg/interop.PublicKey' + createStandardAccount: '[]byte' + createStandardAccount.pub: github.com/nspcc-dev/neo-go/pkg/interop.PublicKey + currentHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256 + currentIndex: int + currentSigners: '[]github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.TransactionSigner' + equals: bool + equals.b: any + gasLeft: int + getAddressVersion: int + getBlock: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Block' + getBlock.indexOrHash: any + getCallFlags: any + getCallingScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160 + getEntryScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160 + getExecutingScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160 + getInvocationCounter: int + getNetwork: int + getNotifications: '[][]any' + getNotifications.h: github.com/nspcc-dev/neo-go/pkg/interop.Hash160 + getRandom: int + getScriptContainer: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction' + getTime: int + getTransaction: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction' + getTransaction.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256 + getTransactionFromBlock: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction' + getTransactionFromBlock.indexOrHash: any + getTransactionFromBlock.txIndex: int + getTransactionHeight: int + getTransactionHeight.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256 + getTransactionSigners: '[]github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.TransactionSigner' + getTransactionSigners.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256 + getTransactionVMState: int + getTransactionVMState.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256 + getTrigger: int + loadScript: any + loadScript.args: '[]any' + loadScript.f: any + loadScript.script: '[]byte' + log.message: string + notify.args: '[]any' + notify.name: string + onNEP11Payment.amount: int + onNEP11Payment.data: any + onNEP11Payment.from: github.com/nspcc-dev/neo-go/pkg/interop.Hash160 + onNEP11Payment.token: '[]byte' + onNEP17Payment.amount: int + onNEP17Payment.data: any + onNEP17Payment.from: github.com/nspcc-dev/neo-go/pkg/interop.Hash160 + opcode0NoReturn.op: string + opcode1: any + opcode1NoReturn.arg: any + opcode1NoReturn.op: string + opcode1.arg: any + opcode1.op: string + opcode2: any + opcode2NoReturn.arg1: any + opcode2NoReturn.arg2: any + opcode2NoReturn.op: string + opcode2.arg1: any + opcode2.arg2: any + opcode2.op: string + opcode3: any + opcode3.arg1: any + opcode3.arg2: any + opcode3.arg3: any + opcode3.op: string + platform: '[]byte' + syscall0: any + syscall0NoReturn.name: string + syscall0.name: string + syscall1: any + syscall1NoReturn.arg: any + syscall1NoReturn.name: string + syscall1.arg: any + syscall1.name: string + syscall2: any + syscall2NoReturn.arg1: any + syscall2NoReturn.arg2: any + syscall2NoReturn.name: string + syscall2.arg1: any + syscall2.arg2: any + syscall2.name: string + syscall3: any + syscall3NoReturn.arg1: any + syscall3NoReturn.arg2: any + syscall3NoReturn.arg3: any + syscall3NoReturn.name: string + syscall3.arg1: any + syscall3.arg2: any + syscall3.arg3: any + syscall3.name: string + syscall4: any + syscall4NoReturn.arg1: any + syscall4NoReturn.arg2: any + syscall4NoReturn.arg3: any + syscall4NoReturn.arg4: any + syscall4NoReturn.name: string + syscall4.arg1: any + syscall4.arg2: any + syscall4.arg3: any + syscall4.arg4: any + syscall4.name: string + toBlockSR: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.BlockSR' + verify: bool +namedtypes: + ledger.Block: + base: Array + name: ledger.Block + fields: + - field: Hash + base: Hash256 + - field: Version + base: Integer + - field: PrevHash + base: Hash256 + - field: MerkleRoot + base: Hash256 + - field: Timestamp + base: Integer + - field: Nonce + base: Integer + - field: Index + base: Integer + - field: NextConsensus + base: Hash160 + - field: TransactionsLength + base: Integer + ledger.BlockSR: + base: Array + name: ledger.BlockSR + fields: + - field: Hash + base: Hash256 + - field: Version + base: Integer + - field: PrevHash + base: Hash256 + - field: MerkleRoot + base: Hash256 + - field: Timestamp + base: Integer + - field: Nonce + base: Integer + - field: Index + base: Integer + - field: NextConsensus + base: Hash160 + - field: TransactionsLength + base: Integer + - field: PrevStateRoot + base: Hash256 + ledger.Transaction: + base: Array + name: ledger.Transaction + fields: + - field: Hash + base: Hash256 + - field: Version + base: Integer + - field: Nonce + base: Integer + - field: Sender + base: Hash160 + - field: SysFee + base: Integer + - field: NetFee + base: Integer + - field: ValidUntilBlock + base: Integer + - field: Script + base: ByteArray + ledger.TransactionSigner: + base: Array + name: ledger.TransactionSigner + fields: + - field: Account + base: Hash160 + - field: Scopes + base: Integer + - field: AllowedContracts + base: Array + value: + base: Hash160 + - field: AllowedGroups + base: Array + value: + base: PublicKey + - field: Rules + base: Array + value: + base: Array + name: ledger.WitnessRule + ledger.WitnessCondition: + base: Array + name: ledger.WitnessCondition + fields: + - field: Type + base: Integer + - field: Value + base: Any + ledger.WitnessRule: + base: Array + name: ledger.WitnessRule + fields: + - field: Action + base: Integer + - field: Condition + base: Array + name: ledger.WitnessCondition +types: + call.args: + base: Array + value: + base: Any + call.f: + base: InteropInterface + interface: iterator + callWithToken.args: + base: Array + value: + base: Any + callWithTokenNoRet.args: + base: Array + value: + base: Any + createMultisigAccount.pubs: + base: Array + value: + base: PublicKey + currentSigners: + base: Array + value: + base: Array + name: ledger.TransactionSigner + getBlock: + base: Array + name: ledger.Block + getCallFlags: + base: InteropInterface + interface: iterator + getNotifications: + base: Array + value: + base: Array + value: + base: Any + getScriptContainer: + base: Array + name: ledger.Transaction + getTransaction: + base: Array + name: ledger.Transaction + getTransactionFromBlock: + base: Array + name: ledger.Transaction + getTransactionSigners: + base: Array + value: + base: Array + name: ledger.TransactionSigner + loadScript.args: + base: Array + value: + base: Any + loadScript.f: + base: InteropInterface + interface: iterator + notify.args: + base: Array + value: + base: Any + toBlockSR: + base: Array + name: ledger.BlockSR diff --git a/cli/smartcontract/testdata/verify.go b/cli/smartcontract/testdata/verify.go new file mode 100644 index 0000000..65cd0d5 --- /dev/null +++ b/cli/smartcontract/testdata/verify.go @@ -0,0 +1,20 @@ +package testdata + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) + +func Verify() bool { + return true +} + +func OnNEP17Payment(from interop.Hash160, amount int, data any) { +} + +// OnNEP11Payment notifies about NEP-11 payment. You don't call this method directly, +// instead it's called by NEP-11 contract when you transfer funds from your address +// to the address of this NFT contract. +func OnNEP11Payment(from interop.Hash160, amount int, tokenId []byte, data any) { + runtime.Notify("OnNEP11Payment", from, amount, tokenId, data) +} diff --git a/cli/smartcontract/testdata/verify.manifest.json b/cli/smartcontract/testdata/verify.manifest.json new file mode 100755 index 0000000..d6363f7 --- /dev/null +++ b/cli/smartcontract/testdata/verify.manifest.json @@ -0,0 +1 @@ +{"name":"verify","abi":{"methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Hello world!","parameters":[{"name":"args","type":"Array"}]}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null,"features":{}} diff --git a/cli/smartcontract/testdata/verify.nef b/cli/smartcontract/testdata/verify.nef new file mode 100755 index 0000000000000000000000000000000000000000..5023c45488398a850b2e79ec0496eda99b301c52 GIT binary patch literal 87 zcmeZsbu-RO&DTxOXTS*z^em0_40H>MQguztb<-_P(u_=#jZ;z8bA~fA2s(r_FgwIv Hzh43X4rUD^ literal 0 HcmV?d00001 diff --git a/cli/smartcontract/testdata/verify.yml b/cli/smartcontract/testdata/verify.yml new file mode 100644 index 0000000..421e5d2 --- /dev/null +++ b/cli/smartcontract/testdata/verify.yml @@ -0,0 +1,12 @@ +name: Test verify +events: + - name: OnNEP11Payment + parameters: + - name: from + type: Hash160 + - name: amount + type: Integer + - name: tokenId + type: ByteArray + - name: data + type: Any diff --git a/cli/smartcontract/testdata/verifyrpc/verify.manifest.json b/cli/smartcontract/testdata/verifyrpc/verify.manifest.json new file mode 100755 index 0000000..221dce8 --- /dev/null +++ b/cli/smartcontract/testdata/verifyrpc/verify.manifest.json @@ -0,0 +1,80 @@ +{ + "groups" : [], + "extra" : null, + "supportedstandards" : [], + "name" : "verify", + "trusts" : [], + "features": {}, + "permissions" : [ + { + "methods" : "*", + "contract" : "*" + } + ], + "abi" : { + "methods" : [ + { + "safe" : false, + "offset" : 0, + "parameters" : [], + "name" : "verify", + "returntype" : "Boolean" + }, + { + "returntype" : "Void", + "safe" : false, + "offset" : 5, + "parameters" : [ + { + "type" : "Hash160", + "name" : "from" + }, + { + "type" : "Integer", + "name" : "amount" + }, + { + "type" : "Any", + "name" : "data" + } + ], + "name" : "onNEP17Payment" + }, + { + "returntype" : "Void", + "safe" : false, + "offset" : 5, + "parameters" : [ + { + "type" : "Hash160", + "name" : "from" + }, + { + "type" : "Integer", + "name" : "amount" + }, + { + "type" : "ByteArray", + "name" : "tokenId" + }, + { + "type" : "Any", + "name" : "data" + } + ], + "name" : "onNEP11Payment" + } + ], + "events" : [ + { + "parameters" : [ + { + "type" : "Array", + "name" : "args" + } + ], + "name" : "Hello world!" + } + ] + } +} diff --git a/cli/testdata/testwallet.json b/cli/testdata/testwallet.json new file mode 100644 index 0000000..8e089ef --- /dev/null +++ b/cli/testdata/testwallet.json @@ -0,0 +1,30 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "Nfyz4KcsgYepRJw1W5C2uKCi6QWKf7v6gG", + "key": "6PYVjvHyAFQPXCU3PNsiRbF1RxEyyrT4PqLbSEEZcyYDFAMpSxPZnuRznY", + "label": "kek", + "contract": { + "script": "DCECl3UyEIq6T5RRIXS6z4tNdZPTzQ7NvXyx7FwK05d9UyZBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 2, + "r": 1, + "p": 1 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/cli/testdata/testwallet_multi.json b/cli/testdata/testwallet_multi.json new file mode 100644 index 0000000..8d1400b --- /dev/null +++ b/cli/testdata/testwallet_multi.json @@ -0,0 +1,64 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "NgHcPxgEKZQV4QBedzyASJrgiANhJqBVLw", + "key": "6PYTbVq2P3AJQwWU5SFMKLjHYco7QABtNRo4ZvLvXhyaYjwMcuZm6xKokT", + "label": "one", + "contract": { + "script": "DCECnmSGVirDOqMr57EHaYz0YMTjaHQtO9FQYu8DMTCDw6VBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NLvHRfKAifjio2z9HiwLo9ZnpRPHUbAHgH", + "key": "6PYUjQ8TgR3cduEpG5niUNuPEWi3tYiQsnC4Jha9nGAJ6tAQGUmcrZXsLF", + "label": "two", + "contract": { + "script": "DCECgk91c1ABAX3A1uJNnxhGlp7NwUJScwJzJhrsYrXIbgNBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NcDfG8foJx79XSihcDDrx1df7cHAoJBfXj", + "key": "6PYRkUQKWFrTovHyeQZ7X4nWoDXKohtFRKW51LiCz317pwCjmB1cVwpcxz", + "label": "three", + "contract": { + "script": "DCEC9v0ZqBg8f4jJX9WR891M0bazf0FYTNu7MEgpSHrb9CVBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 2, + "r": 1, + "p": 1 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/cli/testdata/wallet1_solo.json b/cli/testdata/wallet1_solo.json new file mode 100644 index 0000000..9b6d6e0 --- /dev/null +++ b/cli/testdata/wallet1_solo.json @@ -0,0 +1,83 @@ +{ + "version": "1.0", + "accounts": [ + { + "address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", + "key": "6PYM8VdX3hY4B51UJxmm8D41RQMbpJT8aYHibyQ67gjkUPmvQgu51Y5UQR", + "label": "", + "contract": { + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": true + }, + { + "address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq", + "key": "6PYM8VdX3hY4B51UJxmm8D41RQMbpJT8aYHibyQ67gjkUPmvQgu51Y5UQR", + "label": "", + "contract": { + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + }, + { + "name": "parameter1", + "type": "Signature" + }, + { + "name": "parameter2", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP", + "key": "6PYM8VdX3hY4B51UJxmm8D41RQMbpJT8aYHibyQ67gjkUPmvQgu51Y5UQR", + "label": "", + "contract": { + "script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUGe0Nw6", + "parameters": [ + { + "name": "parameter0", + "type": "Signature" + } + ], + "deployed": false + }, + "lock": false, + "isDefault": false + }, + { + "address": "NiFxRcC5Anz9pmqQyMHh5vamBUZDbRRRzA", + "key": "6PYSATFztBa3CHjSR6sLAKungUEAbQUCVE16KzmaQQ38gLeYGZ9Knd5mGv", + "label": "verify", + "contract": { + "parameters": [], + "deployed": true + }, + "lock": false, + "isDefault": false + } + ], + "scrypt": { + "n": 2, + "r": 1, + "p": 1 + }, + "extra": { + "Tokens": null + } +} \ No newline at end of file diff --git a/cli/txctx/tx.go b/cli/txctx/tx.go new file mode 100644 index 0000000..dcac2f6 --- /dev/null +++ b/cli/txctx/tx.go @@ -0,0 +1,115 @@ +/* +Package txctx contains helper functions that deal with transactions in CLI context. +*/ +package txctx + +import ( + "fmt" + "io" + "time" + + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/input" + "github.com/nspcc-dev/neo-go/cli/paramcontext" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/urfave/cli/v2" +) + +var ( + // GasFlag is a flag used for the additional network fee. + GasFlag = &flags.Fixed8Flag{ + Name: "gas", + Aliases: []string{"g"}, + Usage: "Network fee to add to the transaction (prioritizing it)", + } + // SysGasFlag is a flag used for the additional system fee. + SysGasFlag = &flags.Fixed8Flag{ + Name: "sysgas", + Aliases: []string{"e"}, + Usage: "System fee to add to the transaction (compensating for execution)", + } + // OutFlag is a flag used for file output. + OutFlag = &cli.StringFlag{ + Name: "out", + Usage: "File (JSON) to put signature context with a transaction to", + } + // ForceFlag is a flag used to force transaction send. + ForceFlag = &cli.BoolFlag{ + Name: "force", + Usage: "Do not ask for a confirmation (and ignore errors)", + } + // AwaitFlag is a flag used to wait for the transaction to be included in a block. + AwaitFlag = &cli.BoolFlag{ + Name: "await", + Usage: "Wait for the transaction to be included in a block", + } +) + +// SignAndSend adds network and system fees to the provided transaction and +// either sends it to the network (with a confirmation or --force flag) or saves +// it into a file (given in the --out flag). +func SignAndSend(ctx *cli.Context, act *actor.Actor, acc *wallet.Account, tx *transaction.Transaction) error { + var ( + err error + gas = flags.Fixed8FromContext(ctx, "gas") + sysgas = flags.Fixed8FromContext(ctx, "sysgas") + ver = act.GetVersion() + aer *state.AppExecResult + ) + + tx.SystemFee += int64(sysgas) + tx.NetworkFee += int64(gas) + + if outFile := ctx.String("out"); outFile != "" { + // Make a long-lived transaction, it's to be signed manually. + tx.ValidUntilBlock += (ver.Protocol.MaxValidUntilBlockIncrement - uint32(ver.Protocol.ValidatorsCount)) - 2 + err = paramcontext.InitAndSave(ver.Protocol.Network, tx, acc, outFile) + } else { + if !ctx.Bool("force") { + promptTime := time.Now() + err := input.ConfirmTx(ctx.App.Writer, tx) + if err != nil { + return cli.Exit(err, 1) + } + waitTime := time.Since(promptTime) + // Compensate for confirmation waiting. + tx.ValidUntilBlock += uint32(waitTime.Milliseconds()/int64(ver.Protocol.MillisecondsPerBlock)) + 2 + } + var ( + resTx util.Uint256 + vub uint32 + ) + resTx, vub, err = act.SignAndSend(tx) + if err != nil { + return cli.Exit(err, 1) + } + if ctx.Bool("await") { + aer, err = act.Wait(ctx.Context, resTx, vub, err) + if err != nil { + return cli.Exit(fmt.Errorf("failed to await transaction %s: %w", resTx.StringLE(), err), 1) + } + } + } + if err != nil { + return cli.Exit(err, 1) + } + + DumpTransactionInfo(ctx.App.Writer, tx.Hash(), aer) + return nil +} + +// DumpTransactionInfo prints transaction info to the given writer. +func DumpTransactionInfo(w io.Writer, h util.Uint256, res *state.AppExecResult) { + fmt.Fprintln(w, h.StringLE()) + if res != nil { + fmt.Fprintf(w, "OnChain:\t%t\n", res != nil) + fmt.Fprintf(w, "VMState:\t%s\n", res.VMState.String()) + if res.FaultException != "" { + fmt.Fprintf(w, "FaultException:\t%s\n", res.FaultException) + } + } +} diff --git a/cli/util/audit-bin.go b/cli/util/audit-bin.go new file mode 100644 index 0000000..87ccc90 --- /dev/null +++ b/cli/util/audit-bin.go @@ -0,0 +1,261 @@ +package util + +import ( + "errors" + "fmt" + "strconv" + "sync" + "time" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/pkg/core/block" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/nspcc-dev/neofs-sdk-go/client" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + "github.com/nspcc-dev/neofs-sdk-go/object" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + "github.com/nspcc-dev/neofs-sdk-go/pool" + "github.com/nspcc-dev/neofs-sdk-go/user" + "github.com/urfave/cli/v2" +) + +func auditBin(ctx *cli.Context) error { + if err := cmdargs.EnsureNone(ctx); err != nil { + return err + } + var ( + numWorkers = ctx.Uint("workers") + + err error + errs = make(chan error, numWorkers) + haveErrors bool + tasks = make(chan func() error) + wg sync.WaitGroup + ) + for range numWorkers { + wg.Add(1) + go func() { + for f := range tasks { + err := f() + if err != nil { + errs <- err + break + } + } + wg.Done() + }() + } + + err = auditBinInt(ctx, tasks, errs) + close(tasks) + wg.Wait() + +drainErrors: + for { + select { + case anotherErr := <-errs: + fmt.Fprintf(ctx.App.Writer, "error in worker thread: %s\n", anotherErr) + haveErrors = true + default: + break drainErrors + } + } + if err == nil { + if haveErrors { + err = cli.Exit(errors.New("audit failed"), 1) // Change return code to signal thread errors. + } else { + fmt.Fprintln(ctx.App.Writer, "Audit is completed.") + } + } + return err +} + +func auditBinInt(ctx *cli.Context, tasks chan func() error, errs chan error) error { + retries := ctx.Uint("retries") + cnrID := ctx.String("container") + debug := ctx.Bool("debug") + dryRun := ctx.Bool("dry-run") + blockAttr := ctx.String("block-attribute") + curH := uint64(ctx.Uint("skip")) + + acc, _, err := options.GetAccFromContext(ctx) + if err != nil { + if errors.Is(err, options.ErrNoWallet) { + acc, err = wallet.NewAccount() + if err != nil { + return cli.Exit(fmt.Errorf("no wallet provided and failed to create account for NeoFS interaction: %w", err), 1) + } + } else { + return cli.Exit(fmt.Errorf("failed to load account: %w", err), 1) + } + } + signer, neoFSPool, err := options.GetNeoFSClientPool(ctx, acc) + if err != nil { + return cli.Exit(err, 1) + } + defer neoFSPool.Close() + + var containerID cid.ID + if err = containerID.DecodeString(cnrID); err != nil { + return cli.Exit(fmt.Errorf("failed to decode container ID: %w", err), 1) + } + if _, err = neoFSPool.ContainerGet(ctx.Context, containerID, client.PrmContainerGet{}); err != nil { + return cli.Exit(fmt.Errorf("failed to get container %s: %w", containerID, err), 1) + } + + if curH != 0 { + fmt.Fprintf(ctx.App.Writer, "Skipping first %d blocks\n", curH) + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + rpc, err := options.GetRPCClient(gctx, ctx) + if err != nil { + return cli.Exit(fmt.Errorf("failed to create RPC client: %w", err), 1) + } + + var ( + prevH uint64 + cursor string + curOID oid.ID + f = object.NewSearchFilters() + ) + f.AddFilter(blockAttr, strconv.FormatUint(curH, 10), object.MatchNumGE) + + for { + var page []client.SearchResultItem + err = retry(func() error { + page, cursor, err = neoFSPool.SearchObjects(ctx.Context, containerID, f, []string{blockAttr}, cursor, signer, client.SearchObjectsOptions{}) + if err != nil { + return fmt.Errorf("failed to search objects: %w", err) + } + return nil + }, retries, debug) + if err != nil { + return cli.Exit(fmt.Errorf("search block objects: %w", err), 1) + } + + for _, itm := range page { + select { + case <-ctx.Done(): + return cli.Exit("context cancelled", 1) + case err := <-errs: + return cli.Exit(fmt.Errorf("error in worker thread: %w", err), 1) + default: + } + if len(itm.Attributes) != 1 { + fmt.Fprintf(ctx.App.Writer, "invalid number attributes for %s: expected %d, got %d", itm.ID, 1, len(itm.Attributes)) + continue + } + h, err := strconv.ParseUint(itm.Attributes[0], 10, 64) + if err != nil { + return cli.Exit(fmt.Errorf("failed to parse block OID (%s): %w", itm.ID, err), 1) + } + + if !curOID.IsZero() && prevH == h { + if dryRun { + fmt.Fprintf(ctx.App.Writer, "[dry-run] block duplicate %s / %s (%d)\n", itm.ID, curOID, prevH) + } else { + tasks <- wrapDropBlock(ctx, neoFSPool, signer, containerID, itm.ID, retries, prevH, curOID, debug) + } + continue + } + + for ; curH < h; curH++ { + if dryRun { + fmt.Fprintf(ctx.App.Writer, "[dry-run] block %d is missing\n", curH) + } else { + tasks <- wrapRestoreMissingBlock(ctx, rpc, neoFSPool, signer, containerID, blockAttr, retries, curH, debug) + } + } + curOID = itm.ID + prevH = curH + curH++ + } + if cursor == "" { + break + } + } + + return nil +} + +func wrapDropBlock(ctx *cli.Context, p *pool.Pool, signer user.Signer, containerID cid.ID, objID oid.ID, retries uint, prevH uint64, curOID oid.ID, debug bool) func() error { + return func() error { + return dropBlock(ctx, p, signer, containerID, objID, retries, prevH, curOID, debug) + } +} + +func dropBlock(ctx *cli.Context, p *pool.Pool, signer user.Signer, containerID cid.ID, objID oid.ID, retries uint, prevH uint64, curOID oid.ID, debug bool) error { + err := retry(func() error { + _, e := p.ObjectDelete(ctx.Context, containerID, objID, signer, client.PrmObjectDelete{}) + return e + }, retries, debug) + if err != nil { + err = fmt.Errorf("failed to remove block duplicate %s / %s (%d): %w", objID, curOID, prevH, err) + } else if debug { + fmt.Fprintf(ctx.App.Writer, "block duplicate %s for %d is removed (%s kept)\n", objID, prevH, curOID) + } + return err +} + +func wrapRestoreMissingBlock(ctx *cli.Context, rpc *rpcclient.Client, p *pool.Pool, signer user.Signer, containerID cid.ID, + blockAttr string, retries uint, index uint64, debug bool) func() error { + return func() error { + return restoreMissingBlock(ctx, rpc, p, signer, containerID, blockAttr, retries, index, debug) + } +} +func restoreMissingBlock(ctx *cli.Context, rpc *rpcclient.Client, p *pool.Pool, signer user.Signer, containerID cid.ID, + blockAttr string, retries uint, index uint64, debug bool) error { + var ( + b *block.Block + err error + ) + err = retry(func() error { + b, err = rpc.GetBlockByIndex(uint32(index)) + return err + }, retries, debug) + if err != nil { + return fmt.Errorf("failed to fetch block %d: %w", index, err) + } + + bw := io.NewBufBinWriter() + b.EncodeBinary(bw.BinWriter) + if bw.Err != nil { + return fmt.Errorf("failed to encode block %d: %w", index, bw.Err) + } + + _, err = createBlockAndUpload(ctx, p, signer, containerID, b, bw, blockAttr, retries, index, debug) + return err +} + +func createBlockAndUpload(ctx *cli.Context, p *pool.Pool, signer user.Signer, containerID cid.ID, b *block.Block, + bw *io.BufBinWriter, blockAttr string, retries uint, index uint64, debug bool) (oid.ID, error) { + attrs := []object.Attribute{ + object.NewAttribute(blockAttr, strconv.FormatUint(uint64(b.Index), 10)), + object.NewAttribute("Hash", b.Hash().StringLE()), + object.NewAttribute("PrevHash", b.PrevHash.StringLE()), + object.NewAttribute("BlockTime", strconv.FormatUint(b.Timestamp, 10)), + object.NewAttribute("Timestamp", strconv.FormatInt(time.Now().Unix(), 10)), + } + + var ( + objBytes = bw.Bytes() + OID oid.ID + ) + err := retry(func() error { + var e error + OID, e = uploadObj(ctx.Context, p, signer, containerID, objBytes, attrs) + return e + }, retries, debug) + if err != nil { + return oid.ID{}, fmt.Errorf("failed to upload block %d: %w", index, err) + } + if debug { + fmt.Fprintf(ctx.App.Writer, "block %d is uploaded: %s\n", index, OID) + } + return OID, nil +} diff --git a/cli/util/cancel.go b/cli/util/cancel.go new file mode 100644 index 0000000..eca0f5d --- /dev/null +++ b/cli/util/cancel.go @@ -0,0 +1,103 @@ +package util + +import ( + "errors" + "fmt" + "strings" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/cli/txctx" + "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/urfave/cli/v2" +) + +func cancelTx(ctx *cli.Context) error { + args := ctx.Args().Slice() + if len(args) == 0 { + return cli.Exit("transaction hash is missing", 1) + } else if len(args) > 1 { + return cli.Exit("only one transaction hash is accepted", 1) + } + + txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x")) + if err != nil { + return cli.Exit(fmt.Sprintf("invalid tx hash: %s", args[0]), 1) + } + + gctx, cancel := options.GetTimeoutContext(ctx) + defer cancel() + + acc, w, err := options.GetAccFromContext(ctx) + if err != nil { + return cli.Exit(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1) + } + defer w.Close() + + signers, err := cmdargs.GetSignersAccounts(acc, w, nil, transaction.CalledByEntry) + if err != nil { + return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1) + } + c, a, exitErr := options.GetRPCWithActor(gctx, ctx, signers) + if exitErr != nil { + return exitErr + } + + mainTx, _ := c.GetRawTransactionVerbose(txHash) + if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) { + return cli.Exit(fmt.Errorf("target transaction %s is accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1) + } + + if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) { + return cli.Exit(fmt.Errorf("account %s is not a signer of the conflicting transaction", acc.Address), 1) + } + + resHash, resVub, err := a.SendTunedRun([]byte{byte(opcode.RET)}, []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: txHash}}}, func(r *result.Invoke, t *transaction.Transaction) error { + err := actor.DefaultCheckerModifier(r, t) + if err != nil { + return err + } + if mainTx != nil { + t.NetworkFee = max(t.NetworkFee, mainTx.NetworkFee+1) + } + t.NetworkFee += int64(flags.Fixed8FromContext(ctx, "gas")) + if mainTx != nil { + t.ValidUntilBlock = mainTx.ValidUntilBlock + } + return nil + }) + if err != nil { + return cli.Exit(fmt.Errorf("failed to send conflicting transaction: %w", err), 1) + } + var res *state.AppExecResult + if ctx.Bool("await") { + res, err = a.WaitAny(gctx, resVub, txHash, resHash) + if err != nil { + if errors.Is(err, waiter.ErrTxNotAccepted) { + if mainTx == nil { + return cli.Exit(fmt.Errorf("neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of conlicting transaction). Main transaction is unknown to the provided RPC node, thus still has chances to be accepted, you may try cancellation again", resVub), 1) + } + fmt.Fprintf(ctx.App.Writer, "Neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of both target and conflicting transactions). Main transaction is not valid anymore, cancellation is successful\n", resVub) + return nil + } + return cli.Exit(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1) + } + if txHash.Equals(res.Container) { + tx, err := c.GetRawTransactionVerbose(txHash) + if err != nil { + return cli.Exit(fmt.Errorf("target transaction %s is accepted", txHash), 1) + } + return cli.Exit(fmt.Errorf("target transaction %s is accepted at block %s", txHash, tx.Blockhash.StringLE()), 1) + } + fmt.Fprintln(ctx.App.Writer, "Conflicting transaction accepted") + } + txctx.DumpTransactionInfo(ctx.App.Writer, resHash, res) + return nil +} diff --git a/cli/util/convert.go b/cli/util/convert.go new file mode 100644 index 0000000..01475b6 --- /dev/null +++ b/cli/util/convert.go @@ -0,0 +1,275 @@ +package util + +import ( + "encoding/base64" + "encoding/hex" + "fmt" + "os" + + "github.com/nspcc-dev/neo-go/cli/cmdargs" + "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/options" + "github.com/nspcc-dev/neo-go/cli/txctx" + vmcli "github.com/nspcc-dev/neo-go/cli/vm" + "github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/urfave/cli/v2" +) + +var neoFSFlags = append([]cli.Flag{ + &cli.StringFlag{ + Name: "container", + Aliases: []string{"cid"}, + Usage: "NeoFS container ID to upload objects to", + Required: true, + Action: cmdargs.EnsureNotEmpty("container"), + }, + &flags.AddressFlag{ + Name: "address", + Usage: "Address to use for signing the uploading and searching transactions in NeoFS", + }, + &cli.UintFlag{ + Name: "retries", + Usage: "Maximum number of NeoFS node request retries", + Value: neofs.MaxRetries, + Action: func(context *cli.Context, u uint) error { + if u < 1 { + return cli.Exit("retries should be greater than 0", 1) + } + return nil + }, + }}, options.NeoFSRPC...) + +// NewCommands returns util commands for neo-go CLI. +func NewCommands() []*cli.Command { + // By default, RPC flag is required. sendtx and txdump may be called without provided rpc-endpoint. + rpcFlagOriginal, _ := options.RPC[0].(*cli.StringFlag) + rpcFlag := *rpcFlagOriginal + rpcFlag.Required = false + txDumpFlags := append([]cli.Flag{&rpcFlag}, options.RPC[1:]...) + txSendFlags := append(txDumpFlags, txctx.AwaitFlag) + txCancelFlags := append([]cli.Flag{ + &flags.AddressFlag{ + Name: "address", + Aliases: []string{"a"}, + Usage: "Address to use as conflicting transaction signee (and gas source)", + }, + txctx.GasFlag, + txctx.AwaitFlag, + }, options.RPC...) + txCancelFlags = append(txCancelFlags, options.Wallet...) + uploadBinFlags := append([]cli.Flag{ + &cli.StringFlag{ + Name: "block-attribute", + Usage: "Attribute key of the block object", + Value: neofs.DefaultBlockAttribute, + Action: cmdargs.EnsureNotEmpty("block-attribute"), + }, + &cli.UintFlag{ + Name: "batch-size", + Usage: "Size of the batch of blocks to upload in parallel. The batch size should be consistent from run to run, otherwise gaps in the uploaded blocks may occur.", + Value: neofs.DefaultBatchSize, + }, + &cli.UintFlag{ + Name: "workers", + Usage: "Number of workers to fetch and upload blocks concurrently", + Value: 20, + }, + options.Debug, + options.ForceTimestampLogs, + }, options.RPC...) + uploadBinFlags = append(uploadBinFlags, options.Wallet...) + uploadBinFlags = append(uploadBinFlags, neoFSFlags...) + + uploadStateFlags := append([]cli.Flag{ + &cli.StringFlag{ + Name: "state-attribute", + Usage: "Attribute key of the state object", + Value: neofs.DefaultStateAttribute, + Action: cmdargs.EnsureNotEmpty("state-attribute"), + }, + options.Debug, options.ForceTimestampLogs, options.Config, options.ConfigFile, options.RelativePath, + }, options.Wallet...) + uploadStateFlags = append(uploadStateFlags, options.Network...) + uploadStateFlags = append(uploadStateFlags, neoFSFlags...) + + auditBinFlags := append([]cli.Flag{ + &cli.StringFlag{ + Name: "block-attribute", + Usage: "Attribute key of the block object", + Value: neofs.DefaultBlockAttribute, + Action: cmdargs.EnsureNotEmpty("block-attribute"), + }, + &cli.BoolFlag{ + Name: "dry-run", + Usage: "If set, the command will not delete any objects, but will print the list of objects to be deleted", + Value: false, + }, + &cli.UintFlag{ + Name: "workers", + Usage: "Number of workers to delete and upload blocks concurrently", + Value: 20, + }, + &cli.IntFlag{ + Name: "skip", + Usage: "Number of blocks to skip audit for", + Value: 0, + Action: func(context *cli.Context, i int) error { + if i < 0 { + return cli.Exit("negative --skip", 1) + } + return nil + }, + }, + options.Debug, + options.ForceTimestampLogs, + }, neoFSFlags...) + auditBinFlags = append(auditBinFlags, options.Wallet...) + auditBinFlags = append(auditBinFlags, options.RPC...) + return []*cli.Command{ + { + Name: "util", + Usage: "Various helper commands", + Subcommands: []*cli.Command{ + { + Name: "convert", + Usage: "Convert provided argument into other possible formats", + UsageText: `convert + + is an argument which is tried to be interpreted as an item of different types + and converted to other formats. Strings are escaped and output in quotes.`, + Action: handleParse, + }, + { + Name: "sendtx", + Usage: "Send complete transaction stored in a context file", + UsageText: "sendtx [-r ] [--await] ", + Description: `Sends the transaction from the given context file to the given RPC node if it's + completely signed and ready. This command expects a ContractParametersContext + JSON file for input, it can't handle binary (or hex- or base64-encoded) + transactions. If the --await flag is included, the command waits for the + transaction to be included in a block before exiting. +`, + Action: sendTx, + Flags: txSendFlags, + }, + { + Name: "canceltx", + Usage: "Cancel transaction by sending conflicting transaction", + UsageText: "canceltx -r --wallet [--address ] [--wallet-config ] [--gas ] [--await] ", + Description: `Aims to prevent a transaction from being added to the blockchain by dispatching a more + prioritized conflicting transaction to the specified RPC node. The input for this command should + be the transaction hash. If another account is not specified, the conflicting transaction is + automatically generated and signed by the default account in the wallet. If the target transaction + is in the memory pool of the provided RPC node, the NetworkFee value of the conflicting transaction + is set to the target transaction's NetworkFee value plus one (if it's sufficient for the + conflicting transaction itself), the ValidUntilBlock value of the conflicting transaction is set to the + target transaction's ValidUntilBlock value. If the target transaction is not in the memory pool, standard + NetworkFee calculations are performed based on the calculatenetworkfee RPC request. If the --gas + flag is included, the specified value is added to the resulting conflicting transaction network fee + in both scenarios. When the --await flag is included, the command waits for one of the conflicting + or target transactions to be included in a block. +`, + Action: cancelTx, + Flags: txCancelFlags, + }, + { + Name: "txdump", + Usage: "Dump transaction stored in file", + UsageText: "txdump [-r ] ", + Action: txDump, + Flags: txDumpFlags, + Description: `Dumps the transaction from the given parameter context file to + the output. This command expects a ContractParametersContext JSON file for input, it can't handle + binary (or hex- or base64-encoded) transactions. If --rpc-endpoint flag is specified the result + of the given script after running it true the VM will be printed. Otherwise only transaction will + be printed. +`, + }, + { + Name: "ops", + Usage: "Pretty-print VM opcodes of the given base64- or hex- encoded script (base64 is checked first). If the input file is specified, then the script is taken from the file.", + UsageText: "ops [-i path-to-file] [--hex] ", + Action: handleOps, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "in", + Aliases: []string{"i"}, + Usage: "Input file containing base64- or hex- encoded script representation", + }, + &cli.BoolFlag{ + Name: "hex", + Usage: "Use hex encoding and do not check base64", + }, + }, + }, + { + Name: "upload-bin", + Usage: "Fetch blocks from RPC node and upload them to the NeoFS container", + UsageText: "neo-go util upload-bin --fs-rpc-endpoint [,[...]] --container --block-attribute block --rpc-endpoint [--timeout