From a63b3dcb70506ce0181c3853b59f50db2f4dfb60 Mon Sep 17 00:00:00 2001 From: Tutus Development Date: Sat, 27 Dec 2025 09:54:31 -0500 Subject: [PATCH] Migrate from GitHub to git.marketally.com MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update all module paths and import statements from github.com/tutus-one to git.marketally.com/tutus-one for self-hosted Git server migration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cli/app/app.go | 14 +- cli/app/main_test.go | 6 +- cli/cmdargs/parser.go | 14 +- cli/cmdargs/parser_test.go | 10 +- cli/flags/address.go | 4 +- cli/flags/address_test.go | 6 +- cli/flags/fixed8.go | 2 +- cli/flags/fixed8_test.go | 2 +- cli/input/input.go | 4 +- cli/main.go | 2 +- cli/nep_test/nep11_test.go | 18 +- cli/nep_test/nep17_test.go | 14 +- cli/options/cli_options_test.go | 6 +- cli/options/options.go | 28 +- cli/options/options_test.go | 2 +- cli/paramcontext/context.go | 8 +- cli/query/query.go | 26 +- cli/query/query_test.go | 38 +- cli/server/cli_dump_test.go | 6 +- cli/server/cli_server_test.go | 8 +- cli/server/dump.go | 4 +- cli/server/dump_bin.go | 8 +- cli/server/dump_bin_test.go | 6 +- cli/server/server.go | 36 +- cli/server/server_test.go | 8 +- cli/smartcontract/contract_test.go | 52 +- cli/smartcontract/generate.go | 8 +- cli/smartcontract/generate_test.go | 40 +- cli/smartcontract/manifest.go | 14 +- cli/smartcontract/permission.go | 6 +- .../nft-d/dynamic_hash/rpcbindings_test.go | 10 +- .../rpcbindings/nft-d/rpcbindings_test.go | 10 +- .../nft-nd/dynamic_hash/rpcbindings_test.go | 16 +- .../rpcbindings/nft-nd/rpcbindings_test.go | 16 +- .../rpcbindings/extended/rpcbindings_test.go | 10 +- .../rpcbindings/guessed/rpcbindings_test.go | 10 +- .../rpcbindings/rpcbindings_test.go | 8 +- .../dynamic_hash/rpcbindings_test.go | 4 +- .../royalty/rpcbindings/rpcbindings_test.go | 4 +- .../dynamic_hash/rpcbindings_test.go | 12 +- .../structs/rpcbindings/rpcbindings_test.go | 12 +- .../dynamic_hash/rpcbindings_test.go | 12 +- .../types/rpcbindings/rpcbindings_test.go | 12 +- .../rpcbindings/verify/rpcbindings_test.go | 10 +- cli/smartcontract/smart_contract.go | 40 +- cli/smartcontract/smart_contract_test.go | 8 +- cli/smartcontract/testdata/deploy/main.go | 12 +- cli/smartcontract/testdata/deploy/sub/put.go | 2 +- cli/smartcontract/testdata/gas/gas.go | 4 +- .../testdata/invalid1/invalid.go | 4 +- .../testdata/invalid2/invalid.go | 4 +- .../testdata/invalid3/invalid.go | 4 +- cli/smartcontract/testdata/nameservice/nns.go | 14 +- cli/smartcontract/testdata/nex/nex.go | 14 +- cli/smartcontract/testdata/nonepiter/iter.go | 8 +- .../testdata/rpcbindings/invalid1/invalid.go | 2 +- .../testdata/rpcbindings/invalid2/invalid.go | 2 +- .../testdata/rpcbindings/invalid3/invalid.go | 2 +- .../testdata/rpcbindings/invalid4/invalid.go | 2 +- .../testdata/rpcbindings/invalid5/invalid.go | 2 +- .../testdata/rpcbindings/invalid6/invalid.go | 2 +- .../testdata/rpcbindings/invalid7/invalid.go | 2 +- .../notifications/notifications.go | 4 +- .../testdata/rpcbindings/royalty/royalty.go | 6 +- .../testdata/rpcbindings/structs/structs.go | 6 +- .../testdata/rpcbindings/types/types.go | 2 +- cli/smartcontract/testdata/verify.go | 4 +- cli/txctx/tx.go | 16 +- cli/util/audit-bin.go | 12 +- cli/util/cancel.go | 22 +- cli/util/convert.go | 14 +- cli/util/dump.go | 8 +- cli/util/send.go | 10 +- cli/util/upload_bin.go | 12 +- cli/util/upload_state.go | 16 +- cli/util/util_test.go | 10 +- cli/vm/cli.go | 50 +- cli/vm/cli_test.go | 58 +- cli/vm/vm.go | 6 +- cli/wallet/candidate_test.go | 4 +- cli/wallet/legacy.go | 10 +- cli/wallet/legacy_test.go | 2 +- cli/wallet/multisig.go | 16 +- cli/wallet/multisig_test.go | 18 +- cli/wallet/nep11.go | 26 +- cli/wallet/nep17.go | 40 +- cli/wallet/validator.go | 24 +- cli/wallet/wallet.go | 30 +- cli/wallet/wallet_test.go | 16 +- examples/engine/engine.go | 2 +- examples/engine/go.mod | 8 +- examples/engine/go.sum | 4 +- examples/events/events.go | 4 +- examples/events/go.mod | 8 +- examples/events/go.sum | 4 +- examples/iterator/go.mod | 8 +- examples/iterator/go.sum | 4 +- examples/iterator/iterator.go | 6 +- examples/neofs/go.mod | 8 +- examples/neofs/go.sum | 4 +- examples/neofs/neofs.go | 10 +- examples/nft-d/go.mod | 8 +- examples/nft-d/go.sum | 4 +- examples/nft-d/nft.go | 22 +- examples/nft-nd-nns/go.mod | 10 +- examples/nft-nd-nns/go.sum | 8 +- examples/nft-nd-nns/namestate.go | 4 +- examples/nft-nd-nns/nns.go | 18 +- examples/nft-nd-nns/nns_test.go | 16 +- examples/nft-nd/go.mod | 8 +- examples/nft-nd/go.sum | 4 +- examples/nft-nd/nft.go | 22 +- examples/oracle/go.mod | 8 +- examples/oracle/go.sum | 4 +- examples/oracle/oracle.go | 6 +- examples/runtime/go.mod | 8 +- examples/runtime/go.sum | 4 +- examples/runtime/runtime.go | 6 +- examples/storage/go.mod | 8 +- examples/storage/go.sum | 4 +- examples/storage/storage.go | 4 +- examples/timer/go.mod | 8 +- examples/timer/go.sum | 4 +- examples/timer/timer.go | 12 +- examples/token/go.mod | 8 +- examples/token/go.sum | 4 +- examples/token/nep17/nep17.go | 10 +- examples/token/token.go | 8 +- examples/zkp/cubic_circuit/go.mod | 10 +- examples/zkp/cubic_circuit/go.sum | 8 +- examples/zkp/cubic_circuit/main_test.go | 6 +- examples/zkp/xor_compat/go.mod | 8 +- examples/zkp/xor_compat/go.sum | 4 +- examples/zkp/xor_compat/verify.go | 6 +- go.mod | 14 +- go.sum | 16 +- internal/basicchain/basic.go | 24 +- .../testdata/invoke/invokescript_contract.go | 2 +- .../testdata/storage/storage_contract.go | 4 +- internal/basicchain/testdata/test_contract.go | 16 +- .../testdata/verify/verification_contract.go | 6 +- internal/contracts/contracts.go | 8 +- internal/contracts/contracts_test.go | 30 +- internal/contracts/oracle_contract/go.mod | 6 +- internal/contracts/oracle_contract/go.sum | 4 +- internal/contracts/oracle_contract/oracle.go | 14 +- internal/fakechain/fakechain.go | 30 +- internal/random/random_util.go | 4 +- internal/testchain/address.go | 18 +- internal/testchain/network.go | 2 +- internal/testchain/transaction.go | 28 +- internal/testcli/executor.go | 28 +- internal/testserdes/testing.go | 4 +- internal/versionutil/init.go | 2 +- pkg/compiler/analysis.go | 10 +- pkg/compiler/assign_test.go | 6 +- pkg/compiler/binary_expr_test.go | 6 +- pkg/compiler/builtin_test.go | 4 +- pkg/compiler/codegen.go | 22 +- pkg/compiler/codegen_test.go | 2 +- pkg/compiler/compiler.go | 14 +- pkg/compiler/compiler_test.go | 46 +- pkg/compiler/constant_test.go | 2 +- pkg/compiler/convert_test.go | 22 +- pkg/compiler/debug.go | 12 +- pkg/compiler/debug_test.go | 16 +- pkg/compiler/defer_test.go | 4 +- pkg/compiler/for_test.go | 8 +- pkg/compiler/function_call_test.go | 16 +- pkg/compiler/generics_test.go | 2 +- pkg/compiler/global_test.go | 36 +- pkg/compiler/import_test.go | 14 +- pkg/compiler/init_test.go | 16 +- pkg/compiler/inline.go | 14 +- pkg/compiler/inline_test.go | 66 +- pkg/compiler/interop_test.go | 122 +- pkg/compiler/jumps_test.go | 2 +- pkg/compiler/map_test.go | 2 +- pkg/compiler/native_test.go | 52 +- pkg/compiler/slice_test.go | 10 +- pkg/compiler/struct_test.go | 6 +- pkg/compiler/switch_test.go | 4 +- pkg/compiler/syscall_test.go | 82 +- pkg/compiler/testdata/compile/test.go | 2 +- .../testdata/globalvar/funccall/main.go | 6 +- .../testdata/globalvar/nested1/main.go | 4 +- .../testdata/importcycle/pkg1/pkg1.go | 2 +- .../testdata/importcycle/pkg2/pkg2.go | 2 +- .../testdata/importcycle/pkg3/pkg3.go | 2 +- pkg/compiler/testdata/inline/inline.go | 4 +- pkg/compiler/testdata/nestedcall/call.go | 2 +- pkg/compiler/testdata/notify/event.go | 2 +- pkg/compiler/testdata/pkg1/pkg1.go | 2 +- pkg/compiler/testdata/pkg2/pkg2.go | 2 +- pkg/compiler/testdata/runh/hash.go | 2 +- pkg/compiler/types.go | 2 +- pkg/compiler/vardecl_test.go | 2 +- pkg/compiler/verify_test.go | 8 +- pkg/compiler/vm_test.go | 20 +- pkg/config/application_config.go | 2 +- pkg/config/application_config_test.go | 2 +- pkg/config/config.go | 4 +- pkg/config/config_test.go | 4 +- pkg/config/genesis_extensions.go | 4 +- pkg/config/hardfork.go | 2 +- pkg/config/ledger_config.go | 2 +- pkg/config/ledger_config_test.go | 4 +- pkg/config/protocol_config.go | 4 +- pkg/config/protocol_config_test.go | 6 +- pkg/config/rpc_config.go | 2 +- pkg/consensus/block.go | 12 +- pkg/consensus/block_test.go | 10 +- pkg/consensus/cache.go | 2 +- pkg/consensus/cache_test.go | 2 +- pkg/consensus/change_view.go | 4 +- pkg/consensus/change_view_test.go | 2 +- pkg/consensus/commit.go | 4 +- pkg/consensus/commit_test.go | 2 +- pkg/consensus/consensus.go | 36 +- pkg/consensus/consensus_test.go | 40 +- pkg/consensus/crypto_test.go | 2 +- pkg/consensus/payload.go | 14 +- pkg/consensus/payload_test.go | 20 +- pkg/consensus/prepare_request.go | 8 +- pkg/consensus/prepare_request_test.go | 8 +- pkg/consensus/prepare_response.go | 6 +- pkg/consensus/prepare_response_test.go | 2 +- pkg/consensus/recovery_message.go | 10 +- pkg/consensus/recovery_message_test.go | 10 +- pkg/consensus/recovery_request.go | 4 +- pkg/core/basic_chain_test.go | 10 +- pkg/core/bench_test.go | 24 +- pkg/core/block/block.go | 10 +- pkg/core/block/block_test.go | 14 +- pkg/core/block/header.go | 12 +- pkg/core/block/header_test.go | 10 +- pkg/core/block/helper_test.go | 2 +- pkg/core/blockchain.go | 64 +- pkg/core/blockchain_core_test.go | 24 +- pkg/core/blockchain_neotest_test.go | 86 +- pkg/core/chaindump/dump.go | 8 +- pkg/core/chaindump/dump_test.go | 14 +- pkg/core/custom_native_test.go | 32 +- pkg/core/dao/dao.go | 22 +- pkg/core/dao/dao_test.go | 20 +- pkg/core/fee/calculate.go | 10 +- pkg/core/fee/opcode.go | 2 +- pkg/core/fee/opcode_test.go | 2 +- pkg/core/headerhashes.go | 8 +- pkg/core/helper_test.go | 14 +- pkg/core/interop/context.go | 38 +- pkg/core/interop/context_test.go | 6 +- pkg/core/interop/contract/account.go | 14 +- pkg/core/interop/contract/account_test.go | 22 +- pkg/core/interop/contract/call.go | 20 +- pkg/core/interop/contract/call_test.go | 86 +- pkg/core/interop/crypto/ecdsa.go | 12 +- pkg/core/interop/crypto/ecdsa_test.go | 30 +- pkg/core/interop/crypto/interop.go | 4 +- pkg/core/interop/gas_price.go | 4 +- pkg/core/interop/iterator/interop.go | 4 +- pkg/core/interop/iterator/interop_test.go | 6 +- pkg/core/interop/runtime/engine.go | 12 +- pkg/core/interop/runtime/engine_test.go | 20 +- pkg/core/interop/runtime/ext_test.go | 58 +- pkg/core/interop/runtime/util.go | 14 +- pkg/core/interop/runtime/util_test.go | 14 +- pkg/core/interop/runtime/witness.go | 16 +- pkg/core/interop/storage/basic.go | 6 +- pkg/core/interop/storage/bench_test.go | 12 +- pkg/core/interop/storage/find.go | 6 +- pkg/core/interop/storage/interops_test.go | 4 +- pkg/core/interop/storage/storage_test.go | 44 +- pkg/core/interops.go | 24 +- pkg/core/mempool/bench_test.go | 4 +- pkg/core/mempool/feer.go | 2 +- pkg/core/mempool/mem_pool.go | 6 +- pkg/core/mempool/mem_pool_test.go | 8 +- pkg/core/mempool/subscriptions.go | 2 +- pkg/core/mempool/subscriptions_test.go | 8 +- pkg/core/mempoolevent/event.go | 2 +- pkg/core/mpt/base.go | 6 +- pkg/core/mpt/batch_test.go | 2 +- pkg/core/mpt/bench_test.go | 2 +- pkg/core/mpt/billet.go | 6 +- pkg/core/mpt/billet_test.go | 6 +- pkg/core/mpt/branch.go | 4 +- pkg/core/mpt/empty.go | 4 +- pkg/core/mpt/extension.go | 6 +- pkg/core/mpt/hash.go | 4 +- pkg/core/mpt/helpers.go | 2 +- pkg/core/mpt/helpers_test.go | 2 +- pkg/core/mpt/leaf.go | 6 +- pkg/core/mpt/node.go | 4 +- pkg/core/mpt/node_test.go | 6 +- pkg/core/mpt/proof.go | 6 +- pkg/core/mpt/trie.go | 6 +- pkg/core/mpt/trie_store.go | 4 +- pkg/core/mpt/trie_store_test.go | 2 +- pkg/core/mpt/trie_test.go | 4 +- pkg/core/native/ancora.go | 2614 ++++----- pkg/core/native/annos.go | 22 +- pkg/core/native/audit_logger.go | 1552 ++--- pkg/core/native/canary_deployment.go | 1304 ++-- pkg/core/native/circuit_breaker.go | 1110 ++-- pkg/core/native/collocatio.go | 5228 ++++++++--------- pkg/core/native/compatibility_test.go | 2 +- pkg/core/native/config_registry.go | 1008 ++-- pkg/core/native/contract.go | 24 +- pkg/core/native/contract_test.go | 2 +- pkg/core/native/crypto.go | 26 +- pkg/core/native/crypto_blspoints.go | 2 +- pkg/core/native/crypto_test.go | 10 +- pkg/core/native/designate.go | 32 +- pkg/core/native/eligere.go | 1900 +++--- pkg/core/native/event_archival.go | 8 +- pkg/core/native/federation.go | 1444 ++--- pkg/core/native/interop.go | 12 +- pkg/core/native/invariants.go | 1056 ++-- pkg/core/native/invocation_test.go | 28 +- pkg/core/native/ledger.go | 24 +- pkg/core/native/lex.go | 22 +- pkg/core/native/management.go | 38 +- pkg/core/native/management_neotest_test.go | 24 +- pkg/core/native/management_test.go | 22 +- pkg/core/native/native_lub.go | 22 +- pkg/core/native/native_neo_test.go | 2 +- pkg/core/native/native_nep17.go | 24 +- pkg/core/native/native_test/ancora_test.go | 564 +- pkg/core/native/native_test/annos_test.go | 8 +- .../native/native_test/collocatio_test.go | 8 +- pkg/core/native/native_test/common_test.go | 26 +- pkg/core/native/native_test/cryptolib_test.go | 20 +- .../cryptolib_verification_test.go | 26 +- pkg/core/native/native_test/designate_test.go | 26 +- pkg/core/native/native_test/eligere_test.go | 8 +- .../native/native_test/federation_test.go | 8 +- .../native_test/generate_expected_test.go | 8 +- .../helpers/vitahelper/vitahelper.go | 152 +- pkg/core/native/native_test/ledger_test.go | 26 +- pkg/core/native/native_test/lex_test.go | 8 +- pkg/core/native/native_test/lub_test.go | 20 +- .../native/native_test/management_test.go | 56 +- pkg/core/native/native_test/notary_test.go | 36 +- pkg/core/native/native_test/opus_test.go | 6 +- pkg/core/native/native_test/oracle_test.go | 18 +- pkg/core/native/native_test/palam_test.go | 8 +- pkg/core/native/native_test/policy_test.go | 52 +- pkg/core/native/native_test/pons_test.go | 8 +- .../native/native_test/role_registry_test.go | 6 +- pkg/core/native/native_test/salus_test.go | 6 +- pkg/core/native/native_test/scire_test.go | 6 +- pkg/core/native/native_test/sese_test.go | 6 +- pkg/core/native/native_test/std_test.go | 14 +- pkg/core/native/native_test/treasury_test.go | 10 +- pkg/core/native/native_test/tribute_test.go | 6 +- pkg/core/native/native_test/tutus_test.go | 50 +- pkg/core/native/native_test/vita_test.go | 8 +- pkg/core/native/native_test/vts_test.go | 8 +- pkg/core/native/native_tutus.go | 38 +- pkg/core/native/native_tutus_candidate.go | 2 +- pkg/core/native/nativehashes/gen.go | 6 +- pkg/core/native/nativehashes/hashes.go | 2 +- pkg/core/native/nativeids/gen.go | 2 +- pkg/core/native/nativenames_test.go | 4 +- pkg/core/native/neo_types.go | 4 +- pkg/core/native/notary.go | 38 +- pkg/core/native/opus.go | 24 +- pkg/core/native/oracle.go | 36 +- pkg/core/native/oracle_types.go | 4 +- pkg/core/native/oracle_types_test.go | 6 +- pkg/core/native/palam.go | 2254 +++---- pkg/core/native/policy.go | 30 +- pkg/core/native/policy_test.go | 18 +- pkg/core/native/pons.go | 2520 ++++---- pkg/core/native/role_registry.go | 2122 +++---- pkg/core/native/role_registry_domain.go | 4 +- pkg/core/native/salus.go | 3032 +++++----- pkg/core/native/scire.go | 2554 ++++---- pkg/core/native/sese.go | 22 +- pkg/core/native/std.go | 22 +- pkg/core/native/std_test.go | 10 +- pkg/core/native/treasury.go | 20 +- pkg/core/native/tribute.go | 3294 +++++------ pkg/core/native/util.go | 8 +- pkg/core/native/validation.go | 256 +- pkg/core/native/vita.go | 4524 +++++++------- pkg/core/native/vts.go | 3214 +++++----- pkg/core/native_designate_test.go | 16 +- pkg/core/state/annos.go | 4 +- pkg/core/state/collocatio.go | 6 +- pkg/core/state/contract.go | 16 +- pkg/core/state/contract_invocation.go | 6 +- pkg/core/state/contract_invocation_test.go | 8 +- pkg/core/state/contract_test.go | 14 +- pkg/core/state/deposit.go | 2 +- pkg/core/state/deposit_test.go | 4 +- pkg/core/state/eligere.go | 4 +- pkg/core/state/lex.go | 4 +- pkg/core/state/mpt_root.go | 8 +- pkg/core/state/mpt_root_test.go | 8 +- pkg/core/state/native_state.go | 6 +- pkg/core/state/native_state_test.go | 4 +- pkg/core/state/notification_event.go | 12 +- pkg/core/state/notification_event_test.go | 14 +- pkg/core/state/opus.go | 4 +- pkg/core/state/oracle.go | 4 +- pkg/core/state/oracle_test.go | 6 +- pkg/core/state/palam.go | 4 +- pkg/core/state/pons.go | 260 +- pkg/core/state/role_registry.go | 654 +-- pkg/core/state/salus.go | 1264 ++-- pkg/core/state/scire.go | 4 +- pkg/core/state/sese.go | 4 +- pkg/core/state/state_anchors.go | 846 +-- pkg/core/state/tokens.go | 8 +- pkg/core/state/tokens_test.go | 6 +- pkg/core/state/tribute.go | 1294 ++-- pkg/core/state/validator.go | 2 +- pkg/core/state/vita.go | 960 +-- pkg/core/state/vts.go | 926 +-- pkg/core/stateroot/callbacks.go | 2 +- pkg/core/stateroot/module.go | 20 +- pkg/core/stateroot/store.go | 6 +- pkg/core/stateroot/validators.go | 6 +- pkg/core/statesync/module.go | 20 +- pkg/core/statesync/module_test.go | 10 +- pkg/core/statesync/mptpool.go | 2 +- pkg/core/statesync/mptpool_test.go | 4 +- pkg/core/statesync/neotest_test.go | 16 +- pkg/core/storage/boltdb_store.go | 6 +- pkg/core/storage/boltdb_store_test.go | 6 +- pkg/core/storage/leveldb_store.go | 2 +- pkg/core/storage/leveldb_store_test.go | 2 +- pkg/core/storage/memcached_store_test.go | 2 +- pkg/core/storage/memory_store_test.go | 2 +- pkg/core/storage/store.go | 4 +- pkg/core/storage/store_test.go | 2 +- pkg/core/storage/store_type_test.go | 2 +- pkg/core/transaction/attribute.go | 2 +- pkg/core/transaction/attribute_test.go | 8 +- pkg/core/transaction/bench_test.go | 2 +- pkg/core/transaction/conflicts.go | 4 +- pkg/core/transaction/fuzz_test.go | 2 +- pkg/core/transaction/not_valid_before.go | 2 +- pkg/core/transaction/notary_assisted.go | 4 +- pkg/core/transaction/oracle.go | 2 +- pkg/core/transaction/oracle_test.go | 2 +- pkg/core/transaction/reserved.go | 2 +- pkg/core/transaction/signer.go | 8 +- pkg/core/transaction/signer_test.go | 6 +- pkg/core/transaction/transaction.go | 12 +- pkg/core/transaction/transaction_test.go | 12 +- pkg/core/transaction/witness.go | 6 +- pkg/core/transaction/witness_condition.go | 8 +- .../transaction/witness_condition_test.go | 8 +- pkg/core/transaction/witness_rule.go | 4 +- pkg/core/transaction/witness_rule_test.go | 4 +- pkg/core/transaction/witness_test.go | 2 +- pkg/core/util.go | 16 +- pkg/core/util_test.go | 10 +- pkg/crypto/hash/hash.go | 2 +- pkg/crypto/hash/merkle_bench_test.go | 6 +- pkg/crypto/hash/merkle_tree.go | 2 +- pkg/crypto/hash/merkle_tree_test.go | 2 +- pkg/crypto/keys/nep2.go | 6 +- pkg/crypto/keys/nep2_test.go | 2 +- pkg/crypto/keys/private_key.go | 6 +- pkg/crypto/keys/private_key_test.go | 4 +- pkg/crypto/keys/publickey.go | 10 +- pkg/crypto/keys/publickey_test.go | 2 +- pkg/crypto/keys/sign_verify_test.go | 2 +- pkg/crypto/keys/wif.go | 2 +- pkg/crypto/keys/wif_test.go | 2 +- pkg/crypto/verifiable.go | 2 +- pkg/encoding/address/address.go | 4 +- pkg/encoding/address/address_test.go | 2 +- pkg/encoding/base58/base58.go | 2 +- pkg/encoding/fixedn/fixed8.go | 2 +- pkg/encoding/fixedn/fixed8_test.go | 2 +- pkg/interop/contract/contract.go | 4 +- pkg/interop/convert/convert.go | 2 +- pkg/interop/crypto/crypto.go | 4 +- pkg/interop/go.mod | 2 +- pkg/interop/iterator/iterator.go | 2 +- pkg/interop/lib/address/address.go | 6 +- pkg/interop/lib/contract/contract.go | 6 +- pkg/interop/math/math.go | 2 +- pkg/interop/native/crypto/crypto.go | 6 +- pkg/interop/native/ledger/block.go | 2 +- pkg/interop/native/ledger/ledger.go | 6 +- pkg/interop/native/ledger/transaction.go | 2 +- .../native/ledger/transaction_signer.go | 2 +- pkg/interop/native/lub/lub.go | 6 +- pkg/interop/native/management/contract.go | 2 +- pkg/interop/native/management/management.go | 8 +- pkg/interop/native/notary/notary.go | 6 +- pkg/interop/native/oracle/oracle.go | 4 +- pkg/interop/native/policy/policy.go | 8 +- pkg/interop/native/roles/roles.go | 6 +- pkg/interop/native/std/std.go | 4 +- pkg/interop/native/tutus/tutus.go | 8 +- pkg/interop/native/tutus/tutus_candidate.go | 2 +- pkg/interop/runtime/engine.go | 6 +- pkg/interop/runtime/runtime.go | 8 +- pkg/interop/storage/storage.go | 4 +- pkg/interop/types.go | 2 +- pkg/interop/util/util.go | 2 +- pkg/io/size_test.go | 4 +- pkg/network/bqueue/queue_test.go | 4 +- pkg/network/bqueue_adapters.go | 4 +- pkg/network/capability/capability.go | 2 +- pkg/network/capability/capability_test.go | 2 +- pkg/network/compress.go | 2 +- pkg/network/discovery.go | 2 +- pkg/network/discovery_test.go | 4 +- pkg/network/extpool/pool.go | 8 +- pkg/network/extpool/pool_test.go | 8 +- pkg/network/fuzz_test.go | 4 +- pkg/network/helper_test.go | 8 +- pkg/network/message.go | 8 +- pkg/network/message_test.go | 18 +- pkg/network/notary_feer.go | 2 +- pkg/network/payload/address.go | 4 +- pkg/network/payload/address_test.go | 4 +- pkg/network/payload/extensible.go | 8 +- pkg/network/payload/extensible_test.go | 8 +- pkg/network/payload/getblockbyindex.go | 2 +- pkg/network/payload/getblockbyindex_test.go | 2 +- pkg/network/payload/getblocks.go | 4 +- pkg/network/payload/getblocks_test.go | 4 +- pkg/network/payload/headers.go | 4 +- pkg/network/payload/headers_test.go | 6 +- pkg/network/payload/inventory.go | 4 +- pkg/network/payload/inventory_test.go | 6 +- pkg/network/payload/merkleblock.go | 6 +- pkg/network/payload/merkleblock_test.go | 10 +- pkg/network/payload/mptdata.go | 2 +- pkg/network/payload/mptdata_test.go | 2 +- pkg/network/payload/mptinventory.go | 4 +- pkg/network/payload/mptinventory_test.go | 4 +- pkg/network/payload/notary_request.go | 12 +- pkg/network/payload/notary_request_test.go | 10 +- pkg/network/payload/payload.go | 2 +- pkg/network/payload/ping.go | 2 +- pkg/network/payload/ping_test.go | 2 +- pkg/network/payload/version.go | 6 +- pkg/network/payload/version_test.go | 6 +- pkg/network/peer.go | 2 +- pkg/network/server.go | 32 +- pkg/network/server_config.go | 4 +- pkg/network/server_test.go | 30 +- pkg/network/state_sync.go | 8 +- pkg/network/tcp_peer.go | 6 +- pkg/network/tcp_peer_test.go | 2 +- pkg/rpcclient/actor/actor.go | 18 +- pkg/rpcclient/actor/actor_test.go | 20 +- pkg/rpcclient/actor/compat_test.go | 4 +- pkg/rpcclient/actor/doc_test.go | 20 +- pkg/rpcclient/actor/maker.go | 8 +- pkg/rpcclient/actor/maker_test.go | 6 +- pkg/rpcclient/client.go | 8 +- pkg/rpcclient/doc_test.go | 4 +- pkg/rpcclient/gas/gas.go | 4 +- pkg/rpcclient/gas/gas_test.go | 6 +- pkg/rpcclient/invoker/compat_test.go | 4 +- pkg/rpcclient/invoker/doc_test.go | 16 +- pkg/rpcclient/invoker/invoker.go | 10 +- pkg/rpcclient/invoker/invoker_test.go | 10 +- pkg/rpcclient/local.go | 2 +- pkg/rpcclient/local_test.go | 8 +- pkg/rpcclient/management/management.go | 20 +- pkg/rpcclient/management/management_test.go | 12 +- pkg/rpcclient/nep11/base.go | 14 +- pkg/rpcclient/nep11/base_test.go | 8 +- pkg/rpcclient/nep11/divisible.go | 8 +- pkg/rpcclient/nep11/divisible_test.go | 8 +- pkg/rpcclient/nep11/doc_test.go | 14 +- pkg/rpcclient/nep11/nondivisible.go | 4 +- pkg/rpcclient/nep11/nondivisible_test.go | 6 +- pkg/rpcclient/nep17/doc_test.go | 14 +- pkg/rpcclient/nep17/nep17.go | 12 +- pkg/rpcclient/nep17/nep17_test.go | 8 +- pkg/rpcclient/nep22/nep22.go | 6 +- pkg/rpcclient/nep22/nep22_test.go | 6 +- pkg/rpcclient/nep24/doc_test.go | 8 +- pkg/rpcclient/nep24/royalty.go | 8 +- pkg/rpcclient/nep24/royalty_test.go | 8 +- pkg/rpcclient/nep31/nep31.go | 6 +- pkg/rpcclient/nep31/nep31_test.go | 4 +- pkg/rpcclient/neptoken/base.go | 6 +- pkg/rpcclient/neptoken/base_test.go | 6 +- pkg/rpcclient/neptoken/info.go | 10 +- pkg/rpcclient/neptoken/info_test.go | 20 +- pkg/rpcclient/nns/contract.go | 14 +- pkg/rpcclient/nns/contract_test.go | 10 +- pkg/rpcclient/nns/iterators.go | 4 +- pkg/rpcclient/nns/record.go | 4 +- pkg/rpcclient/nns/record_test.go | 2 +- pkg/rpcclient/notary/accounts.go | 12 +- pkg/rpcclient/notary/accounts_test.go | 6 +- pkg/rpcclient/notary/actor.go | 26 +- pkg/rpcclient/notary/actor_test.go | 34 +- pkg/rpcclient/notary/contract.go | 14 +- pkg/rpcclient/notary/contract_test.go | 10 +- pkg/rpcclient/notary/doc_test.go | 16 +- pkg/rpcclient/oracle/oracle.go | 10 +- pkg/rpcclient/oracle/oracle_test.go | 8 +- pkg/rpcclient/policy/policy.go | 14 +- pkg/rpcclient/policy/policy_test.go | 8 +- pkg/rpcclient/rolemgmt/roles.go | 16 +- pkg/rpcclient/rolemgmt/roles_test.go | 12 +- pkg/rpcclient/rpc.go | 28 +- pkg/rpcclient/rpc_test.go | 44 +- pkg/rpcclient/tutus/doc_test.go | 14 +- pkg/rpcclient/tutus/tutus.go | 20 +- pkg/rpcclient/tutus/tutus_test.go | 12 +- pkg/rpcclient/unwrap/unwrap.go | 10 +- pkg/rpcclient/unwrap/unwrap_test.go | 10 +- pkg/rpcclient/waiter/waiter.go | 12 +- pkg/rpcclient/waiter/waiter_test.go | 24 +- pkg/rpcclient/wsclient.go | 14 +- pkg/rpcclient/wsclient_test.go | 24 +- pkg/services/blockfetcher/blockfetcher.go | 10 +- .../blockfetcher/blockfetcher_test.go | 6 +- pkg/services/helpers/neofs/blockstorage.go | 4 +- pkg/services/helpers/neofs/neofs.go | 4 +- pkg/services/helpers/rpcbroadcaster/client.go | 2 +- pkg/services/metrics/metrics.go | 2 +- pkg/services/metrics/pprof.go | 2 +- pkg/services/metrics/prometheus.go | 2 +- pkg/services/notary/core_test.go | 44 +- pkg/services/notary/node.go | 8 +- pkg/services/notary/node_test.go | 12 +- pkg/services/notary/notary.go | 32 +- pkg/services/notary/notary_test.go | 20 +- pkg/services/oracle/broadcaster/oracle.go | 10 +- pkg/services/oracle/filter.go | 6 +- pkg/services/oracle/jsonpath/jsonpath.go | 2 +- pkg/services/oracle/jsonpath/jsonpath_test.go | 2 +- pkg/services/oracle/network.go | 2 +- pkg/services/oracle/network_test.go | 2 +- pkg/services/oracle/nodes.go | 8 +- pkg/services/oracle/oracle.go | 22 +- pkg/services/oracle/oracle_test.go | 38 +- pkg/services/oracle/request.go | 10 +- pkg/services/oracle/response.go | 18 +- pkg/services/oracle/transaction.go | 16 +- pkg/services/rpcsrv/client_test.go | 96 +- pkg/services/rpcsrv/error.go | 2 +- pkg/services/rpcsrv/local_test.go | 16 +- .../rpcsrv/notification_comparator.go | 10 +- pkg/services/rpcsrv/params/param.go | 12 +- pkg/services/rpcsrv/params/param_test.go | 10 +- pkg/services/rpcsrv/params/params_test.go | 2 +- pkg/services/rpcsrv/params/txBuilder.go | 14 +- pkg/services/rpcsrv/params/tx_builder_test.go | 6 +- pkg/services/rpcsrv/params/types.go | 2 +- pkg/services/rpcsrv/server.go | 70 +- pkg/services/rpcsrv/server_helper_test.go | 22 +- pkg/services/rpcsrv/server_test.go | 64 +- pkg/services/rpcsrv/subscription.go | 2 +- pkg/services/rpcsrv/subscription_test.go | 16 +- pkg/services/rpcsrv/tokens.go | 2 +- pkg/services/statefetcher/statefetcher.go | 10 +- .../statefetcher/statefetcher_test.go | 6 +- pkg/services/stateroot/message.go | 4 +- pkg/services/stateroot/network.go | 14 +- pkg/services/stateroot/service.go | 20 +- pkg/services/stateroot/service_test.go | 48 +- pkg/services/stateroot/signature.go | 16 +- pkg/services/stateroot/validators.go | 12 +- pkg/services/stateroot/vote.go | 4 +- pkg/smartcontract/binding/generate.go | 20 +- pkg/smartcontract/binding/generate_test.go | 2 +- pkg/smartcontract/binding/override.go | 4 +- pkg/smartcontract/builder.go | 10 +- pkg/smartcontract/builder_test.go | 2 +- pkg/smartcontract/callflag/call_flags_test.go | 2 +- pkg/smartcontract/context/context.go | 20 +- pkg/smartcontract/context/context_test.go | 22 +- pkg/smartcontract/context/item.go | 4 +- pkg/smartcontract/context/item_test.go | 8 +- pkg/smartcontract/contract.go | 8 +- pkg/smartcontract/contract_test.go | 8 +- pkg/smartcontract/doc_test.go | 14 +- pkg/smartcontract/entry.go | 12 +- pkg/smartcontract/manifest/abi.go | 2 +- pkg/smartcontract/manifest/abi_test.go | 2 +- pkg/smartcontract/manifest/container_test.go | 6 +- pkg/smartcontract/manifest/event.go | 2 +- pkg/smartcontract/manifest/event_test.go | 4 +- pkg/smartcontract/manifest/group.go | 8 +- pkg/smartcontract/manifest/group_test.go | 6 +- pkg/smartcontract/manifest/manifest.go | 6 +- pkg/smartcontract/manifest/manifest_test.go | 10 +- pkg/smartcontract/manifest/method.go | 4 +- pkg/smartcontract/manifest/method_test.go | 6 +- pkg/smartcontract/manifest/parameter.go | 4 +- pkg/smartcontract/manifest/parameter_test.go | 4 +- pkg/smartcontract/manifest/permission.go | 6 +- pkg/smartcontract/manifest/permission_test.go | 8 +- pkg/smartcontract/manifest/standard/check.go | 2 +- pkg/smartcontract/manifest/standard/comply.go | 2 +- .../manifest/standard/comply_test.go | 4 +- pkg/smartcontract/manifest/standard/nep11.go | 4 +- pkg/smartcontract/manifest/standard/nep17.go | 4 +- pkg/smartcontract/manifest/standard/nep22.go | 4 +- pkg/smartcontract/manifest/standard/nep24.go | 4 +- pkg/smartcontract/manifest/standard/nep26.go | 4 +- pkg/smartcontract/manifest/standard/nep27.go | 4 +- pkg/smartcontract/manifest/standard/nep29.go | 4 +- pkg/smartcontract/manifest/standard/nep30.go | 4 +- pkg/smartcontract/manifest/standard/nep31.go | 4 +- pkg/smartcontract/manifest/standard/token.go | 4 +- pkg/smartcontract/nef/method_token.go | 6 +- pkg/smartcontract/nef/method_token_test.go | 6 +- pkg/smartcontract/nef/nef.go | 8 +- pkg/smartcontract/nef/nef_test.go | 12 +- pkg/smartcontract/param_type.go | 14 +- pkg/smartcontract/param_type_test.go | 6 +- pkg/smartcontract/parameter.go | 6 +- pkg/smartcontract/parameter_test.go | 10 +- pkg/smartcontract/rpcbinding/binding.go | 56 +- pkg/smartcontract/scparser/context.go | 4 +- pkg/smartcontract/scparser/contract_checks.go | 10 +- .../scparser/contract_checks_test.go | 14 +- pkg/smartcontract/scparser/fuzz_test.go | 6 +- pkg/smartcontract/zkpbinding/binding.go | 20 +- pkg/storage/config.go | 212 +- pkg/storage/doc.go | 184 +- pkg/storage/errors.go | 66 +- pkg/storage/local/adapter.go | 1196 ++-- pkg/storage/local/adapter_test.go | 1208 ++-- pkg/storage/neofs/adapter.go | 310 +- pkg/storage/storage.go | 336 +- pkg/tutusrpc/errors.go | 2 +- pkg/tutusrpc/filters.go | 12 +- pkg/tutusrpc/filters_test.go | 6 +- pkg/tutusrpc/result/application_log.go | 6 +- pkg/tutusrpc/result/block.go | 4 +- pkg/tutusrpc/result/block_header.go | 2 +- pkg/tutusrpc/result/block_notifications.go | 2 +- pkg/tutusrpc/result/invoke.go | 10 +- pkg/tutusrpc/result/invoke_test.go | 12 +- pkg/tutusrpc/result/mempool_event.go | 4 +- pkg/tutusrpc/result/mpt.go | 2 +- pkg/tutusrpc/result/mpt_test.go | 8 +- pkg/tutusrpc/result/notary_request_event.go | 4 +- pkg/tutusrpc/result/peers.go | 2 +- pkg/tutusrpc/result/peers_test.go | 2 +- pkg/tutusrpc/result/raw_mempool.go | 2 +- pkg/tutusrpc/result/raw_notary_pool.go | 2 +- pkg/tutusrpc/result/relay_result.go | 2 +- pkg/tutusrpc/result/tokens.go | 2 +- pkg/tutusrpc/result/tx_raw_output.go | 4 +- pkg/tutusrpc/result/unclaimed_gas.go | 4 +- pkg/tutusrpc/result/validator.go | 2 +- pkg/tutusrpc/result/version.go | 8 +- pkg/tutusrpc/result/version_test.go | 6 +- pkg/tutusrpc/rpcevent/filter.go | 12 +- pkg/tutusrpc/rpcevent/filter_test.go | 22 +- pkg/tutusrpc/types.go | 8 +- pkg/tutusrpc/types_test.go | 8 +- pkg/tutustest/basic.go | 32 +- pkg/tutustest/chain/chain.go | 18 +- pkg/tutustest/chain/chain_test.go | 2 +- pkg/tutustest/client.go | 14 +- pkg/tutustest/compile.go | 14 +- pkg/tutustest/compile_test.go | 2 +- pkg/tutustest/coverage.go | 8 +- pkg/tutustest/crosscontract.go | 508 +- pkg/tutustest/doc.go | 2 +- pkg/tutustest/events.go | 590 +- pkg/tutustest/government.go | 474 +- pkg/tutustest/roles.go | 452 +- pkg/tutustest/signer.go | 22 +- pkg/tutustest/signer_test.go | 6 +- pkg/util/uint160.go | 2 +- pkg/util/uint160_test.go | 4 +- pkg/util/uint256.go | 2 +- pkg/util/uint256_test.go | 4 +- pkg/vm/bench_test.go | 2 +- pkg/vm/context.go | 16 +- pkg/vm/debug_test.go | 6 +- pkg/vm/emit/emit.go | 14 +- pkg/vm/emit/emit_test.go | 12 +- pkg/vm/exception.go | 2 +- pkg/vm/fuzz_test.go | 4 +- pkg/vm/invocation_tree_test.go | 6 +- pkg/vm/invocations/invocation_tree.go | 2 +- pkg/vm/iterator_test.go | 10 +- pkg/vm/json_test.go | 10 +- pkg/vm/opcodebench_test.go | 4 +- pkg/vm/ref_counter.go | 2 +- pkg/vm/ref_counter_test.go | 4 +- pkg/vm/slot.go | 2 +- pkg/vm/slot_test.go | 2 +- pkg/vm/stack.go | 2 +- pkg/vm/stack_test.go | 2 +- pkg/vm/stackitem/item.go | 6 +- pkg/vm/stackitem/item_test.go | 4 +- pkg/vm/stackitem/serialization.go | 6 +- pkg/vm/stackitem/serialization_test.go | 2 +- pkg/vm/vm.go | 30 +- pkg/vm/vm_test.go | 22 +- pkg/wallet/account.go | 20 +- pkg/wallet/account_test.go | 12 +- pkg/wallet/regenerate_test.go | 10 +- pkg/wallet/token.go | 4 +- pkg/wallet/token_test.go | 4 +- pkg/wallet/wallet.go | 8 +- pkg/wallet/wallet_test.go | 6 +- scripts/compare-dumps/compare-dumps_test.go | 6 +- scripts/compare-states/compare-states.go | 4 +- scripts/gendump/main.go | 26 +- scripts/go.mod | 6 +- scripts/go.sum | 4 +- scripts/register-test-vita.sh | 79 + 819 files changed, 30909 insertions(+), 30830 deletions(-) create mode 100644 scripts/register-test-vita.sh diff --git a/cli/app/app.go b/cli/app/app.go index 0268613..844dcef 100644 --- a/cli/app/app.go +++ b/cli/app/app.go @@ -5,13 +5,13 @@ import ( "os" "runtime" - "github.com/tutus-one/tutus-chain/cli/query" - "github.com/tutus-one/tutus-chain/cli/server" - "github.com/tutus-one/tutus-chain/cli/smartcontract" - "github.com/tutus-one/tutus-chain/cli/util" - "github.com/tutus-one/tutus-chain/cli/vm" - "github.com/tutus-one/tutus-chain/cli/wallet" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/cli/query" + "git.marketally.com/tutus-one/tutus-chain/cli/server" + "git.marketally.com/tutus-one/tutus-chain/cli/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/cli/util" + "git.marketally.com/tutus-one/tutus-chain/cli/vm" + "git.marketally.com/tutus-one/tutus-chain/cli/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "github.com/urfave/cli/v2" ) diff --git a/cli/app/main_test.go b/cli/app/main_test.go index 7d6461e..966410a 100644 --- a/cli/app/main_test.go +++ b/cli/app/main_test.go @@ -3,9 +3,9 @@ package app_test import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/internal/versionutil" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/internal/versionutil" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" ) func TestCLIVersion(t *testing.T) { diff --git a/cli/cmdargs/parser.go b/cli/cmdargs/parser.go index c1fbd59..ff5c481 100644 --- a/cli/cmdargs/parser.go +++ b/cli/cmdargs/parser.go @@ -5,13 +5,13 @@ import ( "fmt" "strings" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" ) diff --git a/cli/cmdargs/parser_test.go b/cli/cmdargs/parser_test.go index c2a676c..39a053f 100644 --- a/cli/cmdargs/parser_test.go +++ b/cli/cmdargs/parser_test.go @@ -4,11 +4,11 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/cli/flags/address.go b/cli/flags/address.go index 03f24bf..8a1f43a 100644 --- a/cli/flags/address.go +++ b/cli/flags/address.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/urfave/cli/v2" ) diff --git a/cli/flags/address_test.go b/cli/flags/address_test.go index 361737a..4527a91 100644 --- a/cli/flags/address_test.go +++ b/cli/flags/address_test.go @@ -5,9 +5,9 @@ import ( "io" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) diff --git a/cli/flags/fixed8.go b/cli/flags/fixed8.go index c2da7c2..14895a9 100644 --- a/cli/flags/fixed8.go +++ b/cli/flags/fixed8.go @@ -4,7 +4,7 @@ import ( "flag" "strings" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" "github.com/urfave/cli/v2" ) diff --git a/cli/flags/fixed8_test.go b/cli/flags/fixed8_test.go index 5b08cdd..4806e3d 100644 --- a/cli/flags/fixed8_test.go +++ b/cli/flags/fixed8_test.go @@ -5,7 +5,7 @@ import ( "io" "testing" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) diff --git a/cli/input/input.go b/cli/input/input.go index 31708d3..1d8e668 100644 --- a/cli/input/input.go +++ b/cli/input/input.go @@ -7,8 +7,8 @@ import ( "os" "syscall" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" "golang.org/x/term" ) diff --git a/cli/main.go b/cli/main.go index 4b63f69..1abd712 100644 --- a/cli/main.go +++ b/cli/main.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/tutus-one/tutus-chain/cli/app" + "git.marketally.com/tutus-one/tutus-chain/cli/app" ) func main() { diff --git a/cli/nep_test/nep11_test.go b/cli/nep_test/nep11_test.go index 8bfc2e1..8753fc3 100644 --- a/cli/nep_test/nep11_test.go +++ b/cli/nep_test/nep11_test.go @@ -14,15 +14,15 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/internal/versionutil" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/internal/versionutil" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/cli/nep_test/nep17_test.go b/cli/nep_test/nep17_test.go index 371aa4d..f1a3d39 100644 --- a/cli/nep_test/nep17_test.go +++ b/cli/nep_test/nep17_test.go @@ -10,13 +10,13 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/cli/options/cli_options_test.go b/cli/options/cli_options_test.go index 66988fe..50a1964 100644 --- a/cli/options/cli_options_test.go +++ b/cli/options/cli_options_test.go @@ -4,9 +4,9 @@ import ( "flag" "testing" - "github.com/tutus-one/tutus-chain/cli/app" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/cli/app" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) diff --git a/cli/options/options.go b/cli/options/options.go index da425d0..c67b1e5 100644 --- a/cli/options/options.go +++ b/cli/options/options.go @@ -15,20 +15,20 @@ import ( "strings" "time" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/input" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/input" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/nspcc-dev/neofs-sdk-go/pool" "github.com/nspcc-dev/neofs-sdk-go/user" "github.com/urfave/cli/v2" diff --git a/cli/options/options_test.go b/cli/options/options_test.go index c036c2e..aff9432 100644 --- a/cli/options/options_test.go +++ b/cli/options/options_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" diff --git a/cli/paramcontext/context.go b/cli/paramcontext/context.go index a95a81e..b4ceea1 100644 --- a/cli/paramcontext/context.go +++ b/cli/paramcontext/context.go @@ -5,10 +5,10 @@ import ( "fmt" "os" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/context" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/context" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) // InitAndSave creates an incompletely signed transaction which can be used diff --git a/cli/query/query.go b/cli/query/query.go index 9e6bc29..da1cf24 100644 --- a/cli/query/query.go +++ b/cli/query/query.go @@ -10,19 +10,19 @@ import ( "strings" "text/tabwriter" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/urfave/cli/v2" ) diff --git a/cli/query/query_test.go b/cli/query/query_test.go index 62e312b..a970418 100644 --- a/cli/query/query_test.go +++ b/cli/query/query_test.go @@ -11,25 +11,25 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/notary" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/gas" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/cli/server/cli_dump_test.go b/cli/server/cli_dump_test.go index 6b14af1..e3ee436 100644 --- a/cli/server/cli_dump_test.go +++ b/cli/server/cli_dump_test.go @@ -6,9 +6,9 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/cli/server/cli_server_test.go b/cli/server/cli_server_test.go index bcd2d99..98ed994 100644 --- a/cli/server/cli_server_test.go +++ b/cli/server/cli_server_test.go @@ -10,10 +10,10 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/cli/server" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/cli/server" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/cli/server/dump.go b/cli/server/dump.go index f54c2aa..a23ebe6 100644 --- a/cli/server/dump.go +++ b/cli/server/dump.go @@ -6,8 +6,8 @@ import ( "os" "path/filepath" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dboper" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dboper" ) type dump []blockDump diff --git a/cli/server/dump_bin.go b/cli/server/dump_bin.go index 1495d97..8511611 100644 --- a/cli/server/dump_bin.go +++ b/cli/server/dump_bin.go @@ -5,10 +5,10 @@ import ( "os" "path/filepath" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/urfave/cli/v2" ) diff --git a/cli/server/dump_bin_test.go b/cli/server/dump_bin_test.go index 793e32c..e791347 100644 --- a/cli/server/dump_bin_test.go +++ b/cli/server/dump_bin_test.go @@ -6,9 +6,9 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/cli/server/server.go b/cli/server/server.go index a96a389..6aa7571 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -12,24 +12,24 @@ import ( "slices" "syscall" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/consensus" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/chaindump" - corestate "github.com/tutus-one/tutus-chain/pkg/core/stateroot" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network" - "github.com/tutus-one/tutus-chain/pkg/services/metrics" - "github.com/tutus-one/tutus-chain/pkg/services/notary" - "github.com/tutus-one/tutus-chain/pkg/services/oracle" - "github.com/tutus-one/tutus-chain/pkg/services/rpcsrv" - "github.com/tutus-one/tutus-chain/pkg/services/stateroot" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/chaindump" + corestate "git.marketally.com/tutus-one/tutus-chain/pkg/core/stateroot" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/metrics" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/rpcsrv" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/stateroot" "github.com/urfave/cli/v2" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/cli/server/server_test.go b/cli/server/server_test.go index 08dedb3..54af30c 100644 --- a/cli/server/server_test.go +++ b/cli/server/server_test.go @@ -10,10 +10,10 @@ import ( "go.uber.org/zap/zapcore" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" diff --git a/cli/smartcontract/contract_test.go b/cli/smartcontract/contract_test.go index 7c2e6d9..e09868f 100644 --- a/cli/smartcontract/contract_test.go +++ b/cli/smartcontract/contract_test.go @@ -12,26 +12,26 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/cli/smartcontract" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/internal/versionutil" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/internal/versionutil" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) @@ -117,7 +117,7 @@ func TestContractBindings(t *testing.T) { srcPath := filepath.Join(ctrPath, "main.go") require.NoError(t, os.WriteFile(srcPath, []byte(`package testcontract import( - alias "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + alias "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" ) type MyPair struct { Key int @@ -174,9 +174,9 @@ func Blocks() []*alias.Block { package testcontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" "myimport.com/testcontract" ) @@ -227,7 +227,7 @@ func updateGoMod(dir, moduleName, neoGoPath string) error { } replacementPath := filepath.Join(wd, neoGoPath) - updatedData = append(updatedData, "\nreplace github.com/tutus-one/tutus-chain/pkg/interop => "+replacementPath+" \n"...) + updatedData = append(updatedData, "\nreplace git.marketally.com/tutus-one/tutus-chain/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) @@ -250,7 +250,7 @@ func TestDynamicWrapper(t *testing.T) { helperContract := `package testcontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" verify "myimport.com/testcontract/bindings" ) diff --git a/cli/smartcontract/generate.go b/cli/smartcontract/generate.go index ee26ead..1411765 100644 --- a/cli/smartcontract/generate.go +++ b/cli/smartcontract/generate.go @@ -6,10 +6,10 @@ import ( "os" "strings" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/rpcbinding" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/rpcbinding" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" ) diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index 289c0b3..6605463 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -9,10 +9,10 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) @@ -157,12 +157,12 @@ package wrapper import ( "github.com/heyitsme/mycontract" - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash contains contract hash in big-endian form. @@ -245,11 +245,11 @@ package wrapper import ( "github.com/heyitsme/mycontract" - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) // Contract represents the MyContract smart contract. @@ -364,8 +364,8 @@ func TestGenerateValidPackageName(t *testing.T) { package myspacecontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash contains contract hash in big-endian form. @@ -390,9 +390,9 @@ func Get() int { package myspacecontract import ( - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "math/big" ) diff --git a/cli/smartcontract/manifest.go b/cli/smartcontract/manifest.go index ab67fa0..82caa12 100644 --- a/cli/smartcontract/manifest.go +++ b/cli/smartcontract/manifest.go @@ -5,13 +5,13 @@ import ( "fmt" "os" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/urfave/cli/v2" ) diff --git a/cli/smartcontract/permission.go b/cli/smartcontract/permission.go index 61c033e..b4bb9cf 100644 --- a/cli/smartcontract/permission.go +++ b/cli/smartcontract/permission.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "gopkg.in/yaml.v3" ) diff --git a/cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go index 5f6f44a..48ca036 100644 --- a/cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/nft-d/dynamic_hash/rpcbindings_test.go @@ -4,11 +4,11 @@ package nft import ( - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // NEP22Contract is an alias for nep22.Contract. diff --git a/cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go b/cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go index 13ed5c5..092c035 100644 --- a/cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/nft-d/rpcbindings_test.go @@ -4,11 +4,11 @@ package nft import ( - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Hash contains contract 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 index 2b7d15e..2b338ae 100644 --- a/cli/smartcontract/rpcbindings/nft-nd/dynamic_hash/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/nft-nd/dynamic_hash/rpcbindings_test.go @@ -6,14 +6,14 @@ package nft import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" ) diff --git a/cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go b/cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go index 380ad99..c54da44 100644 --- a/cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/nft-nd/rpcbindings_test.go @@ -6,14 +6,14 @@ package nft import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep22" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep31" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" ) diff --git a/cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go b/cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go index ae3407f..70b2b12 100644 --- a/cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/notifications/rpcbindings/extended/rpcbindings_test.go @@ -6,11 +6,11 @@ package structs import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" "unicode/utf8" ) diff --git a/cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go b/cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go index 69a90b3..0a46d81 100644 --- a/cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/notifications/rpcbindings/guessed/rpcbindings_test.go @@ -6,11 +6,11 @@ package structs import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" "unicode/utf8" ) diff --git a/cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go index 5eae3bb..a3cc291 100644 --- a/cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/notifications/rpcbindings/rpcbindings_test.go @@ -6,10 +6,10 @@ package structs import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "unicode/utf8" ) diff --git a/cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go index 0c24cbe..5014c72 100644 --- a/cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/royalty/rpcbindings/dynamic_hash/rpcbindings_test.go @@ -4,8 +4,8 @@ package royalty import ( - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "math/big" ) diff --git a/cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go index fe92ed6..3ba8b9f 100644 --- a/cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/royalty/rpcbindings/rpcbindings_test.go @@ -4,8 +4,8 @@ package royalty import ( - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "math/big" ) diff --git a/cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go index 6b569fe..53d3f34 100644 --- a/cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/structs/rpcbindings/dynamic_hash/rpcbindings_test.go @@ -7,12 +7,12 @@ import ( "crypto/elliptic" "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" "unicode/utf8" ) diff --git a/cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go index 29e7b40..4757057 100644 --- a/cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/structs/rpcbindings/rpcbindings_test.go @@ -7,12 +7,12 @@ import ( "crypto/elliptic" "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" "unicode/utf8" ) diff --git a/cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go b/cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go index 78793be..4c3523e 100644 --- a/cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/types/rpcbindings/dynamic_hash/rpcbindings_test.go @@ -6,12 +6,12 @@ package types import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" "unicode/utf8" ) diff --git a/cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go b/cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go index 98899dd..25bfb1d 100644 --- a/cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/types/rpcbindings/rpcbindings_test.go @@ -6,12 +6,12 @@ package types import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" "unicode/utf8" ) diff --git a/cli/smartcontract/rpcbindings/verify/rpcbindings_test.go b/cli/smartcontract/rpcbindings/verify/rpcbindings_test.go index 3fcd881..962bac3 100644 --- a/cli/smartcontract/rpcbindings/verify/rpcbindings_test.go +++ b/cli/smartcontract/rpcbindings/verify/rpcbindings_test.go @@ -6,11 +6,11 @@ package verify import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Hash contains contract hash. diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 78baac5..5686875 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -9,24 +9,24 @@ import ( "path/filepath" "strings" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/management" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" ) @@ -72,7 +72,7 @@ const ( // %s is parsed to be the smartContractName. smartContractTmpl = `package %s -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" var notificationName string @@ -452,7 +452,7 @@ func initSmartContract(ctx *cli.Context) error { go 1.24 require ( - github.com/tutus-one/tutus-chain/pkg/interop ` + ver + ` + git.marketally.com/tutus-one/tutus-chain/pkg/interop ` + ver + ` )`) if err := os.WriteFile(filepath.Join(basePath, "go.mod"), gm, 0644); err != nil { return cli.Exit(err, 1) diff --git a/cli/smartcontract/smart_contract_test.go b/cli/smartcontract/smart_contract_test.go index 1512987..13a82ec 100644 --- a/cli/smartcontract/smart_contract_test.go +++ b/cli/smartcontract/smart_contract_test.go @@ -5,9 +5,9 @@ import ( "os" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" @@ -40,7 +40,7 @@ func TestInitSmartContract(t *testing.T) { require.Equal(t, `package `+contractName+` -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" var notificationName string diff --git a/cli/smartcontract/testdata/deploy/main.go b/cli/smartcontract/testdata/deploy/main.go index ab9316a..4807c93 100644 --- a/cli/smartcontract/testdata/deploy/main.go +++ b/cli/smartcontract/testdata/deploy/main.go @@ -1,12 +1,12 @@ package deploy import ( - "github.com/tutus-one/tutus-chain/cli/smartcontract/testdata/deploy/sub" - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/cli/smartcontract/testdata/deploy/sub" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) var key = "key" diff --git a/cli/smartcontract/testdata/deploy/sub/put.go b/cli/smartcontract/testdata/deploy/sub/put.go index 2359d01..ef09dd2 100644 --- a/cli/smartcontract/testdata/deploy/sub/put.go +++ b/cli/smartcontract/testdata/deploy/sub/put.go @@ -1,6 +1,6 @@ package sub -import "github.com/tutus-one/tutus-chain/pkg/interop/storage" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" var Key = "sub" diff --git a/cli/smartcontract/testdata/gas/gas.go b/cli/smartcontract/testdata/gas/gas.go index 3902135..710f17f 100644 --- a/cli/smartcontract/testdata/gas/gas.go +++ b/cli/smartcontract/testdata/gas/gas.go @@ -4,8 +4,8 @@ package lub import ( - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Hash contains contract hash. diff --git a/cli/smartcontract/testdata/invalid1/invalid.go b/cli/smartcontract/testdata/invalid1/invalid.go index d09a535..273617e 100644 --- a/cli/smartcontract/testdata/invalid1/invalid.go +++ b/cli/smartcontract/testdata/invalid1/invalid.go @@ -2,8 +2,8 @@ package invalid1 import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // Notify1 emits a correctly typed event. diff --git a/cli/smartcontract/testdata/invalid2/invalid.go b/cli/smartcontract/testdata/invalid2/invalid.go index 31d5f17..2794ab1 100644 --- a/cli/smartcontract/testdata/invalid2/invalid.go +++ b/cli/smartcontract/testdata/invalid2/invalid.go @@ -2,8 +2,8 @@ package invalid2 import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // Notify1 emits a correctly typed event. diff --git a/cli/smartcontract/testdata/invalid3/invalid.go b/cli/smartcontract/testdata/invalid3/invalid.go index 21b2e5b..8aae59f 100644 --- a/cli/smartcontract/testdata/invalid3/invalid.go +++ b/cli/smartcontract/testdata/invalid3/invalid.go @@ -2,8 +2,8 @@ package invalid3 import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // Notify1 emits a correctly typed event. diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index 671c35e..7bd2f3b 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -7,13 +7,13 @@ import ( "errors" "fmt" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" "unicode/utf8" ) diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index 529973f..52ff489 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -6,13 +6,13 @@ package nextoken import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "math/big" ) diff --git a/cli/smartcontract/testdata/nonepiter/iter.go b/cli/smartcontract/testdata/nonepiter/iter.go index ab9835c..bf8bb49 100644 --- a/cli/smartcontract/testdata/nonepiter/iter.go +++ b/cli/smartcontract/testdata/nonepiter/iter.go @@ -5,10 +5,10 @@ package nonnepxxcontractwithiterators import ( "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Hash contains contract hash. diff --git a/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go index d5b24f0..1405020 100644 --- a/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go +++ b/cli/smartcontract/testdata/rpcbindings/invalid1/invalid.go @@ -1,6 +1,6 @@ package invalid1 -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() { runtime.Notify("Non declared event") diff --git a/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go index 483203b..e1da0ca 100644 --- a/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go +++ b/cli/smartcontract/testdata/rpcbindings/invalid2/invalid.go @@ -1,6 +1,6 @@ package invalid2 -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() { runtime.Notify("SomeEvent", "p1", "p2") diff --git a/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go index 24e5c1a..f390cbd 100644 --- a/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go +++ b/cli/smartcontract/testdata/rpcbindings/invalid3/invalid.go @@ -1,6 +1,6 @@ package invalid3 -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() { runtime.Notify("SomeEvent", "p1", 5) diff --git a/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go index aa8cb1a..e7b86a1 100644 --- a/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go +++ b/cli/smartcontract/testdata/rpcbindings/invalid4/invalid.go @@ -1,6 +1,6 @@ package invalid4 -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" type SomeStruct1 struct { Field1 int diff --git a/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go index e15fe58..b23bb66 100644 --- a/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go +++ b/cli/smartcontract/testdata/rpcbindings/invalid5/invalid.go @@ -1,6 +1,6 @@ package invalid5 -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" type NamedStruct struct { SomeInt int diff --git a/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go index 4a8c541..01d3955 100644 --- a/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go +++ b/cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go @@ -1,6 +1,6 @@ package invalid6 -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" type SomeStruct struct { Field int diff --git a/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go b/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go index 7d19838..8e02891 100644 --- a/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go +++ b/cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go @@ -1,6 +1,6 @@ package invalid7 -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" type SomeStruct struct { Field int diff --git a/cli/smartcontract/testdata/rpcbindings/notifications/notifications.go b/cli/smartcontract/testdata/rpcbindings/notifications/notifications.go index 54cb215..8035880 100644 --- a/cli/smartcontract/testdata/rpcbindings/notifications/notifications.go +++ b/cli/smartcontract/testdata/rpcbindings/notifications/notifications.go @@ -1,8 +1,8 @@ package structs import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) func Main() { diff --git a/cli/smartcontract/testdata/rpcbindings/royalty/royalty.go b/cli/smartcontract/testdata/rpcbindings/royalty/royalty.go index 2e8c88c..6febf98 100644 --- a/cli/smartcontract/testdata/rpcbindings/royalty/royalty.go +++ b/cli/smartcontract/testdata/rpcbindings/royalty/royalty.go @@ -1,9 +1,9 @@ package royalty import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // RoyaltiesTransferred notifies about royalty payment. This method is called by marketplace diff --git a/cli/smartcontract/testdata/rpcbindings/structs/structs.go b/cli/smartcontract/testdata/rpcbindings/structs/structs.go index abbc226..9fcc49f 100644 --- a/cli/smartcontract/testdata/rpcbindings/structs/structs.go +++ b/cli/smartcontract/testdata/rpcbindings/structs/structs.go @@ -1,9 +1,9 @@ package structs import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" ) type Internal struct { diff --git a/cli/smartcontract/testdata/rpcbindings/types/types.go b/cli/smartcontract/testdata/rpcbindings/types/types.go index 9122467..b39be20 100644 --- a/cli/smartcontract/testdata/rpcbindings/types/types.go +++ b/cli/smartcontract/testdata/rpcbindings/types/types.go @@ -1,7 +1,7 @@ package types import ( - "github.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" ) func Bool(b bool) bool { diff --git a/cli/smartcontract/testdata/verify.go b/cli/smartcontract/testdata/verify.go index e332d9e..fe33d04 100644 --- a/cli/smartcontract/testdata/verify.go +++ b/cli/smartcontract/testdata/verify.go @@ -1,8 +1,8 @@ package testdata import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) func Verify() bool { diff --git a/cli/txctx/tx.go b/cli/txctx/tx.go index e93c055..5863c58 100644 --- a/cli/txctx/tx.go +++ b/cli/txctx/tx.go @@ -8,14 +8,14 @@ import ( "io" "time" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/input" - "github.com/tutus-one/tutus-chain/cli/paramcontext" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/input" + "git.marketally.com/tutus-one/tutus-chain/cli/paramcontext" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" ) diff --git a/cli/util/audit-bin.go b/cli/util/audit-bin.go index 224a794..741bda6 100644 --- a/cli/util/audit-bin.go +++ b/cli/util/audit-bin.go @@ -7,12 +7,12 @@ import ( "sync" "time" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/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" diff --git a/cli/util/cancel.go b/cli/util/cancel.go index d246d30..08b77a1 100644 --- a/cli/util/cancel.go +++ b/cli/util/cancel.go @@ -5,17 +5,17 @@ import ( "fmt" "strings" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/urfave/cli/v2" ) diff --git a/cli/util/convert.go b/cli/util/convert.go index 748d226..3ff5520 100644 --- a/cli/util/convert.go +++ b/cli/util/convert.go @@ -6,13 +6,13 @@ import ( "fmt" "os" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/txctx" - vmcli "github.com/tutus-one/tutus-chain/cli/vm" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" - "github.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + vmcli "git.marketally.com/tutus-one/tutus-chain/cli/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" "github.com/urfave/cli/v2" ) diff --git a/cli/util/dump.go b/cli/util/dump.go index 2567b84..d4c3b8a 100644 --- a/cli/util/dump.go +++ b/cli/util/dump.go @@ -4,10 +4,10 @@ import ( "encoding/json" "fmt" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/paramcontext" - "github.com/tutus-one/tutus-chain/cli/query" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/paramcontext" + "git.marketally.com/tutus-one/tutus-chain/cli/query" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" "github.com/urfave/cli/v2" ) diff --git a/cli/util/send.go b/cli/util/send.go index 1b765e6..680451c 100644 --- a/cli/util/send.go +++ b/cli/util/send.go @@ -3,11 +3,11 @@ package util import ( "fmt" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/paramcontext" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/paramcontext" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" "github.com/urfave/cli/v2" ) diff --git a/cli/util/upload_bin.go b/cli/util/upload_bin.go index 71e3337..5645e4f 100644 --- a/cli/util/upload_bin.go +++ b/cli/util/upload_bin.go @@ -7,12 +7,12 @@ import ( "sync" "time" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" "github.com/nspcc-dev/neofs-sdk-go/client" "github.com/nspcc-dev/neofs-sdk-go/container" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" diff --git a/cli/util/upload_state.go b/cli/util/upload_state.go index 8ca529a..75b6331 100644 --- a/cli/util/upload_state.go +++ b/cli/util/upload_state.go @@ -5,14 +5,14 @@ import ( "strconv" "time" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/server" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core" - gio "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/server" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + gio "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/nspcc-dev/neofs-sdk-go/client" "github.com/nspcc-dev/neofs-sdk-go/object" "github.com/urfave/cli/v2" diff --git a/cli/util/util_test.go b/cli/util/util_test.go index 2499490..cc2f40c 100644 --- a/cli/util/util_test.go +++ b/cli/util/util_test.go @@ -8,11 +8,11 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/cli/vm/cli.go b/cli/vm/cli.go index 6ec5106..8c5017f 100644 --- a/cli/vm/cli.go +++ b/cli/vm/cli.go @@ -19,31 +19,31 @@ import ( "github.com/chzyer/readline" "github.com/kballard/go-shellquote" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/paramcontext" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/paramcontext" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/urfave/cli/v2" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 398fc2a..e89774a 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -19,29 +19,29 @@ import ( "time" "github.com/chzyer/readline" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/paramcontext" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/versionutil" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dboper" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/paramcontext" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/versionutil" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dboper" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -374,9 +374,9 @@ func prepareLoadgoSrc(t *testing.T, tmpDir, src string) string { require.NoError(t, err) goMod := []byte(`module test.example/kek require ( - github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 + git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 ) -replace github.com/tutus-one/tutus-chain/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + ` +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + ` go 1.24`) require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm)) return filename @@ -532,7 +532,7 @@ func TestLoad(t *testing.T) { t.Run("check calling flags", func(t *testing.T) { srcAllowNotify := `package kek - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() int { runtime.Log("Hello, world!") return 1 @@ -550,8 +550,8 @@ func TestLoad(t *testing.T) { t.Run("check signers", func(t *testing.T) { srcCheckWitness := `package kek import ( - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" ) func Main() bool { var owner = address.ToHash160("` + ownerAddress + `") @@ -654,7 +654,7 @@ func TestLoad(t *testing.T) { func TestLoad_RunWithCALLT(t *testing.T) { // Our smart compiler will generate CALLT instruction for the following StdLib call: src := `package kek - import "github.com/tutus-one/tutus-chain/pkg/interop/native/std" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" func Main() int { return std.Atoi("123", 10) }` diff --git a/cli/vm/vm.go b/cli/vm/vm.go index f1bf7fd..805923f 100644 --- a/cli/vm/vm.go +++ b/cli/vm/vm.go @@ -5,9 +5,9 @@ import ( "os" "github.com/chzyer/readline" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/urfave/cli/v2" ) diff --git a/cli/wallet/candidate_test.go b/cli/wallet/candidate_test.go index 81b71c5..2d9142a 100644 --- a/cli/wallet/candidate_test.go +++ b/cli/wallet/candidate_test.go @@ -5,8 +5,8 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "github.com/stretchr/testify/require" ) diff --git a/cli/wallet/legacy.go b/cli/wallet/legacy.go index 697f33a..59f565b 100644 --- a/cli/wallet/legacy.go +++ b/cli/wallet/legacy.go @@ -8,11 +8,11 @@ import ( "errors" "os" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) type ( diff --git a/cli/wallet/legacy_test.go b/cli/wallet/legacy_test.go index f5a3e73..9d955db 100644 --- a/cli/wallet/legacy_test.go +++ b/cli/wallet/legacy_test.go @@ -6,7 +6,7 @@ import ( "encoding/hex" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" "github.com/stretchr/testify/require" ) diff --git a/cli/wallet/multisig.go b/cli/wallet/multisig.go index 3aae900..02d0655 100644 --- a/cli/wallet/multisig.go +++ b/cli/wallet/multisig.go @@ -4,14 +4,14 @@ import ( "encoding/json" "fmt" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/paramcontext" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/paramcontext" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" "github.com/urfave/cli/v2" ) diff --git a/cli/wallet/multisig_test.go b/cli/wallet/multisig_test.go index 4db49b8..87cfebb 100644 --- a/cli/wallet/multisig_test.go +++ b/cli/wallet/multisig_test.go @@ -8,15 +8,15 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/context" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/context" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) diff --git a/cli/wallet/nep11.go b/cli/wallet/nep11.go index 60a2b68..704b341 100644 --- a/cli/wallet/nep11.go +++ b/cli/wallet/nep11.go @@ -7,19 +7,19 @@ import ( "slices" "strconv" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" ) diff --git a/cli/wallet/nep17.go b/cli/wallet/nep17.go index 2f73d60..3acbfba 100644 --- a/cli/wallet/nep17.go +++ b/cli/wallet/nep17.go @@ -8,26 +8,26 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/gas" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" ) diff --git a/cli/wallet/validator.go b/cli/wallet/validator.go index 3133f43..0635f11 100644 --- a/cli/wallet/validator.go +++ b/cli/wallet/validator.go @@ -4,18 +4,18 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/gas" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" ) diff --git a/cli/wallet/wallet.go b/cli/wallet/wallet.go index a322278..2b59ae5 100644 --- a/cli/wallet/wallet.go +++ b/cli/wallet/wallet.go @@ -11,20 +11,20 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/cli/cmdargs" - "github.com/tutus-one/tutus-chain/cli/flags" - "github.com/tutus-one/tutus-chain/cli/input" - "github.com/tutus-one/tutus-chain/cli/options" - "github.com/tutus-one/tutus-chain/cli/txctx" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/cli/cmdargs" + "git.marketally.com/tutus-one/tutus-chain/cli/flags" + "git.marketally.com/tutus-one/tutus-chain/cli/input" + "git.marketally.com/tutus-one/tutus-chain/cli/options" + "git.marketally.com/tutus-one/tutus-chain/cli/txctx" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/urfave/cli/v2" ) @@ -1050,7 +1050,7 @@ func newAccountFromWIF(w io.Writer, wif string, scrypt keys.ScryptParams, label acc, err := wallet.NewAccountFromEncryptedWIF(wif, phrase, scrypt) if err != nil { // If password from wallet config wasn't OK then retry with the user input, - // see the https://github.com/tutus-one/tutus-chain/issues/2883#issuecomment-1399923088. + // see the https://git.marketally.com/tutus-one/tutus-chain/issues/2883#issuecomment-1399923088. if pass == nil { return nil, err } diff --git a/cli/wallet/wallet_test.go b/cli/wallet/wallet_test.go index d2b85e7..db86b4d 100644 --- a/cli/wallet/wallet_test.go +++ b/cli/wallet/wallet_test.go @@ -9,14 +9,14 @@ import ( "testing" "github.com/chzyer/readline" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/examples/engine/engine.go b/examples/engine/engine.go index b33d072..38e1066 100644 --- a/examples/engine/engine.go +++ b/examples/engine/engine.go @@ -1,7 +1,7 @@ package enginecontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // NotifyScriptContainer sends runtime notification with script container hash. diff --git a/examples/engine/go.mod b/examples/engine/go.mod index b5c8594..9e9f47a 100644 --- a/examples/engine/go.mod +++ b/examples/engine/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/engine +module git.marketally.com/tutus-one/tutus-chain/examples/engine go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/engine/go.sum b/examples/engine/go.sum index a6aeabe..f1b4892 100644 --- a/examples/engine/go.sum +++ b/examples/engine/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/events/events.go b/examples/events/events.go index f6d3bf3..cede577 100644 --- a/examples/events/events.go +++ b/examples/events/events.go @@ -1,8 +1,8 @@ package events import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // NotifySomeBytes emits notification with ByteArray. diff --git a/examples/events/go.mod b/examples/events/go.mod index e7ca658..90670f0 100644 --- a/examples/events/go.mod +++ b/examples/events/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/events +module git.marketally.com/tutus-one/tutus-chain/examples/events go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/events/go.sum b/examples/events/go.sum index a6aeabe..f1b4892 100644 --- a/examples/events/go.sum +++ b/examples/events/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/iterator/go.mod b/examples/iterator/go.mod index 27ef82e..c02af8b 100644 --- a/examples/iterator/go.mod +++ b/examples/iterator/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/iterator +module git.marketally.com/tutus-one/tutus-chain/examples/iterator go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/iterator/go.sum b/examples/iterator/go.sum index a6aeabe..f1b4892 100644 --- a/examples/iterator/go.sum +++ b/examples/iterator/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/iterator/iterator.go b/examples/iterator/iterator.go index 9767c77..88347e8 100644 --- a/examples/iterator/iterator.go +++ b/examples/iterator/iterator.go @@ -1,9 +1,9 @@ package iteratorcontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) // _deploy primes contract's storage with some data to be used later. diff --git a/examples/neofs/go.mod b/examples/neofs/go.mod index 141efbb..fd404d8 100644 --- a/examples/neofs/go.mod +++ b/examples/neofs/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/neofs +module git.marketally.com/tutus-one/tutus-chain/examples/neofs go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/neofs/go.sum b/examples/neofs/go.sum index a6aeabe..f1b4892 100644 --- a/examples/neofs/go.sum +++ b/examples/neofs/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/neofs/neofs.go b/examples/neofs/neofs.go index 5cef498..9b0083a 100644 --- a/examples/neofs/neofs.go +++ b/examples/neofs/neofs.go @@ -1,11 +1,11 @@ package neofs import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/native/oracle" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) const ( diff --git a/examples/nft-d/go.mod b/examples/nft-d/go.mod index a085314..af004da 100644 --- a/examples/nft-d/go.mod +++ b/examples/nft-d/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/nft +module git.marketally.com/tutus-one/tutus-chain/examples/nft go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/nft-d/go.sum b/examples/nft-d/go.sum index a6aeabe..f1b4892 100644 --- a/examples/nft-d/go.sum +++ b/examples/nft-d/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/nft-d/nft.go b/examples/nft-d/nft.go index aeaecab..173ed76 100644 --- a/examples/nft-d/nft.go +++ b/examples/nft-d/nft.go @@ -7,17 +7,17 @@ produce NFT which represents NeoFS object. package nft import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" - "github.com/tutus-one/tutus-chain/pkg/interop/native/crypto" - "github.com/tutus-one/tutus-chain/pkg/interop/native/lub" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/lub" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) const ( diff --git a/examples/nft-nd-nns/go.mod b/examples/nft-nd-nns/go.mod index fc579ca..9d6c1b7 100644 --- a/examples/nft-nd-nns/go.mod +++ b/examples/nft-nd-nns/go.mod @@ -1,16 +1,16 @@ -module github.com/tutus-one/tutus-chain/examples/nft-nd-nns +module git.marketally.com/tutus-one/tutus-chain/examples/nft-nd-nns go 1.24.0 require ( - github.com/tutus-one/tutus-chain v0.0.0 - github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 + git.marketally.com/tutus-one/tutus-chain v0.0.0 + git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 github.com/stretchr/testify v1.11.1 ) -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect diff --git a/examples/nft-nd-nns/go.sum b/examples/nft-nd-nns/go.sum index 34a210b..82a4894 100644 --- a/examples/nft-nd-nns/go.sum +++ b/examples/nft-nd-nns/go.sum @@ -128,10 +128,10 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20250911084817-6fb4472993d1 h1:U3wvY github.com/nspcc-dev/go-ordered-json v0.0.0-20250911084817-6fb4472993d1/go.mod h1:CHwf1nwquA6ecSfxmNF0YuemOPHAnRGoLuZUv/WPjeY= github.com/nspcc-dev/hrw/v2 v2.0.4 h1:o3Zh/2aF+IgGpvt414f46Ya20WG9u9vWxVd16ErFI8w= github.com/nspcc-dev/hrw/v2 v2.0.4/go.mod h1:dUjOx27zTTvoPmT5EG25vSSWL2tKS7ndAa2TPTiZwFo= -github.com/tutus-one/tutus-chain v0.113.1-0.20251010141927-ac58bbb39350 h1:v3kn1A+IzUd+tSPutByivpVRP/R+vz4pYH9Se72pHMc= -github.com/tutus-one/tutus-chain v0.113.1-0.20251010141927-ac58bbb39350/go.mod h1:h63dEk1KmppwxXGi453woIhpvPf0lBmqLfWzd5TEUrs= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain v0.113.1-0.20251010141927-ac58bbb39350 h1:v3kn1A+IzUd+tSPutByivpVRP/R+vz4pYH9Se72pHMc= +git.marketally.com/tutus-one/tutus-chain v0.113.1-0.20251010141927-ac58bbb39350/go.mod h1:h63dEk1KmppwxXGi453woIhpvPf0lBmqLfWzd5TEUrs= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.15 h1:KM1uenYD6BES9pW69ZNglCKTLu5WzQCziaD361YfuW4= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.15/go.mod h1:Vukuf6qDOQESOWAx5yOjYtVC5wdsQp3hiZrxbJIa2fs= github.com/nspcc-dev/rfc6979 v0.2.4 h1:NBgsdCjhLpEPJZqmC9rciMZDcSY297po2smeaRjw57k= diff --git a/examples/nft-nd-nns/namestate.go b/examples/nft-nd-nns/namestate.go index e8fde0c..2d32fff 100644 --- a/examples/nft-nd-nns/namestate.go +++ b/examples/nft-nd-nns/namestate.go @@ -1,8 +1,8 @@ package nns import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // NameState represents domain name state. diff --git a/examples/nft-nd-nns/nns.go b/examples/nft-nd-nns/nns.go index 7ef69e6..9ac1ec9 100644 --- a/examples/nft-nd-nns/nns.go +++ b/examples/nft-nd-nns/nns.go @@ -9,15 +9,15 @@ must be added by the committee before new domain name can be registered. package nns import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/native/crypto" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/native/tutus" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) // Prefixes used for contract data storage. diff --git a/examples/nft-nd-nns/nns_test.go b/examples/nft-nd-nns/nns_test.go index b5f7a6b..089bd5d 100644 --- a/examples/nft-nd-nns/nns_test.go +++ b/examples/nft-nd-nns/nns_test.go @@ -4,13 +4,13 @@ import ( "strings" "testing" - nns "github.com/tutus-one/tutus-chain/examples/nft-nd-nns" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + nns "git.marketally.com/tutus-one/tutus-chain/examples/nft-nd-nns" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -377,7 +377,7 @@ func TestTransfer(t *testing.T) { // with onNEP11Transfer ctr = tutustest.CompileSource(t, e.CommitteeHash, strings.NewReader(`package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data any) {}`), &compiler.Options{Name: "foo"}) e.DeployContract(t, ctr, nil) diff --git a/examples/nft-nd/go.mod b/examples/nft-nd/go.mod index 41eeca1..fb46611 100644 --- a/examples/nft-nd/go.mod +++ b/examples/nft-nd/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/nft-nd +module git.marketally.com/tutus-one/tutus-chain/examples/nft-nd go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/nft-nd/go.sum b/examples/nft-nd/go.sum index a6aeabe..f1b4892 100644 --- a/examples/nft-nd/go.sum +++ b/examples/nft-nd/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/nft-nd/nft.go b/examples/nft-nd/nft.go index 1fbda5d..ed088e7 100644 --- a/examples/nft-nd/nft.go +++ b/examples/nft-nd/nft.go @@ -8,17 +8,17 @@ you own a hash it's HASHY. package nft import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" - "github.com/tutus-one/tutus-chain/pkg/interop/native/crypto" - "github.com/tutus-one/tutus-chain/pkg/interop/native/lub" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/lub" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) // Prefixes used for contract data storage. diff --git a/examples/oracle/go.mod b/examples/oracle/go.mod index 6883bfc..2527440 100644 --- a/examples/oracle/go.mod +++ b/examples/oracle/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/oracle +module git.marketally.com/tutus-one/tutus-chain/examples/oracle go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/oracle/go.sum b/examples/oracle/go.sum index a6aeabe..f1b4892 100644 --- a/examples/oracle/go.sum +++ b/examples/oracle/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/oracle/oracle.go b/examples/oracle/oracle.go index 88abb7e..0dab5fc 100644 --- a/examples/oracle/oracle.go +++ b/examples/oracle/oracle.go @@ -1,9 +1,9 @@ package oraclecontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/oracle" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // Request does an oracle request for the URL specified. It adds minimum diff --git a/examples/runtime/go.mod b/examples/runtime/go.mod index 4ce4d86..8d1cba9 100644 --- a/examples/runtime/go.mod +++ b/examples/runtime/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/runtime +module git.marketally.com/tutus-one/tutus-chain/examples/runtime go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/runtime/go.sum b/examples/runtime/go.sum index a6aeabe..f1b4892 100644 --- a/examples/runtime/go.sum +++ b/examples/runtime/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/runtime/runtime.go b/examples/runtime/runtime.go index 4193aab..346cebe 100644 --- a/examples/runtime/runtime.go +++ b/examples/runtime/runtime.go @@ -1,9 +1,9 @@ package runtimecontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) var ( diff --git a/examples/storage/go.mod b/examples/storage/go.mod index e689f44..4865088 100644 --- a/examples/storage/go.mod +++ b/examples/storage/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/storage +module git.marketally.com/tutus-one/tutus-chain/examples/storage go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/storage/go.sum b/examples/storage/go.sum index a6aeabe..f1b4892 100644 --- a/examples/storage/go.sum +++ b/examples/storage/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/storage/storage.go b/examples/storage/storage.go index 7cbed61..aac1223 100644 --- a/examples/storage/storage.go +++ b/examples/storage/storage.go @@ -1,8 +1,8 @@ package storagecontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) // ctx holds storage context for contract methods. diff --git a/examples/timer/go.mod b/examples/timer/go.mod index 3c3906e..91ce42b 100644 --- a/examples/timer/go.mod +++ b/examples/timer/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/timer +module git.marketally.com/tutus-one/tutus-chain/examples/timer go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/timer/go.sum b/examples/timer/go.sum index a6aeabe..f1b4892 100644 --- a/examples/timer/go.sum +++ b/examples/timer/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/timer/timer.go b/examples/timer/timer.go index 028762c..4fa626e 100644 --- a/examples/timer/timer.go +++ b/examples/timer/timer.go @@ -1,12 +1,12 @@ package timer import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) const defaultTicks = 3 // nolint: unused diff --git a/examples/token/go.mod b/examples/token/go.mod index ef1f044..0260b20 100644 --- a/examples/token/go.mod +++ b/examples/token/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/token +module git.marketally.com/tutus-one/tutus-chain/examples/token go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../.. +replace git.marketally.com/tutus-one/tutus-chain => ../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../pkg/interop diff --git a/examples/token/go.sum b/examples/token/go.sum index a6aeabe..f1b4892 100644 --- a/examples/token/go.sum +++ b/examples/token/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/token/nep17/nep17.go b/examples/token/nep17/nep17.go index 1c3c933..7452ede 100644 --- a/examples/token/nep17/nep17.go +++ b/examples/token/nep17/nep17.go @@ -1,11 +1,11 @@ package nep17 import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) // Token holds all token info. diff --git a/examples/token/token.go b/examples/token/token.go index 54f2bd4..083a3b7 100644 --- a/examples/token/token.go +++ b/examples/token/token.go @@ -1,10 +1,10 @@ package tokencontract import ( - "github.com/tutus-one/tutus-chain/examples/token/nep17" - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/examples/token/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) const ( diff --git a/examples/zkp/cubic_circuit/go.mod b/examples/zkp/cubic_circuit/go.mod index 5b1ed07..f909c7c 100644 --- a/examples/zkp/cubic_circuit/go.mod +++ b/examples/zkp/cubic_circuit/go.mod @@ -1,17 +1,17 @@ -module github.com/tutus-one/tutus-chain/examples/zkp/cubic +module git.marketally.com/tutus-one/tutus-chain/examples/zkp/cubic go 1.24.0 require ( github.com/consensys/gnark v0.14.0 github.com/consensys/gnark-crypto v0.19.1 - github.com/tutus-one/tutus-chain v0.0.0 + git.marketally.com/tutus-one/tutus-chain v0.0.0 github.com/stretchr/testify v1.10.0 ) -replace github.com/tutus-one/tutus-chain => ../../.. +replace git.marketally.com/tutus-one/tutus-chain => ../../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../../pkg/interop require ( github.com/beorn7/perks v1.0.1 // indirect @@ -33,7 +33,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect - github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 // indirect + git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 // indirect github.com/nspcc-dev/rfc6979 v0.2.1 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/examples/zkp/cubic_circuit/go.sum b/examples/zkp/cubic_circuit/go.sum index cddc80b..b30989b 100644 --- a/examples/zkp/cubic_circuit/go.sum +++ b/examples/zkp/cubic_circuit/go.sum @@ -81,10 +81,10 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc= github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUOU= github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA= -github.com/tutus-one/tutus-chain v0.106.3 h1:HEyhgkjQY+HfBzotMJ12xx2VuOUphkngZ4kEkjvXDtE= -github.com/tutus-one/tutus-chain v0.106.3/go.mod h1:3vEwJ2ld12N7HRGCaH/l/7EwopplC/+8XdIdPDNmD/M= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain v0.106.3 h1:HEyhgkjQY+HfBzotMJ12xx2VuOUphkngZ4kEkjvXDtE= +git.marketally.com/tutus-one/tutus-chain v0.106.3/go.mod h1:3vEwJ2ld12N7HRGCaH/l/7EwopplC/+8XdIdPDNmD/M= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no= github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 h1:mdxtlSU2I4oVZ/7AXTLKyz8uUPbDWikZw4DM8gvrddA= diff --git a/examples/zkp/cubic_circuit/main_test.go b/examples/zkp/cubic_circuit/main_test.go index c18c2f1..b2885df 100644 --- a/examples/zkp/cubic_circuit/main_test.go +++ b/examples/zkp/cubic_circuit/main_test.go @@ -16,9 +16,9 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/test" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/zkpbinding" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/zkpbinding" "github.com/stretchr/testify/require" ) diff --git a/examples/zkp/xor_compat/go.mod b/examples/zkp/xor_compat/go.mod index 9e77ae5..5e43618 100644 --- a/examples/zkp/xor_compat/go.mod +++ b/examples/zkp/xor_compat/go.mod @@ -1,9 +1,9 @@ -module github.com/tutus-one/tutus-chain/examples/zkp/xor +module git.marketally.com/tutus-one/tutus-chain/examples/zkp/xor go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0 -replace github.com/tutus-one/tutus-chain => ../../.. +replace git.marketally.com/tutus-one/tutus-chain => ../../.. -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../../pkg/interop diff --git a/examples/zkp/xor_compat/go.sum b/examples/zkp/xor_compat/go.sum index a6aeabe..f1b4892 100644 --- a/examples/zkp/xor_compat/go.sum +++ b/examples/zkp/xor_compat/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/examples/zkp/xor_compat/verify.go b/examples/zkp/xor_compat/verify.go index 358d2d9..9ec020e 100644 --- a/examples/zkp/xor_compat/verify.go +++ b/examples/zkp/xor_compat/verify.go @@ -11,13 +11,13 @@ // Please, do not use this example to create, proof and verify your own circuits. // Refer to examples/cubic_circuit package to get an example of a custom circuit // creation in Go and check out the -// [zkpbinding](https://pkg.go.dev/github.com/tutus-one/tutus-chain/pkg/smartcontract/zkpbinding) +// [zkpbinding](https://pkg.go.dev/git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/zkpbinding) // package to create your own verification contract for the circuit. package xor import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/crypto" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) // Constants needed for verification should be obtained via MPC process. diff --git a/go.mod b/go.mod index 7ba531d..91b1e00 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/tutus-one/tutus-chain +module git.marketally.com/tutus-one/tutus-chain go 1.24.0 @@ -20,11 +20,11 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/stretchr/testify v1.11.1 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 - github.com/tutus-one/tutus-bolt v1.0.0 - github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 - github.com/tutus-one/tutus-consensus v1.0.0 - github.com/tutus-one/tutus-ordered-json v1.0.0 - github.com/tutus-one/tutus-rfc6979 v1.0.0 + git.marketally.com/tutus-one/tutus-bolt v1.0.0 + git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 + git.marketally.com/tutus-one/tutus-consensus v1.0.0 + git.marketally.com/tutus-one/tutus-ordered-json v1.0.0 + git.marketally.com/tutus-one/tutus-rfc6979 v1.0.0 github.com/twmb/murmur3 v1.1.8 github.com/urfave/cli/v2 v2.27.7 go.uber.org/zap v1.27.0 @@ -74,4 +74,4 @@ require ( google.golang.org/protobuf v1.36.9 // indirect ) -replace github.com/tutus-one/tutus-chain/pkg/interop => ./pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ./pkg/interop diff --git a/go.sum b/go.sum index c64df0f..084e911 100644 --- a/go.sum +++ b/go.sum @@ -209,14 +209,14 @@ github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8O github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= -github.com/tutus-one/tutus-bolt v1.0.0 h1:+O167MPPixi8254zWs1lYwsZ6QBhJCaIJQf9Qc4u4q0= -github.com/tutus-one/tutus-bolt v1.0.0/go.mod h1:v5z98B9Ao/zkKsG8S9cwTovD1UsdN8mmdQTXiaBbPVo= -github.com/tutus-one/tutus-consensus v1.0.0 h1:PFMXSSSYjTnbymoIjU2L30ckf92j2kVwrmtRnkuGlnY= -github.com/tutus-one/tutus-consensus v1.0.0/go.mod h1:hpA/0rPM0Frdh9cowAp//vQTMN8j6Q0GYq1BFlpCLVM= -github.com/tutus-one/tutus-ordered-json v1.0.0 h1:/wPlNUwyqs+SUO2+Ra2sGJ2FrvQjIrh2McFDQ6moR7Y= -github.com/tutus-one/tutus-ordered-json v1.0.0/go.mod h1:M58J4PCtf7JauNaz2jgi8yKUygKMSqqG6YEa2rhRfuU= -github.com/tutus-one/tutus-rfc6979 v1.0.0 h1:eFPDOMle4Wv2RmC9DLTO4R6vjQjx5c6H9+RrdoFNeLM= -github.com/tutus-one/tutus-rfc6979 v1.0.0/go.mod h1:qary3d/z/0Zwb06/wqXv1/FHuP8rBJHrP66/8wDFaTA= +git.marketally.com/tutus-one/tutus-bolt v1.0.0 h1:+O167MPPixi8254zWs1lYwsZ6QBhJCaIJQf9Qc4u4q0= +git.marketally.com/tutus-one/tutus-bolt v1.0.0/go.mod h1:v5z98B9Ao/zkKsG8S9cwTovD1UsdN8mmdQTXiaBbPVo= +git.marketally.com/tutus-one/tutus-consensus v1.0.0 h1:PFMXSSSYjTnbymoIjU2L30ckf92j2kVwrmtRnkuGlnY= +git.marketally.com/tutus-one/tutus-consensus v1.0.0/go.mod h1:hpA/0rPM0Frdh9cowAp//vQTMN8j6Q0GYq1BFlpCLVM= +git.marketally.com/tutus-one/tutus-ordered-json v1.0.0 h1:/wPlNUwyqs+SUO2+Ra2sGJ2FrvQjIrh2McFDQ6moR7Y= +git.marketally.com/tutus-one/tutus-ordered-json v1.0.0/go.mod h1:M58J4PCtf7JauNaz2jgi8yKUygKMSqqG6YEa2rhRfuU= +git.marketally.com/tutus-one/tutus-rfc6979 v1.0.0 h1:eFPDOMle4Wv2RmC9DLTO4R6vjQjx5c6H9+RrdoFNeLM= +git.marketally.com/tutus-one/tutus-rfc6979 v1.0.0/go.mod h1:qary3d/z/0Zwb06/wqXv1/FHuP8rBJHrP66/8wDFaTA= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= diff --git a/internal/basicchain/basic.go b/internal/basicchain/basic.go index 295cb9a..6ffa474 100644 --- a/internal/basicchain/basic.go +++ b/internal/basicchain/basic.go @@ -8,18 +8,18 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nns" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nns" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/internal/basicchain/testdata/invoke/invokescript_contract.go b/internal/basicchain/testdata/invoke/invokescript_contract.go index 71ec6e3..e74598a 100644 --- a/internal/basicchain/testdata/invoke/invokescript_contract.go +++ b/internal/basicchain/testdata/invoke/invokescript_contract.go @@ -1,6 +1,6 @@ package invoke -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" // This contract is used to test `invokescript` and `invokefunction` RPC-calls func Main() int { diff --git a/internal/basicchain/testdata/storage/storage_contract.go b/internal/basicchain/testdata/storage/storage_contract.go index 6139f32..ead34b7 100644 --- a/internal/basicchain/testdata/storage/storage_contract.go +++ b/internal/basicchain/testdata/storage/storage_contract.go @@ -6,8 +6,8 @@ The contract is aimed to test iterator sessions RPC API. package storage import ( - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) // valuesCount is the amount of stored values. diff --git a/internal/basicchain/testdata/test_contract.go b/internal/basicchain/testdata/test_contract.go index 3e98e06..f005640 100644 --- a/internal/basicchain/testdata/test_contract.go +++ b/internal/basicchain/testdata/test_contract.go @@ -1,14 +1,14 @@ package testdata import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/native/tutus" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) const ( diff --git a/internal/basicchain/testdata/verify/verification_contract.go b/internal/basicchain/testdata/verify/verification_contract.go index 9d0c1a2..d7a1578 100644 --- a/internal/basicchain/testdata/verify/verification_contract.go +++ b/internal/basicchain/testdata/verify/verification_contract.go @@ -1,9 +1,9 @@ package verify import ( - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) // Verify is a verification contract method. diff --git a/internal/contracts/contracts.go b/internal/contracts/contracts.go index 72c3b41..9c5198d 100644 --- a/internal/contracts/contracts.go +++ b/internal/contracts/contracts.go @@ -8,10 +8,10 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/internal/contracts/contracts_test.go b/internal/contracts/contracts_test.go index b94db82..92065b1 100644 --- a/internal/contracts/contracts_test.go +++ b/internal/contracts/contracts_test.go @@ -5,21 +5,21 @@ import ( "os" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/internal/contracts/oracle_contract/go.mod b/internal/contracts/oracle_contract/go.mod index 385b048..6cbe869 100644 --- a/internal/contracts/oracle_contract/go.mod +++ b/internal/contracts/oracle_contract/go.mod @@ -1,7 +1,7 @@ -module github.com/tutus-one/tutus-chain/internal/examples/oracle +module git.marketally.com/tutus-one/tutus-chain/internal/examples/oracle go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 -replace github.com/tutus-one/tutus-chain/pkg/interop => ../../../pkg/interop +replace git.marketally.com/tutus-one/tutus-chain/pkg/interop => ../../../pkg/interop diff --git a/internal/contracts/oracle_contract/go.sum b/internal/contracts/oracle_contract/go.sum index a6aeabe..f1b4892 100644 --- a/internal/contracts/oracle_contract/go.sum +++ b/internal/contracts/oracle_contract/go.sum @@ -1,2 +1,2 @@ -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9 h1:5+Ue5+i72uJVfHoq1+6mc6KlpriaqQaO96LtaDEsTfg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251217090505-857f951d81a9/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= diff --git a/internal/contracts/oracle_contract/oracle.go b/internal/contracts/oracle_contract/oracle.go index 576b3c0..66b0113 100644 --- a/internal/contracts/oracle_contract/oracle.go +++ b/internal/contracts/oracle_contract/oracle.go @@ -1,13 +1,13 @@ package oraclecontract import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/native/oracle" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) // RequestURL accepts a complete set of parameters to make an oracle request and diff --git a/internal/fakechain/fakechain.go b/internal/fakechain/fakechain.go index 3753230..0d176e4 100644 --- a/internal/fakechain/fakechain.go +++ b/internal/fakechain/fakechain.go @@ -6,21 +6,21 @@ import ( "sync/atomic" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // FakeChain implements the Blockchainer interface, but does not provide real functionality. diff --git a/internal/random/random_util.go b/internal/random/random_util.go index 7e7e3da..f8c4c91 100644 --- a/internal/random/random_util.go +++ b/internal/random/random_util.go @@ -3,8 +3,8 @@ package random import ( "math/rand/v2" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // String returns a random string with the n as its length. diff --git a/internal/testchain/address.go b/internal/testchain/address.go index 9f5cf36..bea7e9b 100644 --- a/internal/testchain/address.go +++ b/internal/testchain/address.go @@ -4,15 +4,15 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" "github.com/stretchr/testify/require" ) diff --git a/internal/testchain/network.go b/internal/testchain/network.go index b832458..5aadf4d 100644 --- a/internal/testchain/network.go +++ b/internal/testchain/network.go @@ -1,6 +1,6 @@ package testchain -import "github.com/tutus-one/tutus-chain/pkg/config/netmode" +import "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" // Network returns testchain network's magic number. func Network() netmode.Magic { diff --git a/internal/testchain/transaction.go b/internal/testchain/transaction.go index 91e1caa..a32e27d 100644 --- a/internal/testchain/transaction.go +++ b/internal/testchain/transaction.go @@ -6,20 +6,20 @@ import ( gio "io" "strings" - clisc "github.com/tutus-one/tutus-chain/cli/smartcontract" - "github.com/tutus-one/tutus-chain/internal/versionutil" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" + clisc "git.marketally.com/tutus-one/tutus-chain/cli/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/internal/versionutil" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Ledger is an interface that abstracts the implementation of the blockchain. diff --git a/internal/testcli/executor.go b/internal/testcli/executor.go index bbf40d3..51ff59f 100644 --- a/internal/testcli/executor.go +++ b/internal/testcli/executor.go @@ -19,20 +19,20 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/cli/app" - "github.com/tutus-one/tutus-chain/cli/input" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/consensus" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/network" - "github.com/tutus-one/tutus-chain/pkg/services/rpcsrv" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/cli/app" + "git.marketally.com/tutus-one/tutus-chain/cli/input" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/rpcsrv" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" "go.uber.org/zap" diff --git a/internal/testserdes/testing.go b/internal/testserdes/testing.go index d2e8e37..03f11bf 100644 --- a/internal/testserdes/testing.go +++ b/internal/testserdes/testing.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/internal/versionutil/init.go b/internal/versionutil/init.go index 9453566..49aa45f 100644 --- a/internal/versionutil/init.go +++ b/internal/versionutil/init.go @@ -1,6 +1,6 @@ package versionutil -import "github.com/tutus-one/tutus-chain/pkg/config" +import "git.marketally.com/tutus-one/tutus-chain/pkg/config" // TestVersion is a NeoGo version that should be used to keep all // compiled NEFs the same from run to run for tests. diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index ffee2f0..2a4fe2c 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -9,8 +9,8 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "golang.org/x/tools/go/packages" ) @@ -21,7 +21,7 @@ var ( // ErrInvalidExportedRetCount is returned when exported contract method has invalid return values count. ErrInvalidExportedRetCount = errors.New("exported method is not allowed to have more than one return value") // ErrGenericsUnsupported is returned when generics-related tokens are encountered. - ErrGenericsUnsupported = errors.New("generics are currently unsupported, please, see the https://github.com/tutus-one/tutus-chain/issues/2376") + ErrGenericsUnsupported = errors.New("generics are currently unsupported, please, see the https://git.marketally.com/tutus-one/tutus-chain/issues/2376") ) var ( @@ -721,7 +721,7 @@ func isSyscall(fun *funcScope) bool { strings.HasPrefix(fun.name, "Opcode") || strings.HasPrefix(fun.name, "CallWithToken")) } -const interopPrefix = "github.com/tutus-one/tutus-chain/pkg/interop" +const interopPrefix = "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func isInteropPath(s string) bool { return strings.HasPrefix(s, interopPrefix) @@ -748,7 +748,7 @@ func canConvert(s string) bool { // manually. // nolint:staticcheck // could apply De Morgan's law, but it's less readable. func canInline(s string, name string, isBuiltin bool) bool { - if strings.HasPrefix(s, "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline") { + if strings.HasPrefix(s, "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline") { return true } if !isInteropPath(s) { diff --git a/pkg/compiler/assign_test.go b/pkg/compiler/assign_test.go index 2deb16f..6f05fdd 100644 --- a/pkg/compiler/assign_test.go +++ b/pkg/compiler/assign_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/binary_expr_test.go b/pkg/compiler/binary_expr_test.go index d6611f4..df9d862 100644 --- a/pkg/compiler/binary_expr_test.go +++ b/pkg/compiler/binary_expr_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/builtin_test.go b/pkg/compiler/builtin_test.go index d753728..bd8aa5a 100644 --- a/pkg/compiler/builtin_test.go +++ b/pkg/compiler/builtin_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index f4a16ad..d8e14e8 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -14,17 +14,17 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/util/bitfield" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util/bitfield" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "golang.org/x/tools/go/packages" ) diff --git a/pkg/compiler/codegen_test.go b/pkg/compiler/codegen_test.go index 12ab145..0556f18 100644 --- a/pkg/compiler/codegen_test.go +++ b/pkg/compiler/codegen_test.go @@ -5,7 +5,7 @@ import ( "go/types" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" ) diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index c6f7c7e..928dc65 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -13,13 +13,13 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest/standard" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/rpcbinding" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest/standard" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/rpcbinding" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "golang.org/x/tools/go/packages" "gopkg.in/yaml.v3" ) diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go index fe59c6c..b0e7214 100644 --- a/pkg/compiler/compiler_test.go +++ b/pkg/compiler/compiler_test.go @@ -8,15 +8,15 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/versionutil" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/interop/native/tutus" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/versionutil" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -130,25 +130,25 @@ func TestOnPayableChecks(t *testing.T) { t.Run("NEP-11, good", func(t *testing.T) { src := `package payable - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func OnNEP11Payment(from interop.Hash160, amount int, tokenID []byte, data any) {}` require.NoError(t, compileAndCheck(t, src)) }) t.Run("NEP-11, bad", func(t *testing.T) { src := `package payable - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func OnNEP11Payment(from interop.Hash160, amount int, oldParam string, tokenID []byte, data any) {}` require.Error(t, compileAndCheck(t, src)) }) t.Run("NEP-17, good", func(t *testing.T) { src := `package payable - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func OnNEP17Payment(from interop.Hash160, amount int, data any) {}` require.NoError(t, compileAndCheck(t, src)) }) t.Run("NEP-17, bad", func(t *testing.T) { src := `package payable - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func OnNEP17Payment(from interop.Hash160, amount int, data any, extra int) {}` require.Error(t, compileAndCheck(t, src)) }) @@ -171,7 +171,7 @@ func TestSafeMethodWarnings(t *testing.T) { func TestEventWarnings(t *testing.T) { src := `package payable - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() { runtime.Notify("Event", 1) }` _, di, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), nil) @@ -226,7 +226,7 @@ func TestEventWarnings(t *testing.T) { t.Run("event in imported package", func(t *testing.T) { t.Run("unused", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/notify" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/notify" func Main() int { return notify.Value }` @@ -239,7 +239,7 @@ func TestEventWarnings(t *testing.T) { }) t.Run("used", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/notify" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/notify" func Main() int { notify.EmitEvent() return 42 @@ -261,7 +261,7 @@ func TestEventWarnings(t *testing.T) { }) t.Run("variadic event args via ellipsis", func(t *testing.T) { src := `package payable - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() { runtime.Notify("Event", []any{1}...) }` @@ -282,7 +282,7 @@ func TestEventWarnings(t *testing.T) { func TestNotifyInVerify(t *testing.T) { srcTmpl := `package payable - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Verify() bool { runtime.%s("Event"); return true }` for _, name := range []string{"Notify", "Log"} { @@ -315,8 +315,8 @@ func TestInvokedContractsPermissons(t *testing.T) { t.Run("native", func(t *testing.T) { src := `package test - import "github.com/tutus-one/tutus-chain/pkg/interop/native/tutus" - import "github.com/tutus-one/tutus-chain/pkg/interop/native/management" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/tutus" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" func Main() int { tutus.Transfer(nil, nil, 10, nil) management.GetContract(nil) // skip read-only @@ -344,9 +344,9 @@ func TestInvokedContractsPermissons(t *testing.T) { t.Run("custom", func(t *testing.T) { hashStr := "aaaaaaaaaaaaaaaaaaaa" src := fmt.Sprintf(`package test - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" - import "github.com/tutus-one/tutus-chain/pkg/interop" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/runh" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/runh" const hash = "%s" var runtimeHash interop.Hash160 diff --git a/pkg/compiler/constant_test.go b/pkg/compiler/constant_test.go index 2d717a3..0b50b9a 100644 --- a/pkg/compiler/constant_test.go +++ b/pkg/compiler/constant_test.go @@ -38,7 +38,7 @@ func TestShortHandMultiConst(t *testing.T) { func TestByteConstant(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/convert" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert" const a byte = 0xFF func Main() int { x := convert.ToInteger(a) diff --git a/pkg/compiler/convert_test.go b/pkg/compiler/convert_test.go index 1d9334e..8cfb30e 100644 --- a/pkg/compiler/convert_test.go +++ b/pkg/compiler/convert_test.go @@ -8,10 +8,10 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -62,7 +62,7 @@ func TestConvert(t *testing.T) { } srcBuilder := bytes.NewBuffer([]byte(`package testcase - import "github.com/tutus-one/tutus-chain/pkg/interop/convert" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert" `)) for i, tc := range convertTestCases { name := getFunctionName(tc.returnType) @@ -189,9 +189,9 @@ func TestTypeConversion(t *testing.T) { func TestSelectorTypeConversion(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/types" - import "github.com/tutus-one/tutus-chain/pkg/interop/util" - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/types" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() int { var a int if util.Equals(types.Buffer(nil), nil) { @@ -311,7 +311,7 @@ func TestConvert_Uint(t *testing.T) { } srcToBytesTmpl := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/convert" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert" func Main(args []any) []byte { return convert.%sToBytes%s(args[0].(%s)) @@ -319,7 +319,7 @@ func TestConvert_Uint(t *testing.T) { ` srcFromBytesTmpl := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/convert" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert" func Main(args []any) %s { return convert.Bytes%sTo%s(args[0].([]byte)) @@ -327,7 +327,7 @@ func TestConvert_Uint(t *testing.T) { ` srcCompatibilityCheckTmpl := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/convert" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert" func Main(args []any) %s { data := convert.%sToBytes%s(args[0].(%s)) return convert.Bytes%sTo%s(data) diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 23bcb61..885f141 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -12,12 +12,12 @@ import ( "unicode" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // DebugInfo represents smart-contract debug information. diff --git a/pkg/compiler/debug_test.go b/pkg/compiler/debug_test.go index 8f99f41..2e13dae 100644 --- a/pkg/compiler/debug_test.go +++ b/pkg/compiler/debug_test.go @@ -5,20 +5,20 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCodeGen_DebugInfo(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" - import "github.com/tutus-one/tutus-chain/pkg/interop/storage" - import "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" var staticVar int func init() { a := 1 diff --git a/pkg/compiler/defer_test.go b/pkg/compiler/defer_test.go index 6b24591..8ca363b 100644 --- a/pkg/compiler/defer_test.go +++ b/pkg/compiler/defer_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -56,7 +56,7 @@ func TestDefer(t *testing.T) { src := `package main import ( - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) func Main() { diff --git a/pkg/compiler/for_test.go b/pkg/compiler/for_test.go index 993831a..1f680f9 100644 --- a/pkg/compiler/for_test.go +++ b/pkg/compiler/for_test.go @@ -7,10 +7,10 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/function_call_test.go b/pkg/compiler/function_call_test.go index 6653f54..4cba1e7 100644 --- a/pkg/compiler/function_call_test.go +++ b/pkg/compiler/function_call_test.go @@ -6,15 +6,15 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) func TestReturnValueReceiver(t *testing.T) { t.Run("regular", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/method" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/method" func Main() int { return method.NewX().GetA() @@ -23,7 +23,7 @@ func TestReturnValueReceiver(t *testing.T) { }) t.Run("inline", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { return inline.NewT().GetN() @@ -89,7 +89,7 @@ func TestNotAssignedFunctionCall(t *testing.T) { }) t.Run("Builtin", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" func Main() int { address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") @@ -364,7 +364,7 @@ func TestFunctionUnusedParameters(t *testing.T) { func TestUnusedFunctions(t *testing.T) { t.Run("only variable", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall" func Main() int { return nestedcall.X }` @@ -377,7 +377,7 @@ func TestUnusedFunctions(t *testing.T) { t.Run("imported function", func(t *testing.T) { // Check that import map is set correctly during package traversal. src := `package foo - import inner "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall" + import inner "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall" func Main() int { return inner.N() }` @@ -389,7 +389,7 @@ func TestUnusedFunctions(t *testing.T) { t.Run("method inside of an imported package", func(t *testing.T) { // Check that import map is set correctly during package traversal. src := `package foo - import inner "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall" + import inner "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall" func Main() int { var t inner.Token return t.Method() diff --git a/pkg/compiler/generics_test.go b/pkg/compiler/generics_test.go index 739aebc..a0b5911 100644 --- a/pkg/compiler/generics_test.go +++ b/pkg/compiler/generics_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/global_test.go b/pkg/compiler/global_test.go index 4ec8ff7..16bd49a 100644 --- a/pkg/compiler/global_test.go +++ b/pkg/compiler/global_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) @@ -588,7 +588,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { t.Run("imported", func(t *testing.T) { t.Run("init by func call", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar" func Main() int { return globalvar.Default }` @@ -597,7 +597,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { }) t.Run("nested var call", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested1" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested1" func Main() int { return nested1.C }` @@ -606,7 +606,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { }) t.Run("nested func call", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/funccall" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/funccall" func Main() int { return funccall.F() }` @@ -615,7 +615,7 @@ func TestUnusedOptimizedGlobalVar(t *testing.T) { }) t.Run("nested method call", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/funccall" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/funccall" func Main() int { return funccall.GetAge() }` @@ -642,7 +642,7 @@ func TestChangeGlobal(t *testing.T) { eval(t, src, big.NewInt(42)) }) t.Run("from other global", func(t *testing.T) { - t.Skip("see https://github.com/tutus-one/tutus-chain/issues/2661") + t.Skip("see https://git.marketally.com/tutus-one/tutus-chain/issues/2661") src := `package foo var A = f() var B int @@ -773,7 +773,7 @@ func TestContractWithNoMain(t *testing.T) { func TestMultipleFiles(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" func Main() int { return multi.Sum() }` @@ -783,7 +783,7 @@ func TestMultipleFiles(t *testing.T) { func TestExportedVariable(t *testing.T) { t.Run("Use", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" func Main() int { return multi.SomeVar12 }` @@ -791,7 +791,7 @@ func TestExportedVariable(t *testing.T) { }) t.Run("ChangeAndUse", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" func Main() int { multi.SomeVar12 = 10 return multi.Sum() @@ -800,7 +800,7 @@ func TestExportedVariable(t *testing.T) { }) t.Run("PackageAlias", func(t *testing.T) { src := `package foo - import kek "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" + import kek "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" func Main() int { kek.SomeVar12 = 10 return kek.Sum() @@ -809,7 +809,7 @@ func TestExportedVariable(t *testing.T) { }) t.Run("DifferentName", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/strange" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/strange" func Main() int { normal.NormalVar = 42 return normal.NormalVar @@ -818,7 +818,7 @@ func TestExportedVariable(t *testing.T) { }) t.Run("MultipleEqualNames", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" var SomeVar12 = 1 func Main() int { SomeVar30 := 3 @@ -834,7 +834,7 @@ func TestExportedVariable(t *testing.T) { func TestExportedConst(t *testing.T) { t.Run("with vars", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" func Main() int { return multi.SomeConst }` @@ -842,7 +842,7 @@ func TestExportedConst(t *testing.T) { }) t.Run("const only", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/constonly" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/constonly" func Main() int { return constonly.Answer }` @@ -853,7 +853,7 @@ func TestExportedConst(t *testing.T) { func TestMultipleFuncSameName(t *testing.T) { t.Run("Simple", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/multi" func Main() int { return multi.Sum() + Sum() } @@ -864,7 +864,7 @@ func TestMultipleFuncSameName(t *testing.T) { }) t.Run("WithMethod", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" type Foo struct{} func (f Foo) Bar() int { return 11 } func Bar() int { return 22 } diff --git a/pkg/compiler/import_test.go b/pkg/compiler/import_test.go index 63add37..c271dd6 100644 --- a/pkg/compiler/import_test.go +++ b/pkg/compiler/import_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" "github.com/stretchr/testify/require" ) @@ -13,7 +13,7 @@ func TestImportFunction(t *testing.T) { src := ` package somethingelse - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" func Main() int { i := foo.NewBar() @@ -27,7 +27,7 @@ func TestImportStruct(t *testing.T) { src := ` package somethingwedontcareabout - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/bar" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/bar" func Main() int { b := bar.Bar{ @@ -43,7 +43,7 @@ func TestMultipleDirFileImport(t *testing.T) { src := ` package hello - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/foobar" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/foobar" func Main() bool { ok := foobar.OtherBool() @@ -55,7 +55,7 @@ func TestMultipleDirFileImport(t *testing.T) { func TestImportNameSameAsOwn(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" func get3() int { return 3 } func Main() int { return get3() @@ -69,7 +69,7 @@ func TestImportNameSameAsOwn(t *testing.T) { func TestImportCycleDirect(t *testing.T) { src := ` package some - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg2" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg2" func Main() int { return pkg2.A } @@ -81,7 +81,7 @@ func TestImportCycleDirect(t *testing.T) { func TestImportCycleIndirect(t *testing.T) { src := ` package some - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg1" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg1" func Main() int { return pkg1.A } diff --git a/pkg/compiler/init_test.go b/pkg/compiler/init_test.go index 8ec6c4e..1617078 100644 --- a/pkg/compiler/init_test.go +++ b/pkg/compiler/init_test.go @@ -68,23 +68,23 @@ func TestInit(t *testing.T) { func TestImportOrder(t *testing.T) { t.Run("1,2", func(t *testing.T) { src := `package foo - import _ "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg1" - import _ "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg2" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" + import _ "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg1" + import _ "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg2" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" func Main() int { return pkg3.A }` eval(t, src, big.NewInt(2)) }) t.Run("2,1", func(t *testing.T) { src := `package foo - import _ "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg2" - import _ "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg1" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" + import _ "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg2" + import _ "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg1" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" func Main() int { return pkg3.A }` eval(t, src, big.NewInt(1)) }) t.Run("InitializeOnce", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" var A = pkg3.A func Main() int { return A }` eval(t, src, big.NewInt(3)) @@ -93,7 +93,7 @@ func TestImportOrder(t *testing.T) { func TestInitWithNoGlobals(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func init() { runtime.Notify("called in '_initialize'") } diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go index 9b4881c..2a597ed 100644 --- a/pkg/compiler/inline.go +++ b/pkg/compiler/inline.go @@ -7,13 +7,13 @@ import ( "go/types" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // inlineCall inlines call of n for function represented by f. diff --git a/pkg/compiler/inline_test.go b/pkg/compiler/inline_test.go index c7278bb..94f199b 100644 --- a/pkg/compiler/inline_test.go +++ b/pkg/compiler/inline_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) @@ -63,8 +63,8 @@ func checkInstrCount(t *testing.T, src string, expectedSSlotCount, expectedCall, func TestInline(t *testing.T) { srcTmpl := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/foo" var _ = foo.Dummy type pair struct { a, b int } type triple struct { @@ -184,7 +184,7 @@ func TestInline(t *testing.T) { func TestIssue1879(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() int { data := "main is called" runtime.Log("log " + string(data)) @@ -196,8 +196,8 @@ func TestIssue1879(t *testing.T) { func TestInlineInLoop(t *testing.T) { t.Run("simple", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/storage" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { sum := 0 values := []int{10, 11} @@ -212,9 +212,9 @@ func TestInlineInLoop(t *testing.T) { }) t.Run("inlined argument", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - import "github.com/tutus-one/tutus-chain/pkg/interop/storage" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { sum := 0 values := []int{10, 11} @@ -229,8 +229,8 @@ func TestInlineInLoop(t *testing.T) { }) t.Run("check clean stack on return", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/storage" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { values := []int{10, 11, 12} for _, v := range values { @@ -247,7 +247,7 @@ func TestInlineInLoop(t *testing.T) { func TestInlineInSwitch(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { switch inline.VarSum(1, 2) { case inline.VarSum(3, 1): @@ -264,7 +264,7 @@ func TestInlineInSwitch(t *testing.T) { func TestInlineGlobalVariable(t *testing.T) { t.Run("simple", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" var a = inline.Sum(1, 2) func Main() int { return a @@ -273,7 +273,7 @@ func TestInlineGlobalVariable(t *testing.T) { }) t.Run("complex", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" var a = inline.Sum(3, 4) var b = inline.SumSquared(1, 2) var c = a + b @@ -289,7 +289,7 @@ func TestInlineGlobalVariable(t *testing.T) { func TestInlineVariadicInInlinedCall(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { return inline.SumSquared(inline.SumVar(3, 4) - 2, 3) }` @@ -298,7 +298,7 @@ func TestInlineVariadicInInlinedCall(t *testing.T) { func TestInlineConversion(t *testing.T) { src1 := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" var _ = inline.A func Main() int { a := 2 @@ -308,7 +308,7 @@ func TestInlineConversion(t *testing.T) { require.NoError(t, err) src2 := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" var _ = inline.A func Main() int { a := 2 @@ -323,7 +323,7 @@ func TestInlineConversion(t *testing.T) { func TestInlineConversionQualified(t *testing.T) { src1 := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" var A = 1 func Main() int { return inline.Concat(A) @@ -332,8 +332,8 @@ func TestInlineConversionQualified(t *testing.T) { require.NoError(t, err) src2 := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/b" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/b" var A = 1 func Main() int { return A * 100 + b.A * 10 + inline.A @@ -345,8 +345,8 @@ func TestInlineConversionQualified(t *testing.T) { func TestPackageVarsInInlinedCalls(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/b" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/b" func Main() int { return inline.Sum(inline.A, b.A) }` @@ -355,7 +355,7 @@ func TestPackageVarsInInlinedCalls(t *testing.T) { func TestInlinedMethod(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { // It's important for this variable to not be named 't'. var z inline.T @@ -374,7 +374,7 @@ func TestInlinedMethod(t *testing.T) { func TestInlinedMethodWithPointer(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { // It's important for this variable to not be named 't'. var z = &inline.T{} @@ -393,7 +393,7 @@ func TestInlinedMethodWithPointer(t *testing.T) { func TestInlineConditionalReturn(t *testing.T) { srcTmpl := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/c" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/c" func Main() int { x := %d if c.Is42(x) { @@ -411,7 +411,7 @@ func TestInlineConditionalReturn(t *testing.T) { func TestInlineDoubleConditionalReturn(t *testing.T) { srcTmpl := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/c" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/c" func Main() int { return c.Transform(%d, %d) }` @@ -436,7 +436,7 @@ func TestInlineDoubleConditionalReturn(t *testing.T) { func TestInlineAppendStatement(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() []byte { val := []byte{4, 5, 6} @@ -447,7 +447,7 @@ func TestInlineAppendStatement(t *testing.T) { func TestInlineForeignType(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline" func Main() int { return inline.ForeignTypeInsideInline() @@ -457,7 +457,7 @@ func TestInlineForeignType(t *testing.T) { func TestInlineModifyArg(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" func Main() int { return d.Negate(-42) @@ -467,7 +467,7 @@ func TestInlineModifyArg(t *testing.T) { func TestInlineMixedArgs(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" func someCall() int { return -7 } @@ -479,7 +479,7 @@ func TestInlineMixedArgs(t *testing.T) { func TestInlineChain(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" func Main() int { return d.Wrap2(-42) @@ -489,7 +489,7 @@ func TestInlineChain(t *testing.T) { func TestInlineSlice(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/d" func Main() int { s := make([]int, 1) diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 04b6039..cda1f05 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -8,38 +8,38 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/base58" - cinterop "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/base58" + cinterop "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) func TestTypeConstantSize(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" var a %T // type declaration is always ok func Main() any { return %#v @@ -83,8 +83,8 @@ func TestAddressToHash160BuiltinConversion(t *testing.T) { t.Run("builtin conversion", func(t *testing.T) { src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" ) var addr = address.ToHash160("` + a + `") func Main() interop.Hash160 { @@ -100,8 +100,8 @@ func TestAddressToHash160BuiltinConversion(t *testing.T) { t.Run("generate code", func(t *testing.T) { src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" ) var addr = "` + a + `" func Main() interop.Hash160 { @@ -119,7 +119,7 @@ func TestAddressToHash160BuiltinConversion(t *testing.T) { t.Run("AliasPackage", func(t *testing.T) { src := ` package foo - import ad "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" + import ad "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" func Main() []byte { addr1 := ad.ToHash160("` + a + `") addr2 := ad.ToHash160("` + a2 + `") @@ -139,8 +139,8 @@ func TestInvokeAddressToFromHash160(t *testing.T) { e := tutustest.NewExecutor(t, bc, acc, acc) src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" ) const addr = "` + a + `" func ToHash160(a string) interop.Hash160 { @@ -184,7 +184,7 @@ func TestInvokeAddressToFromHash160(t *testing.T) { func TestAbort(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" func Main() int { util.Abort() return 1 @@ -196,7 +196,7 @@ func TestAbort(t *testing.T) { func TestAbortMsg(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" func Main() int { util.AbortMsg("some message") return 1 @@ -210,7 +210,7 @@ func TestAbortMsg(t *testing.T) { func TestAssert(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" func Main(ok bool) int { util.Assert(ok) return 1 @@ -230,7 +230,7 @@ func TestAssert(t *testing.T) { func TestAssertMsg(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" func Main(ok bool) int { util.AssertMsg(ok, "some message") return 1 @@ -253,8 +253,8 @@ func TestCurrentSigners(t *testing.T) { e := tutustest.NewExecutor(t, bc, acc, acc) src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) func Main() []ledger.TransactionSigner { return runtime.CurrentSigners() @@ -282,7 +282,7 @@ func TestStdLib_StrLen(t *testing.T) { e := tutustest.NewExecutor(t, bc, acc, acc) src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" ) func Main(s string) int { return std.StrLen(s) @@ -322,8 +322,8 @@ func TestAppCall(t *testing.T) { barH := hash.Hash160(barCtr.Script) srcInner := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" var a int = 3 func Main(a []byte, b []byte) []byte { panic("Main was called") @@ -409,7 +409,7 @@ func TestAppCall(t *testing.T) { t.Run("convert from string constant", func(t *testing.T) { src := ` package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" const scriptHash = ` + fmt.Sprintf("%#v", string(ih.BytesBE())) + ` func Main() []byte { x := []byte{1, 2} @@ -428,7 +428,7 @@ func TestAppCall(t *testing.T) { t.Run("convert from var", func(t *testing.T) { src := ` package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" func Main() []byte { x := []byte{1, 2} y := []byte{3, 4} @@ -446,7 +446,7 @@ func TestAppCall(t *testing.T) { t.Run("InitializedGlobals", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" func Main() int { var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) result := contract.Call(addr, "add3", contract.All, 39) @@ -461,7 +461,7 @@ func TestAppCall(t *testing.T) { t.Run("AliasPackage", func(t *testing.T) { src := `package foo - import ee "github.com/tutus-one/tutus-chain/pkg/interop/contract" + import ee "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" func Main() int { var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) result := ee.Call(addr, "add3", ee.All, 39) @@ -476,7 +476,7 @@ func TestAppCall(t *testing.T) { func getAppCallScript(h string) string { return ` package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" func Main() []byte { x := []byte{1, 2} y := []byte{3, 4} @@ -488,7 +488,7 @@ func getAppCallScript(h string) string { func getCallExScript(h string, flags string) string { return `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" func Main() int { result := contract.Call(` + h + `, "callInner", ` + flags + `) return result.(int) @@ -497,7 +497,7 @@ func getCallExScript(h string, flags string) string { func TestBuiltinDoesNotCompile(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" func Main() bool { a := 1 b := 2 @@ -520,7 +520,7 @@ func TestBuiltinDoesNotCompile(t *testing.T) { func TestInteropPackage(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/block" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/block" func Main() int { b := block.Block{} a := block.GetTransactionCount(b) @@ -531,7 +531,7 @@ func TestInteropPackage(t *testing.T) { func TestBuiltinPackage(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/util" func Main() int { if util.Equals(1, 2) { // always returns true return 1 @@ -555,7 +555,7 @@ func TestLenForNil(t *testing.T) { func TestCallTConversionErrors(t *testing.T) { t.Run("variable hash", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" func Main() int { var hash string return tutusinternal.CallWithToken(hash, "method", 0).(int) @@ -565,7 +565,7 @@ func TestCallTConversionErrors(t *testing.T) { }) t.Run("bad hash", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" func Main() int { return tutusinternal.CallWithToken("badstring", "method", 0).(int) }` @@ -574,7 +574,7 @@ func TestCallTConversionErrors(t *testing.T) { }) t.Run("variable method", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" func Main() int { var method string return tutusinternal.CallWithToken("\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\xa0\x73\x40\xef", method, 0).(int) @@ -584,7 +584,7 @@ func TestCallTConversionErrors(t *testing.T) { }) t.Run("variable flags", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" func Main() { var flags int tutusinternal.CallWithTokenNoRet("\xf5\x63\xea\x40\xbc\x28\x3d\x4d\x0e\x05\xc4\x8e\xa3\x05\xb3\xf2\xa0\x73\x40\xef", "method", flags) @@ -599,9 +599,9 @@ func TestCallWithVersion(t *testing.T) { e := tutustest.NewExecutor(t, bc, acc, acc) src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - util "github.com/tutus-one/tutus-chain/pkg/interop/lib/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + util "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/contract" ) func CallWithVersion(hash interop.Hash160, version int, method string) any { return util.CallWithVersion(hash, version, method, contract.All) @@ -631,7 +631,7 @@ func TestForcedNotifyArgumentsConversion(t *testing.T) { }) e := tutustest.NewExecutor(t, bc, acc, acc) src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" const arg4 = 4 // Const value. func WithoutEllipsis() { var arg0 int // Default value. @@ -775,8 +775,8 @@ func TestStorageIterator_Value(t *testing.T) { src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" ) func Main() int { diff --git a/pkg/compiler/jumps_test.go b/pkg/compiler/jumps_test.go index 970804c..7dfa7c4 100644 --- a/pkg/compiler/jumps_test.go +++ b/pkg/compiler/jumps_test.go @@ -3,7 +3,7 @@ package compiler import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/map_test.go b/pkg/compiler/map_test.go index 0bc0e2d..8de9645 100644 --- a/pkg/compiler/map_test.go +++ b/pkg/compiler/map_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var mapTestCases = []testCase{ diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go index 95f3ac3..6e1c1cc 100644 --- a/pkg/compiler/native_test.go +++ b/pkg/compiler/native_test.go @@ -9,30 +9,30 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/interop/native/crypto" - "github.com/tutus-one/tutus-chain/pkg/interop/native/lub" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/native/tutus" - "github.com/tutus-one/tutus-chain/pkg/interop/native/notary" - "github.com/tutus-one/tutus-chain/pkg/interop/native/oracle" - "github.com/tutus-one/tutus-chain/pkg/interop/native/policy" - "github.com/tutus-one/tutus-chain/pkg/interop/native/roles" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/lub" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/policy" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/roles" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) @@ -283,8 +283,8 @@ func TestNativeHelpersCompile(t *testing.T) { func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, nativeTestCases []nativeTestCase) { srcBuilder := bytes.NewBuffer([]byte(`package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/native/` + name + `" - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/` + name + `" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" var _ interop.Hash160 `)) for i, tc := range nativeTestCases { diff --git a/pkg/compiler/slice_test.go b/pkg/compiler/slice_test.go index 1e23b0d..7d4ee70 100644 --- a/pkg/compiler/slice_test.go +++ b/pkg/compiler/slice_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -564,7 +564,7 @@ func TestSubsliceFromStructField(t *testing.T) { func TestRemove(t *testing.T) { t.Run("Valid", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" func Main() int { a := []int{11, 22, 33} util.Remove(a, 1) @@ -578,7 +578,7 @@ func TestRemove(t *testing.T) { // Skip the test for now. t.Skip() src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/util" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" func Main() int { a := []byte{11, 22, 33} util.Remove(a, 1) diff --git a/pkg/compiler/struct_test.go b/pkg/compiler/struct_test.go index 2bfb4ab..7556fcd 100644 --- a/pkg/compiler/struct_test.go +++ b/pkg/compiler/struct_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/switch_test.go b/pkg/compiler/switch_test.go index dc359d2..e41c04b 100644 --- a/pkg/compiler/switch_test.go +++ b/pkg/compiler/switch_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" "github.com/stretchr/testify/require" ) diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index be1b763..7a0a9ee 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -8,20 +8,20 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - istorage "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + istorage "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -120,12 +120,12 @@ func TestSyscallExecution(t *testing.T) { srcBuilder := bytes.NewBuffer(nil) srcBuilder.WriteString(`package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/contract" - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - import "github.com/tutus-one/tutus-chain/pkg/interop/storage" - import "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - import "github.com/tutus-one/tutus-chain/pkg/interop/crypto" - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/crypto" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func unused() { var _ interop.Hash160 } `) for goName, tc := range interops { @@ -187,7 +187,7 @@ func TestStoragePutGet(t *testing.T) { src := ` package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/storage" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" func Main() string { ctx := storage.GetContext() @@ -202,7 +202,7 @@ func TestStoragePutGet(t *testing.T) { func TestNotify(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main(arg int) { runtime.Notify("Event1", arg, "sum", arg+1) runtime.Notify("single") @@ -222,7 +222,7 @@ func TestNotify(t *testing.T) { t.Run("long event name", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main(arg int) { runtime.Notify("long event12345678901234567890123") }` @@ -234,7 +234,7 @@ func TestNotify(t *testing.T) { func TestSyscallInGlobalInit(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" var a = runtime.CheckWitness([]byte("5T")) func Main() bool { return a @@ -253,7 +253,7 @@ func TestSyscallInGlobalInit(t *testing.T) { func TestOpcode(t *testing.T) { t.Run("1 argument", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" func abs(a int) int { return tutusinternal.Opcode1("ABS", a).(int) } @@ -264,7 +264,7 @@ func TestOpcode(t *testing.T) { }) t.Run("2 arguments", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" func add3(a, b, c int) int { return tutusinternal.Opcode2("SUB", a, tutusinternal.Opcode2("SUB", b, c).(int)).(int) @@ -276,7 +276,7 @@ func TestOpcode(t *testing.T) { }) t.Run("POW", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/math" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/math" func Main() int { return math.Pow(2, math.Pow(3, 2)) }` @@ -284,7 +284,7 @@ func TestOpcode(t *testing.T) { }) t.Run("SRQT", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/math" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/math" func Main() int { return math.Sqrt(math.Sqrt(101)) // == sqrt(10) == 3 }` @@ -292,7 +292,7 @@ func TestOpcode(t *testing.T) { }) t.Run("SIGN", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/math" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/math" func Main() []int { signs := make([]int, 3) signs[0] = math.Sign(-123) @@ -308,7 +308,7 @@ func TestOpcode(t *testing.T) { }) t.Run("ABS", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/math" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/math" func Main() int { return math.Abs(-3) }` @@ -316,7 +316,7 @@ func TestOpcode(t *testing.T) { }) t.Run("WITHIN", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/math" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/math" func Main() []bool { r := make([]bool, 5) r[0] = math.Within(2, 3, 5) @@ -336,7 +336,7 @@ func TestOpcode(t *testing.T) { }) t.Run("MODMUL", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/math" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/math" func Main() []int { r := make([]int, 6) r[0] = math.ModMul(3, 4, 5) @@ -358,7 +358,7 @@ func TestOpcode(t *testing.T) { }) t.Run("MODPOW", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop/math" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/math" func Main() []int { r := make([]int, 5) r[0] = math.ModPow(3, 4, 5) @@ -393,7 +393,7 @@ func TestInteropTypesComparison(t *testing.T) { } check := func(t *testing.T, a, b string, expected bool) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { a := interop.` + typeName + `{` + a + `} b := interop.` + typeName + `{` + b + `} @@ -407,7 +407,7 @@ func TestInteropTypesComparison(t *testing.T) { }) t.Run("a is nil", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { var a interop.` + typeName + ` @@ -418,7 +418,7 @@ func TestInteropTypesComparison(t *testing.T) { }) t.Run("b is nil", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { a := interop.` + typeName + `{` + ha + `} @@ -429,7 +429,7 @@ func TestInteropTypesComparison(t *testing.T) { }) t.Run("both nil", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { var a interop.` + typeName + ` @@ -440,7 +440,7 @@ func TestInteropTypesComparison(t *testing.T) { }) t.Run("different types", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { a := interop.` + typeName + `{` + ha + `} @@ -451,7 +451,7 @@ func TestInteropTypesComparison(t *testing.T) { }) t.Run("b is Buffer", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { a := interop.` + typeName + `{` + ha + `} @@ -462,7 +462,7 @@ func TestInteropTypesComparison(t *testing.T) { }) t.Run("b is ByteString", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { a := interop.` + typeName + `{` + ha + `} @@ -473,7 +473,7 @@ func TestInteropTypesComparison(t *testing.T) { }) t.Run("b is compound type", func(t *testing.T) { src := `package foo - import "github.com/tutus-one/tutus-chain/pkg/interop" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" func Main() bool { a := interop.` + typeName + `{` + ha + `} diff --git a/pkg/compiler/testdata/compile/test.go b/pkg/compiler/testdata/compile/test.go index 11924db..8748789 100644 --- a/pkg/compiler/testdata/compile/test.go +++ b/pkg/compiler/testdata/compile/test.go @@ -1,6 +1,6 @@ package compile -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func Main() { runtime.Notify("Hello world!") diff --git a/pkg/compiler/testdata/globalvar/funccall/main.go b/pkg/compiler/testdata/globalvar/funccall/main.go index 55b0a3b..99bcc33 100644 --- a/pkg/compiler/testdata/globalvar/funccall/main.go +++ b/pkg/compiler/testdata/globalvar/funccall/main.go @@ -1,9 +1,9 @@ package funccall import ( - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested1" - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested2" - alias "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested3" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested1" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested2" + alias "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested3" ) // F should be called from the main package to check usage analyzer against diff --git a/pkg/compiler/testdata/globalvar/nested1/main.go b/pkg/compiler/testdata/globalvar/nested1/main.go index 812866e..913130d 100644 --- a/pkg/compiler/testdata/globalvar/nested1/main.go +++ b/pkg/compiler/testdata/globalvar/nested1/main.go @@ -1,8 +1,8 @@ package nested1 import ( - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested2" - alias "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested3" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested2" + alias "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/globalvar/nested3" ) // Unused shouldn't produce any code if unused. diff --git a/pkg/compiler/testdata/importcycle/pkg1/pkg1.go b/pkg/compiler/testdata/importcycle/pkg1/pkg1.go index 34ffa71..7fec3d6 100644 --- a/pkg/compiler/testdata/importcycle/pkg1/pkg1.go +++ b/pkg/compiler/testdata/importcycle/pkg1/pkg1.go @@ -1,6 +1,6 @@ package pkg1 -import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg2" +import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg2" var A int diff --git a/pkg/compiler/testdata/importcycle/pkg2/pkg2.go b/pkg/compiler/testdata/importcycle/pkg2/pkg2.go index f6115b2..7b2aef9 100644 --- a/pkg/compiler/testdata/importcycle/pkg2/pkg2.go +++ b/pkg/compiler/testdata/importcycle/pkg2/pkg2.go @@ -1,7 +1,7 @@ package pkg2 import ( - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg3" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg3" ) var A int diff --git a/pkg/compiler/testdata/importcycle/pkg3/pkg3.go b/pkg/compiler/testdata/importcycle/pkg3/pkg3.go index c98501d..e19003e 100644 --- a/pkg/compiler/testdata/importcycle/pkg3/pkg3.go +++ b/pkg/compiler/testdata/importcycle/pkg3/pkg3.go @@ -1,7 +1,7 @@ package pkg3 import ( - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg2" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/importcycle/pkg2" ) var A int diff --git a/pkg/compiler/testdata/inline/inline.go b/pkg/compiler/testdata/inline/inline.go index b020fd3..b0199f0 100644 --- a/pkg/compiler/testdata/inline/inline.go +++ b/pkg/compiler/testdata/inline/inline.go @@ -1,8 +1,8 @@ package inline import ( - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/a" - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/b" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/a" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/inline/b" ) func NoArgsNoReturn() {} diff --git a/pkg/compiler/testdata/nestedcall/call.go b/pkg/compiler/testdata/nestedcall/call.go index e4d6186..f92353e 100644 --- a/pkg/compiler/testdata/nestedcall/call.go +++ b/pkg/compiler/testdata/nestedcall/call.go @@ -1,6 +1,6 @@ package nestedcall -import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall/inner" +import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/nestedcall/inner" // X is what we use. const X = 42 diff --git a/pkg/compiler/testdata/notify/event.go b/pkg/compiler/testdata/notify/event.go index a1f8205..5317fbf 100644 --- a/pkg/compiler/testdata/notify/event.go +++ b/pkg/compiler/testdata/notify/event.go @@ -1,6 +1,6 @@ package notify -import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" // Value is the constant we use. const Value = 42 diff --git a/pkg/compiler/testdata/pkg1/pkg1.go b/pkg/compiler/testdata/pkg1/pkg1.go index 333257c..934b6f4 100644 --- a/pkg/compiler/testdata/pkg1/pkg1.go +++ b/pkg/compiler/testdata/pkg1/pkg1.go @@ -1,6 +1,6 @@ package pkg1 -import "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" +import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" func init() { pkg3.A = 1 diff --git a/pkg/compiler/testdata/pkg2/pkg2.go b/pkg/compiler/testdata/pkg2/pkg2.go index 75aaadc..40e4b43 100644 --- a/pkg/compiler/testdata/pkg2/pkg2.go +++ b/pkg/compiler/testdata/pkg2/pkg2.go @@ -1,7 +1,7 @@ package pkg2 import ( - "github.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/pkg3" ) func init() { diff --git a/pkg/compiler/testdata/runh/hash.go b/pkg/compiler/testdata/runh/hash.go index 11e9c18..4fd4674 100644 --- a/pkg/compiler/testdata/runh/hash.go +++ b/pkg/compiler/testdata/runh/hash.go @@ -1,6 +1,6 @@ package runh -import "github.com/tutus-one/tutus-chain/pkg/interop" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" // RuntimeHash possibly returns some hash at runtime. func RuntimeHash() interop.Hash160 { diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index e088c02..eea6880 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -5,7 +5,7 @@ import ( "go/types" "slices" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func (c *codegen) typeAndValueOf(e ast.Expr) types.TypeAndValue { diff --git a/pkg/compiler/vardecl_test.go b/pkg/compiler/vardecl_test.go index 65a0a08..074de60 100644 --- a/pkg/compiler/vardecl_test.go +++ b/pkg/compiler/vardecl_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) func TestGenDeclWithMultiRet(t *testing.T) { diff --git a/pkg/compiler/verify_test.go b/pkg/compiler/verify_test.go index 3da4368..c6f167b 100644 --- a/pkg/compiler/verify_test.go +++ b/pkg/compiler/verify_test.go @@ -4,9 +4,9 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -46,7 +46,7 @@ func getVerifyProg(pub, sig []byte) string { return ` package hello - import "github.com/tutus-one/tutus-chain/pkg/interop/crypto" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/crypto" func Main() bool { pub := ` + pubS + ` diff --git a/pkg/compiler/vm_test.go b/pkg/compiler/vm_test.go index dd47c41..ae95052 100644 --- a/pkg/compiler/vm_test.go +++ b/pkg/compiler/vm_test.go @@ -6,16 +6,16 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/config/application_config.go b/pkg/config/application_config.go index 45a6a10..51d23b4 100644 --- a/pkg/config/application_config.go +++ b/pkg/config/application_config.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" ) // ApplicationConfiguration config specific to the node. diff --git a/pkg/config/application_config_test.go b/pkg/config/application_config_test.go index 3b27d8d..fdba54d 100644 --- a/pkg/config/application_config_test.go +++ b/pkg/config/application_config_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/config/config.go b/pkg/config/config.go index 09a5aa6..6a383b1 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -8,8 +8,8 @@ import ( "path/filepath" "time" - "github.com/tutus-one/tutus-chain/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" "gopkg.in/yaml.v3" ) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 55a44d7..1f2253f 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -7,8 +7,8 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/pkg/config/genesis_extensions.go b/pkg/config/genesis_extensions.go index ac08b8d..2ba31cb 100644 --- a/pkg/config/genesis_extensions.go +++ b/pkg/config/genesis_extensions.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" "gopkg.in/yaml.v3" ) diff --git a/pkg/config/hardfork.go b/pkg/config/hardfork.go index 9ce66ce..8de409b 100644 --- a/pkg/config/hardfork.go +++ b/pkg/config/hardfork.go @@ -31,7 +31,7 @@ const ( // https://github.com/neo-project/neo/pull/3290). It makes the`node use // executing contract state for the contract call permissions check instead // of the state stored in the native Management. This change was introduced - // in [#3473](https://github.com/tutus-one/tutus-chain/pull/3473) and ported to + // in [#3473](https://git.marketally.com/tutus-one/tutus-chain/pull/3473) and ported to // the [reference](https://github.com/neo-project/neo/pull/3290). Also, this // hard-fork makes the System.Runtime.GetNotifications interop properly // count stack references of notification parameters which prevents users diff --git a/pkg/config/ledger_config.go b/pkg/config/ledger_config.go index 585d61f..e53852e 100644 --- a/pkg/config/ledger_config.go +++ b/pkg/config/ledger_config.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "gopkg.in/yaml.v3" ) diff --git a/pkg/config/ledger_config_test.go b/pkg/config/ledger_config_test.go index 0280819..8dd5baa 100644 --- a/pkg/config/ledger_config_test.go +++ b/pkg/config/ledger_config_test.go @@ -3,8 +3,8 @@ package config import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go index b9c5acd..cb10c6c 100644 --- a/pkg/config/protocol_config.go +++ b/pkg/config/protocol_config.go @@ -8,8 +8,8 @@ import ( "slices" "time" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" ) // ProtocolConfiguration represents the protocol config. diff --git a/pkg/config/protocol_config_test.go b/pkg/config/protocol_config_test.go index 03a2c5d..1e6c4da 100644 --- a/pkg/config/protocol_config_test.go +++ b/pkg/config/protocol_config_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/pkg/config/rpc_config.go b/pkg/config/rpc_config.go index d22e2a6..6e93895 100644 --- a/pkg/config/rpc_config.go +++ b/pkg/config/rpc_config.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" ) type ( diff --git a/pkg/consensus/block.go b/pkg/consensus/block.go index c6a4eda..0ed0df3 100644 --- a/pkg/consensus/block.go +++ b/pkg/consensus/block.go @@ -3,12 +3,12 @@ package consensus import ( "errors" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - coreb "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + coreb "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // neoBlock is a wrapper of a core.Block which implements diff --git a/pkg/consensus/block_test.go b/pkg/consensus/block_test.go index 9a55272..ed021af 100644 --- a/pkg/consensus/block_test.go +++ b/pkg/consensus/block_test.go @@ -3,11 +3,11 @@ package consensus import ( "testing" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/cache.go b/pkg/consensus/cache.go index 39148e7..609b3ae 100644 --- a/pkg/consensus/cache.go +++ b/pkg/consensus/cache.go @@ -4,7 +4,7 @@ import ( "container/list" "sync" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // relayCache is payload cache which is used to store diff --git a/pkg/consensus/cache_test.go b/pkg/consensus/cache_test.go index 71154a3..2d3cb56 100644 --- a/pkg/consensus/cache_test.go +++ b/pkg/consensus/cache_test.go @@ -3,7 +3,7 @@ package consensus import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/random" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/change_view.go b/pkg/consensus/change_view.go index 9cb875c..22125d4 100644 --- a/pkg/consensus/change_view.go +++ b/pkg/consensus/change_view.go @@ -1,8 +1,8 @@ package consensus import ( - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // changeView represents dBFT ChangeView message. diff --git a/pkg/consensus/change_view_test.go b/pkg/consensus/change_view_test.go index 836f6a9..606c1cf 100644 --- a/pkg/consensus/change_view_test.go +++ b/pkg/consensus/change_view_test.go @@ -3,7 +3,7 @@ package consensus import ( "testing" - "github.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-consensus" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/commit.go b/pkg/consensus/commit.go index 0f55602..ab0ed8a 100644 --- a/pkg/consensus/commit.go +++ b/pkg/consensus/commit.go @@ -1,8 +1,8 @@ package consensus import ( - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // commit represents dBFT Commit message. diff --git a/pkg/consensus/commit_test.go b/pkg/consensus/commit_test.go index 66aff1b..b3402f4 100644 --- a/pkg/consensus/commit_test.go +++ b/pkg/consensus/commit_test.go @@ -3,7 +3,7 @@ package consensus import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/random" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index 625638c..d889a48 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -7,24 +7,24 @@ import ( "sync/atomic" "time" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-consensus/timer" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - coreb "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - npayload "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-consensus/timer" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + coreb "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + npayload "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 545f9e9..6c87193 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -4,26 +4,26 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - coreb "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - npayload "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + coreb "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + npayload "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) diff --git a/pkg/consensus/crypto_test.go b/pkg/consensus/crypto_test.go index 997a2fb..a6b8e22 100644 --- a/pkg/consensus/crypto_test.go +++ b/pkg/consensus/crypto_test.go @@ -4,7 +4,7 @@ import ( "crypto/sha256" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/payload.go b/pkg/consensus/payload.go index 2f2398e..e4fd84b 100644 --- a/pkg/consensus/payload.go +++ b/pkg/consensus/payload.go @@ -3,13 +3,13 @@ package consensus import ( "fmt" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - npayload "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + npayload "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" ) type ( diff --git a/pkg/consensus/payload_test.go b/pkg/consensus/payload_test.go index aea6ab9..47631b4 100644 --- a/pkg/consensus/payload_test.go +++ b/pkg/consensus/payload_test.go @@ -6,16 +6,16 @@ import ( "math/rand/v2" "testing" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - npayload "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + npayload "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/prepare_request.go b/pkg/consensus/prepare_request.go index a6d3ca7..79c32d4 100644 --- a/pkg/consensus/prepare_request.go +++ b/pkg/consensus/prepare_request.go @@ -1,10 +1,10 @@ package consensus import ( - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // prepareRequest represents dBFT prepareRequest message. diff --git a/pkg/consensus/prepare_request_test.go b/pkg/consensus/prepare_request_test.go index e6c1df3..ba3f725 100644 --- a/pkg/consensus/prepare_request_test.go +++ b/pkg/consensus/prepare_request_test.go @@ -3,10 +3,10 @@ package consensus import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/prepare_response.go b/pkg/consensus/prepare_response.go index 06e81d3..bfc74d0 100644 --- a/pkg/consensus/prepare_response.go +++ b/pkg/consensus/prepare_response.go @@ -1,9 +1,9 @@ package consensus import ( - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // prepareResponse represents dBFT PrepareResponse message. diff --git a/pkg/consensus/prepare_response_test.go b/pkg/consensus/prepare_response_test.go index 024ac3f..bdc5ad5 100644 --- a/pkg/consensus/prepare_response_test.go +++ b/pkg/consensus/prepare_response_test.go @@ -3,7 +3,7 @@ package consensus import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/recovery_message.go b/pkg/consensus/recovery_message.go index 5a8e867..158e480 100644 --- a/pkg/consensus/recovery_message.go +++ b/pkg/consensus/recovery_message.go @@ -3,11 +3,11 @@ package consensus import ( "errors" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - npayload "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + npayload "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) type ( diff --git a/pkg/consensus/recovery_message_test.go b/pkg/consensus/recovery_message_test.go index 06d19f8..38a3233 100644 --- a/pkg/consensus/recovery_message_test.go +++ b/pkg/consensus/recovery_message_test.go @@ -3,11 +3,11 @@ package consensus import ( "testing" - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/consensus/recovery_request.go b/pkg/consensus/recovery_request.go index 1f275ab..145dff0 100644 --- a/pkg/consensus/recovery_request.go +++ b/pkg/consensus/recovery_request.go @@ -1,8 +1,8 @@ package consensus import ( - "github.com/tutus-one/tutus-consensus" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // recoveryRequest represents dBFT RecoveryRequest message. diff --git a/pkg/core/basic_chain_test.go b/pkg/core/basic_chain_test.go index f460a1c..f7828f8 100644 --- a/pkg/core/basic_chain_test.go +++ b/pkg/core/basic_chain_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/pkg/core/chaindump" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/chaindump" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/bench_test.go b/pkg/core/bench_test.go index 25883e9..c09023c 100644 --- a/pkg/core/bench_test.go +++ b/pkg/core/bench_test.go @@ -6,18 +6,18 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/block/block.go b/pkg/core/block/block.go index a4a6781..76b08d9 100644 --- a/pkg/core/block/block.go +++ b/pkg/core/block/block.go @@ -6,11 +6,11 @@ import ( "math" "math/big" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) const ( diff --git a/pkg/core/block/block_test.go b/pkg/core/block/block_test.go index 2179868..a9d9d15 100644 --- a/pkg/core/block/block_test.go +++ b/pkg/core/block/block_test.go @@ -7,13 +7,13 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/block/header.go b/pkg/core/block/header.go index 1f31eb2..475dc77 100644 --- a/pkg/core/block/header.go +++ b/pkg/core/block/header.go @@ -6,12 +6,12 @@ import ( "fmt" "strconv" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // VersionInitial is the default Neo block version. diff --git a/pkg/core/block/header_test.go b/pkg/core/block/header_test.go index 68827de..5fbea50 100644 --- a/pkg/core/block/header_test.go +++ b/pkg/core/block/header_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" ) diff --git a/pkg/core/block/helper_test.go b/pkg/core/block/helper_test.go index 363cb20..508db20 100644 --- a/pkg/core/block/helper_test.go +++ b/pkg/core/block/helper_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index a819b7c..7ffdb5c 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -13,38 +13,38 @@ import ( "time" lru "github.com/hashicorp/golang-lru/v2" - json "github.com/tutus-one/tutus-ordered-json" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/stateroot" - "github.com/tutus-one/tutus-chain/pkg/core/statesync" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + json "git.marketally.com/tutus-one/tutus-ordered-json" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/stateroot" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/statesync" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "go.uber.org/zap" ) diff --git a/pkg/core/blockchain_core_test.go b/pkg/core/blockchain_core_test.go index 036e193..d1fde02 100644 --- a/pkg/core/blockchain_core_test.go +++ b/pkg/core/blockchain_core_test.go @@ -9,18 +9,18 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/chaindump" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/chaindump" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index 9e6076a..80ce3f4 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -11,44 +11,44 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeprices" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeprices" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -1007,8 +1007,8 @@ func TestBlockchain_IsTxStillRelevant(t *testing.T) { t.Run("contract witness check fails", func(t *testing.T) { src := fmt.Sprintf(`package verify import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/lib/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/lib/address" ) func Verify() bool { addr := address.ToHash160("`+address.Uint160ToString(e.NativeHash(t, nativenames.Ledger))+`") @@ -2563,7 +2563,7 @@ func TestBlockchain_Bug1728(t *testing.T) { managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management)) src := `package example - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func init() { if true { } else { } } func _deploy(_ any, isUpdate bool) { runtime.Log("Deploy") @@ -2891,7 +2891,7 @@ func TestEngineLimits(t *testing.T) { args, _ := strings.CutSuffix(strings.Repeat(`"", `, eArgsCount), `, `) src := fmt.Sprintf(`package test import ( - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // args is an array of LargeEvent parameters containing 500 empty strings. var args = []any{%s}; @@ -2972,7 +2972,7 @@ func TestRuntimeNotifyRefcounting(t *testing.T) { args, _ := strings.CutSuffix(strings.Repeat(`"", `, eArgsCount), `, `) src := fmt.Sprintf(`package test import ( - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // args is an array of LargeEvent parameters containing 500 empty strings. var args = []any{%s}; diff --git a/pkg/core/chaindump/dump.go b/pkg/core/chaindump/dump.go index a208228..f323ed5 100644 --- a/pkg/core/chaindump/dump.go +++ b/pkg/core/chaindump/dump.go @@ -3,10 +3,10 @@ package chaindump import ( "fmt" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // DumperRestorer is an interface to get/add blocks from/to. diff --git a/pkg/core/chaindump/dump_test.go b/pkg/core/chaindump/dump_test.go index d40839b..653b0d5 100644 --- a/pkg/core/chaindump/dump_test.go +++ b/pkg/core/chaindump/dump_test.go @@ -4,13 +4,13 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/chaindump" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/chaindump" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/custom_native_test.go b/pkg/core/custom_native_test.go index fa2b434..b37db28 100644 --- a/pkg/core/custom_native_test.go +++ b/pkg/core/custom_native_test.go @@ -4,22 +4,22 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var ( diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 1c472db..66dc5ba 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -12,17 +12,17 @@ import ( "math/big" "sync" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // HasTransaction errors. diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index f0c076b..e8682a1 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -4,16 +4,16 @@ import ( "encoding/binary" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/fee/calculate.go b/pkg/core/fee/calculate.go index 597a67c..adfbc15 100644 --- a/pkg/core/fee/calculate.go +++ b/pkg/core/fee/calculate.go @@ -1,11 +1,11 @@ package fee import ( - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // ECDSAVerifyPrice is a gas price of a single verification. diff --git a/pkg/core/fee/opcode.go b/pkg/core/fee/opcode.go index 2e74b9d..bda35c3 100644 --- a/pkg/core/fee/opcode.go +++ b/pkg/core/fee/opcode.go @@ -1,7 +1,7 @@ package fee import ( - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // Opcode returns the deployment coefficients of the specified opcodes. diff --git a/pkg/core/fee/opcode_test.go b/pkg/core/fee/opcode_test.go index fdffd57..c5f59d3 100644 --- a/pkg/core/fee/opcode_test.go +++ b/pkg/core/fee/opcode_test.go @@ -3,7 +3,7 @@ package fee import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) const feeFactor = 30 diff --git a/pkg/core/headerhashes.go b/pkg/core/headerhashes.go index d5fe648..c722d74 100644 --- a/pkg/core/headerhashes.go +++ b/pkg/core/headerhashes.go @@ -6,10 +6,10 @@ import ( "sync" lru "github.com/hashicorp/golang-lru/v2" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index a1689d0..59c1532 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -4,13 +4,13 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zaptest" diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 6854ece..c5261a0 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -8,25 +8,25 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "go.uber.org/zap" ) diff --git a/pkg/core/interop/context_test.go b/pkg/core/interop/context_test.go index 9f5c967..18b77d9 100644 --- a/pkg/core/interop/context_test.go +++ b/pkg/core/interop/context_test.go @@ -3,9 +3,9 @@ package interop import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/contract/account.go b/pkg/core/interop/contract/account.go index 48f6d89..5ca37de 100644 --- a/pkg/core/interop/contract/account.go +++ b/pkg/core/interop/contract/account.go @@ -5,13 +5,13 @@ import ( "errors" "math" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // CreateMultisigAccount calculates multisig contract scripthash for a diff --git a/pkg/core/interop/contract/account_test.go b/pkg/core/interop/contract/account_test.go index b59eefe..cf5f0e4 100644 --- a/pkg/core/interop/contract/account_test.go +++ b/pkg/core/interop/contract/account_test.go @@ -6,17 +6,17 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/contract/call.go b/pkg/core/interop/contract/call.go index b6467d9..a0577c8 100644 --- a/pkg/core/interop/contract/call.go +++ b/pkg/core/interop/contract/call.go @@ -7,16 +7,16 @@ import ( "math/big" "strings" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // LoadToken calls method specified by the token id. diff --git a/pkg/core/interop/contract/call_test.go b/pkg/core/interop/contract/call_test.go index 4c07ff4..8173769 100644 --- a/pkg/core/interop/contract/call_test.go +++ b/pkg/core/interop/contract/call_test.go @@ -8,25 +8,25 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -203,9 +203,9 @@ func TestSystemContractCall_Permissions(t *testing.T) { // calls contract's A after B's update. srcB := `package contractB import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" ) func CallRetOne() int { res := contract.Call(interop.Hash160{` + hashAStr + `}, "retOne", contract.All).(int) @@ -319,11 +319,11 @@ func TestSnapshotIsolation_Exceptions(t *testing.T) { // Contract A puts value in the storage, emits notifications and panics. srcA := `package contractA import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) - func DoAndPanic(key, value []byte, nNtf int) int { // avoid https://github.com/tutus-one/tutus-chain/issues/2509 + func DoAndPanic(key, value []byte, nNtf int) int { // avoid https://git.marketally.com/tutus-one/tutus-chain/issues/2509 c := storage.GetContext() storage.Put(c, key, value) for i := 0; i < nNtf; i++ { @@ -378,11 +378,11 @@ func TestSnapshotIsolation_Exceptions(t *testing.T) { // and storage changes are available from different contexts. srcB := `package contractB import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) var caughtKey = []byte("caught") func DoAndCatch(shouldRecover bool, keyA, valueA, keyB, valueB []byte, nNtfA, nNtfB1, nNtfB2 int) { @@ -469,8 +469,8 @@ func TestSnapshotIsolation_NestedContextException(t *testing.T) { srcA := `package contractA import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) func CallA() { runtime.Notify("Calling A") @@ -533,9 +533,9 @@ func TestSnapshotIsolation_CallToItself(t *testing.T) { // Contract A calls method of self and throws if storage changes made by Do are unavailable after call to it. srcA := `package contractA import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) var key = []byte("key") func Test() { @@ -577,7 +577,7 @@ func TestSnapshotIsolation_CallToItself(t *testing.T) { ctrInvoker.Invoke(t, stackitem.Null{}, "check") } -// This test is written to check https://github.com/tutus-one/tutus-chain/issues/2509 +// This test is written to check https://git.marketally.com/tutus-one/tutus-chain/issues/2509 // and https://github.com/neo-project/neo/pull/2745#discussion_r879167180. func TestRET_after_FINALLY_PanicInsideVoidMethod(t *testing.T) { bc, acc := chain.NewSingle(t) @@ -608,8 +608,8 @@ func TestRET_after_FINALLY_PanicInsideVoidMethod(t *testing.T) { // Contract B calls A and catches the exception thrown by A. srcB := `package contractB import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" ) func Catch() { defer func() { @@ -643,7 +643,7 @@ func TestRET_after_FINALLY_CallNonVoidAfterVoidMethod(t *testing.T) { // Contract A has two methods. One of them has no return value, and the other has it. srcA := `package contractA - import "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" func NoRet() { runtime.Log("no ret") } @@ -668,9 +668,9 @@ func TestRET_after_FINALLY_CallNonVoidAfterVoidMethod(t *testing.T) { // Contract B calls A in try-catch block. srcB := `package contractB import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) func CallAInTryCatch() { defer func() { diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go index 05db841..f0050f3 100644 --- a/pkg/core/interop/crypto/ecdsa.go +++ b/pkg/core/interop/crypto/ecdsa.go @@ -5,12 +5,12 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using diff --git a/pkg/core/interop/crypto/ecdsa_test.go b/pkg/core/interop/crypto/ecdsa_test.go index ab4df19..6469ac5 100644 --- a/pkg/core/interop/crypto/ecdsa_test.go +++ b/pkg/core/interop/crypto/ecdsa_test.go @@ -5,21 +5,21 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/crypto/interop.go b/pkg/core/interop/crypto/interop.go index c2a00e0..189e9f1 100644 --- a/pkg/core/interop/crypto/interop.go +++ b/pkg/core/interop/crypto/interop.go @@ -1,8 +1,8 @@ package crypto import ( - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" ) var ( diff --git a/pkg/core/interop/gas_price.go b/pkg/core/interop/gas_price.go index 924c18e..e8fbadd 100644 --- a/pkg/core/interop/gas_price.go +++ b/pkg/core/interop/gas_price.go @@ -1,8 +1,8 @@ package interop import ( - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // GetPrice returns a price for executing op with the provided parameter in diff --git a/pkg/core/interop/iterator/interop.go b/pkg/core/interop/iterator/interop.go index a0da0ce..77cacc3 100644 --- a/pkg/core/interop/iterator/interop.go +++ b/pkg/core/interop/iterator/interop.go @@ -1,8 +1,8 @@ package iterator import ( - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) type iterator interface { diff --git a/pkg/core/interop/iterator/interop_test.go b/pkg/core/interop/iterator/interop_test.go index 941c620..4d73ec0 100644 --- a/pkg/core/interop/iterator/interop_test.go +++ b/pkg/core/interop/iterator/interop_test.go @@ -4,9 +4,9 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/runtime/engine.go b/pkg/core/interop/runtime/engine.go index 88b4ade..3389fe2 100644 --- a/pkg/core/interop/runtime/engine.go +++ b/pkg/core/interop/runtime/engine.go @@ -5,12 +5,12 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "go.uber.org/zap" ) diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go index 076ec96..1d9576f 100644 --- a/pkg/core/interop/runtime/engine_test.go +++ b/pkg/core/interop/runtime/engine_test.go @@ -5,16 +5,16 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/pkg/core/interop/runtime/ext_test.go b/pkg/core/interop/runtime/ext_test.go index a230b7c..43aedb2 100644 --- a/pkg/core/interop/runtime/ext_test.go +++ b/pkg/core/interop/runtime/ext_test.go @@ -8,35 +8,35 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/runtime/util.go b/pkg/core/interop/runtime/util.go index abd755a..b69d58f 100644 --- a/pkg/core/interop/runtime/util.go +++ b/pkg/core/interop/runtime/util.go @@ -6,13 +6,13 @@ import ( "math/big" "slices" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/twmb/murmur3" ) diff --git a/pkg/core/interop/runtime/util_test.go b/pkg/core/interop/runtime/util_test.go index 3d13749..b6206fb 100644 --- a/pkg/core/interop/runtime/util_test.go +++ b/pkg/core/interop/runtime/util_test.go @@ -4,13 +4,13 @@ import ( "encoding/hex" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/runtime/witness.go b/pkg/core/interop/runtime/witness.go index 6c5df93..2c0a616 100644 --- a/pkg/core/interop/runtime/witness.go +++ b/pkg/core/interop/runtime/witness.go @@ -6,14 +6,14 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // CheckHashedWitness checks the given hash against the current list of script hashes diff --git a/pkg/core/interop/storage/basic.go b/pkg/core/interop/storage/basic.go index 90c6791..0283503 100644 --- a/pkg/core/interop/storage/basic.go +++ b/pkg/core/interop/storage/basic.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var ( diff --git a/pkg/core/interop/storage/bench_test.go b/pkg/core/interop/storage/bench_test.go index 3ea4b2c..30ebb67 100644 --- a/pkg/core/interop/storage/bench_test.go +++ b/pkg/core/interop/storage/bench_test.go @@ -4,12 +4,12 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/interop/iterator" - istorage "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/iterator" + istorage "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/storage/find.go b/pkg/core/interop/storage/find.go index 87908f9..c8fec75 100644 --- a/pkg/core/interop/storage/find.go +++ b/pkg/core/interop/storage/find.go @@ -6,9 +6,9 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Storage iterator options. diff --git a/pkg/core/interop/storage/interops_test.go b/pkg/core/interop/storage/interops_test.go index 8cc6e44..5a7b5d1 100644 --- a/pkg/core/interop/storage/interops_test.go +++ b/pkg/core/interop/storage/interops_test.go @@ -5,8 +5,8 @@ import ( "runtime" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/interop/storage/storage_test.go b/pkg/core/interop/storage/storage_test.go index c6b6769..8e9aecd 100644 --- a/pkg/core/interop/storage/storage_test.go +++ b/pkg/core/interop/storage/storage_test.go @@ -5,26 +5,26 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/iterator" - istorage "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/iterator" + istorage "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -348,8 +348,8 @@ func TestStorage_LocalInteropAPI(t *testing.T) { src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" ) func LocalPut() { diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 21b780a..f63c881 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -8,18 +8,18 @@ package core */ import ( - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/core/interop/crypto" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" ) // SpawnVM returns a VM with script getter and interop functions set diff --git a/pkg/core/mempool/bench_test.go b/pkg/core/mempool/bench_test.go index 61f4c69..cf13b7c 100644 --- a/pkg/core/mempool/bench_test.go +++ b/pkg/core/mempool/bench_test.go @@ -3,8 +3,8 @@ package mempool import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/core/mempool/feer.go b/pkg/core/mempool/feer.go index f79788f..65690a3 100644 --- a/pkg/core/mempool/feer.go +++ b/pkg/core/mempool/feer.go @@ -3,7 +3,7 @@ package mempool import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Feer is an interface that abstracts the implementation of the fee calculation. diff --git a/pkg/core/mempool/mem_pool.go b/pkg/core/mempool/mem_pool.go index 58e7d8e..41ae1c9 100644 --- a/pkg/core/mempool/mem_pool.go +++ b/pkg/core/mempool/mem_pool.go @@ -9,9 +9,9 @@ import ( "sync/atomic" "github.com/holiman/uint256" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) var ( diff --git a/pkg/core/mempool/mem_pool_test.go b/pkg/core/mempool/mem_pool_test.go index 586787b..c6a458d 100644 --- a/pkg/core/mempool/mem_pool_test.go +++ b/pkg/core/mempool/mem_pool_test.go @@ -9,10 +9,10 @@ import ( "time" "github.com/holiman/uint256" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/mempool/subscriptions.go b/pkg/core/mempool/subscriptions.go index 1b88a58..cf00e70 100644 --- a/pkg/core/mempool/subscriptions.go +++ b/pkg/core/mempool/subscriptions.go @@ -1,6 +1,6 @@ package mempool -import "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" +import "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" // RunSubscriptions runs subscriptions goroutine if mempool subscriptions are enabled. // You should manually free the resources by calling StopSubscriptions on mempool shutdown. diff --git a/pkg/core/mempool/subscriptions_test.go b/pkg/core/mempool/subscriptions_test.go index c0fc40c..21fab0b 100644 --- a/pkg/core/mempool/subscriptions_test.go +++ b/pkg/core/mempool/subscriptions_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/mempoolevent/event.go b/pkg/core/mempoolevent/event.go index c389221..e9aad83 100644 --- a/pkg/core/mempoolevent/event.go +++ b/pkg/core/mempoolevent/event.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" ) // Type represents mempool event type. diff --git a/pkg/core/mpt/base.go b/pkg/core/mpt/base.go index 994fb90..f1609da 100644 --- a/pkg/core/mpt/base.go +++ b/pkg/core/mpt/base.go @@ -3,9 +3,9 @@ package mpt import ( "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // BaseNode implements basic things every node needs like caching hash and diff --git a/pkg/core/mpt/batch_test.go b/pkg/core/mpt/batch_test.go index 6ea7e62..088d946 100644 --- a/pkg/core/mpt/batch_test.go +++ b/pkg/core/mpt/batch_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/mpt/bench_test.go b/pkg/core/mpt/bench_test.go index ad8dd60..ec2df95 100644 --- a/pkg/core/mpt/bench_test.go +++ b/pkg/core/mpt/bench_test.go @@ -3,7 +3,7 @@ package mpt import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/random" ) func benchmarkBytes(b *testing.B, n Node) { diff --git a/pkg/core/mpt/billet.go b/pkg/core/mpt/billet.go index ea73420..5550e29 100644 --- a/pkg/core/mpt/billet.go +++ b/pkg/core/mpt/billet.go @@ -6,9 +6,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // DummySTTempStoragePrefix is a dummy contract storage item prefix that may be diff --git a/pkg/core/mpt/billet_test.go b/pkg/core/mpt/billet_test.go index b4bf93f..f9c9d91 100644 --- a/pkg/core/mpt/billet_test.go +++ b/pkg/core/mpt/billet_test.go @@ -4,9 +4,9 @@ import ( "encoding/binary" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/mpt/branch.go b/pkg/core/mpt/branch.go index 744630e..b063c2f 100644 --- a/pkg/core/mpt/branch.go +++ b/pkg/core/mpt/branch.go @@ -4,8 +4,8 @@ import ( "encoding/json" "errors" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/core/mpt/empty.go b/pkg/core/mpt/empty.go index 2030605..947a7a6 100644 --- a/pkg/core/mpt/empty.go +++ b/pkg/core/mpt/empty.go @@ -4,8 +4,8 @@ import ( "encoding/json" "errors" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // EmptyNode represents an empty node. diff --git a/pkg/core/mpt/extension.go b/pkg/core/mpt/extension.go index 24d3da5..cd2e18f 100644 --- a/pkg/core/mpt/extension.go +++ b/pkg/core/mpt/extension.go @@ -6,9 +6,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/core/mpt/hash.go b/pkg/core/mpt/hash.go index 5f6872a..f3eec3b 100644 --- a/pkg/core/mpt/hash.go +++ b/pkg/core/mpt/hash.go @@ -3,8 +3,8 @@ package mpt import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // HashNode represents an MPT's hash node. diff --git a/pkg/core/mpt/helpers.go b/pkg/core/mpt/helpers.go index fa68d4b..07ccf23 100644 --- a/pkg/core/mpt/helpers.go +++ b/pkg/core/mpt/helpers.go @@ -3,7 +3,7 @@ package mpt import ( "slices" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // lcp returns the longest common prefix of a and b. diff --git a/pkg/core/mpt/helpers_test.go b/pkg/core/mpt/helpers_test.go index 3e59cbd..89530c9 100644 --- a/pkg/core/mpt/helpers_test.go +++ b/pkg/core/mpt/helpers_test.go @@ -3,7 +3,7 @@ package mpt import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/mpt/leaf.go b/pkg/core/mpt/leaf.go index 7522132..8705ead 100644 --- a/pkg/core/mpt/leaf.go +++ b/pkg/core/mpt/leaf.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // MaxValueLength is the max length of a leaf node value. diff --git a/pkg/core/mpt/node.go b/pkg/core/mpt/node.go index defab46..6d9d2b8 100644 --- a/pkg/core/mpt/node.go +++ b/pkg/core/mpt/node.go @@ -5,8 +5,8 @@ import ( "encoding/json" "errors" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // NodeType represents a node type.. diff --git a/pkg/core/mpt/node_test.go b/pkg/core/mpt/node_test.go index 4f24775..c854b41 100644 --- a/pkg/core/mpt/node_test.go +++ b/pkg/core/mpt/node_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/mpt/proof.go b/pkg/core/mpt/proof.go index 1654f79..b01f0c1 100644 --- a/pkg/core/mpt/proof.go +++ b/pkg/core/mpt/proof.go @@ -4,9 +4,9 @@ import ( "bytes" "errors" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // GetProof returns a proof that the key belongs to t. diff --git a/pkg/core/mpt/trie.go b/pkg/core/mpt/trie.go index 0f6e47c..393a50b 100644 --- a/pkg/core/mpt/trie.go +++ b/pkg/core/mpt/trie.go @@ -7,9 +7,9 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // TrieMode is the storage mode of a trie, it affects the DB scheme. diff --git a/pkg/core/mpt/trie_store.go b/pkg/core/mpt/trie_store.go index 50ae55a..e52aaef 100644 --- a/pkg/core/mpt/trie_store.go +++ b/pkg/core/mpt/trie_store.go @@ -6,8 +6,8 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // TrieStore is an MPT-based storage implementation for storing and retrieving diff --git a/pkg/core/mpt/trie_store_test.go b/pkg/core/mpt/trie_store_test.go index 11e8aef..eb56e5d 100644 --- a/pkg/core/mpt/trie_store_test.go +++ b/pkg/core/mpt/trie_store_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/mpt/trie_test.go b/pkg/core/mpt/trie_test.go index c6bd67b..6107898 100644 --- a/pkg/core/mpt/trie_test.go +++ b/pkg/core/mpt/trie_test.go @@ -3,8 +3,8 @@ package mpt import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/ancora.go b/pkg/core/native/ancora.go index da4c1f8..f26690e 100644 --- a/pkg/core/native/ancora.go +++ b/pkg/core/native/ancora.go @@ -1,1307 +1,1307 @@ -package native - -import ( - "errors" - "fmt" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Ancora represents the Ancora native contract for anchoring Merkle roots of off-chain data. -// Latin: "ancora" = anchor - anchors off-chain data to on-chain verification. -type Ancora struct { - interop.ContractMD - Vita IVita - Tutus ITutus - Audit *AuditLogger // ARCH-005: Comprehensive audit logging -} - -// AncoraCache holds cached configuration for the Ancora contract. -type AncoraCache struct { - config state.StateAnchorsConfig -} - -// Storage prefixes for Ancora contract. -const ( - ancoraConfigPrefix = 0x01 - ancoraRootPrefix = 0x10 // vitaID + dataType -> RootInfo - ancoraHistoryPrefix = 0x11 // vitaID + dataType + version -> RootInfo - ancoraProviderPrefix = 0x20 // dataType + provider -> ProviderConfig - ancoraErasurePrefix = 0x30 // vitaID + dataType -> ErasureInfo - ancoraUpdateCountPrefix = 0x40 // blockHeight + provider -> count - ancoraLastUpdatePrefix = 0x41 // vitaID + dataType + provider -> blockHeight - ancoraAttestationPrefix = 0x50 // attestationHash -> AttestationInfo -) - -// Errors for Ancora contract. -var ( - ErrAncoraInvalidDataType = errors.New("invalid data type") - ErrAncoraInvalidRoot = errors.New("invalid Merkle root: must be 32 bytes") - ErrAncoraProviderNotFound = errors.New("provider not found") - ErrAncoraProviderInactive = errors.New("provider is inactive") - ErrAncoraProviderExists = errors.New("provider already registered") - ErrAncoraUnauthorized = errors.New("unauthorized: caller is not authorized provider") - ErrAncoraRateLimited = errors.New("rate limit exceeded") - ErrAncoraUpdateCooldown = errors.New("update cooldown not elapsed") - ErrAncoraNoRoot = errors.New("no root found for vitaID and dataType") - ErrAncoraInvalidProof = errors.New("invalid Merkle proof") - ErrAncoraProofTooDeep = errors.New("proof exceeds maximum depth") - ErrAncoraVersionNotFound = errors.New("version not found in history") - ErrAncoraErasurePending = errors.New("erasure already pending") - ErrAncoraErasureNotPending = errors.New("no pending erasure request") - ErrAncoraErasureGracePeriod = errors.New("erasure grace period not elapsed") - ErrAncoraInvalidAttestation = errors.New("invalid attestation") - ErrAncoraAttestationExpired = errors.New("attestation has expired") - ErrAncoraVitaNotFound = errors.New("vita not found") -) - -var ( - _ interop.Contract = (*Ancora)(nil) - _ dao.NativeContractCache = (*AncoraCache)(nil) -) - -// Copy implements NativeContractCache interface. -func (c *AncoraCache) Copy() dao.NativeContractCache { - cp := &AncoraCache{config: c.config} - return cp -} - -// newAncora returns a new Ancora native contract. -func newAncora() *Ancora { - a := &Ancora{ - ContractMD: *interop.NewContractMD(nativenames.Ancora, nativeids.Ancora, nil), - Audit: NewAuditLogger(nativeids.Ancora), - } - defer a.BuildHFSpecificMD(a.ActiveIn()) - - // Configuration methods - desc := NewDescriptor("getConfig", smartcontract.ArrayType) - md := NewMethodAndPrice(a.getConfig, 1<<15, callflag.ReadStates) - a.AddMethod(md, desc) - - desc = NewDescriptor("setConfig", smartcontract.VoidType, - manifest.NewParameter("config", smartcontract.ArrayType)) - md = NewMethodAndPrice(a.setConfig, 1<<15, callflag.States) - a.AddMethod(md, desc) - - // Provider management - desc = NewDescriptor("registerProvider", smartcontract.BoolType, - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("provider", smartcontract.Hash160Type), - manifest.NewParameter("description", smartcontract.StringType), - manifest.NewParameter("maxUpdatesPerBlock", smartcontract.IntegerType), - manifest.NewParameter("updateCooldown", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.registerProvider, 1<<15, callflag.States) - a.AddMethod(md, desc) - - desc = NewDescriptor("revokeProvider", smartcontract.BoolType, - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("provider", smartcontract.Hash160Type)) - md = NewMethodAndPrice(a.revokeProvider, 1<<15, callflag.States) - a.AddMethod(md, desc) - - desc = NewDescriptor("getProvider", smartcontract.ArrayType, - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("provider", smartcontract.Hash160Type)) - md = NewMethodAndPrice(a.getProvider, 1<<15, callflag.ReadStates) - a.AddMethod(md, desc) - - desc = NewDescriptor("isProviderActive", smartcontract.BoolType, - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("provider", smartcontract.Hash160Type)) - md = NewMethodAndPrice(a.isProviderActive, 1<<15, callflag.ReadStates) - a.AddMethod(md, desc) - - // Root management - desc = NewDescriptor("updateDataRoot", smartcontract.BoolType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("root", smartcontract.ByteArrayType), - manifest.NewParameter("leafCount", smartcontract.IntegerType), - manifest.NewParameter("treeAlgorithm", smartcontract.IntegerType), - manifest.NewParameter("schemaVersion", smartcontract.StringType), - manifest.NewParameter("contentHash", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(a.updateDataRoot, 1<<16, callflag.States) - a.AddMethod(md, desc) - - desc = NewDescriptor("getDataRoot", smartcontract.ArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.getDataRoot, 1<<15, callflag.ReadStates) - a.AddMethod(md, desc) - - desc = NewDescriptor("getDataRootAtVersion", smartcontract.ArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("version", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.getDataRootAtVersion, 1<<15, callflag.ReadStates) - a.AddMethod(md, desc) - - // Proof verification - desc = NewDescriptor("verifyProof", smartcontract.BoolType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("leaf", smartcontract.ByteArrayType), - manifest.NewParameter("proof", smartcontract.ArrayType), - manifest.NewParameter("index", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.verifyProof, 1<<16, callflag.ReadStates) - a.AddMethod(md, desc) - - desc = NewDescriptor("verifyProofAtVersion", smartcontract.BoolType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("version", smartcontract.IntegerType), - manifest.NewParameter("leaf", smartcontract.ByteArrayType), - manifest.NewParameter("proof", smartcontract.ArrayType), - manifest.NewParameter("index", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.verifyProofAtVersion, 1<<16, callflag.ReadStates) - a.AddMethod(md, desc) - - // GDPR erasure - desc = NewDescriptor("requestErasure", smartcontract.BoolType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(a.requestErasure, 1<<15, callflag.States) - a.AddMethod(md, desc) - - desc = NewDescriptor("confirmErasure", smartcontract.BoolType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.confirmErasure, 1<<15, callflag.States) - a.AddMethod(md, desc) - - desc = NewDescriptor("denyErasure", smartcontract.BoolType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(a.denyErasure, 1<<15, callflag.States) - a.AddMethod(md, desc) - - desc = NewDescriptor("getErasureInfo", smartcontract.ArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataType", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.getErasureInfo, 1<<15, callflag.ReadStates) - a.AddMethod(md, desc) - - // Data portability - desc = NewDescriptor("generatePortabilityAttestation", smartcontract.ByteArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("dataTypes", smartcontract.ArrayType)) - md = NewMethodAndPrice(a.generatePortabilityAttestation, 1<<16, callflag.States) - a.AddMethod(md, desc) - - desc = NewDescriptor("verifyPortabilityAttestation", smartcontract.BoolType, - manifest.NewParameter("attestation", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(a.verifyPortabilityAttestation, 1<<15, callflag.ReadStates) - a.AddMethod(md, desc) - - // Audit log query (ARCH-005) - desc = NewDescriptor("getAuditLog", smartcontract.ArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("startBlock", smartcontract.IntegerType), - manifest.NewParameter("endBlock", smartcontract.IntegerType), - manifest.NewParameter("limit", smartcontract.IntegerType)) - md = NewMethodAndPrice(a.getAuditLog, 1<<16, callflag.ReadStates) - a.AddMethod(md, desc) - - return a -} - -// Metadata implements the Contract interface. -func (a *Ancora) Metadata() *interop.ContractMD { - return &a.ContractMD -} - -// Initialize initializes Ancora native contract and implements the Contract interface. -func (a *Ancora) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != a.ActiveIn() { - return nil - } - - cfg := state.DefaultStateAnchorsConfig() - if err := a.putConfig(ic.DAO, &cfg); err != nil { - return fmt.Errorf("failed to initialize config: %w", err) - } - - cache := &AncoraCache{config: cfg} - ic.DAO.SetCache(a.ID, cache) - return nil -} - -// InitializeCache implements the Contract interface. -func (a *Ancora) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cfg, err := a.loadConfig(d) - if err != nil { - return fmt.Errorf("failed to load config: %w", err) - } - - cache := &AncoraCache{config: *cfg} - d.SetCache(a.ID, cache) - return nil -} - -// OnPersist implements the Contract interface. -func (a *Ancora) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (a *Ancora) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn implements the Contract interface. -func (a *Ancora) ActiveIn() *config.Hardfork { - return nil // Active from genesis -} - -// Address returns the contract's script hash. -func (a *Ancora) Address() util.Uint160 { - return a.Hash -} - -// ========== Configuration Methods ========== - -func (a *Ancora) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(a.ID).(*AncoraCache) - cfg := cache.config - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(cfg.DefaultTreeAlgorithm))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.MaxProofDepth))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.DefaultMaxUpdatesPerBlock))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.DefaultUpdateCooldown))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.MaxHistoryVersions))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.ErasureGracePeriod))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.AttestationValidBlocks))), - }) -} - -func (a *Ancora) setConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !a.Tutus.CheckCommittee(ic) { - panic("invalid committee signature") - } - - arr, ok := args[0].Value().([]stackitem.Item) - if !ok || len(arr) != 7 { - panic("invalid config array") - } - - cfg := state.StateAnchorsConfig{ - DefaultTreeAlgorithm: state.TreeAlgorithm(toUint32(arr[0])), - MaxProofDepth: toUint32(arr[1]), - DefaultMaxUpdatesPerBlock: toUint32(arr[2]), - DefaultUpdateCooldown: toUint32(arr[3]), - MaxHistoryVersions: toUint32(arr[4]), - ErasureGracePeriod: toUint32(arr[5]), - AttestationValidBlocks: toUint32(arr[6]), - } - - if err := a.putConfig(ic.DAO, &cfg); err != nil { - panic(fmt.Errorf("failed to save config: %w", err)) - } - - cache := ic.DAO.GetRWCache(a.ID).(*AncoraCache) - cache.config = cfg - return stackitem.Null{} -} - -func (a *Ancora) putConfig(d *dao.Simple, cfg *state.StateAnchorsConfig) error { - key := []byte{ancoraConfigPrefix} - data := make([]byte, 28) // 7 * 4 bytes - // Simple serialization - putUint32(data[0:4], uint32(cfg.DefaultTreeAlgorithm)) - putUint32(data[4:8], cfg.MaxProofDepth) - putUint32(data[8:12], cfg.DefaultMaxUpdatesPerBlock) - putUint32(data[12:16], cfg.DefaultUpdateCooldown) - putUint32(data[16:20], cfg.MaxHistoryVersions) - putUint32(data[20:24], cfg.ErasureGracePeriod) - putUint32(data[24:28], cfg.AttestationValidBlocks) - d.PutStorageItem(a.ID, key, data) - return nil -} - -func (a *Ancora) loadConfig(d *dao.Simple) (*state.StateAnchorsConfig, error) { - key := []byte{ancoraConfigPrefix} - data := d.GetStorageItem(a.ID, key) - if data == nil { - return nil, errors.New("config not found") - } - if len(data) != 28 { - return nil, errors.New("invalid config data length") - } - cfg := &state.StateAnchorsConfig{ - DefaultTreeAlgorithm: state.TreeAlgorithm(getUint32(data[0:4])), - MaxProofDepth: getUint32(data[4:8]), - DefaultMaxUpdatesPerBlock: getUint32(data[8:12]), - DefaultUpdateCooldown: getUint32(data[12:16]), - MaxHistoryVersions: getUint32(data[16:20]), - ErasureGracePeriod: getUint32(data[20:24]), - AttestationValidBlocks: getUint32(data[24:28]), - } - return cfg, nil -} - -// ========== Provider Management ========== - -func (a *Ancora) registerProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !a.Tutus.CheckCommittee(ic) { - panic("invalid committee signature") - } - - dataType := state.DataType(toUint32(args[0])) - if dataType > state.DataTypeCustom { - panic(ErrAncoraInvalidDataType) - } - // Note: Valid types are 0-5 (Medical, Education, Investment, Documents, Presence, Custom) - - provider := toUint160(args[1]) - description := toString(args[2]) - maxUpdatesPerBlock := toUint32(args[3]) - updateCooldown := toUint32(args[4]) - - // Check if provider already exists - existing := a.getProviderConfig(ic.DAO, dataType, provider) - if existing != nil { - panic(ErrAncoraProviderExists) - } - - cfg := &state.ProviderConfig{ - DataType: dataType, - Provider: provider, - Description: description, - RegisteredAt: ic.BlockHeight(), - Active: true, - MaxUpdatesPerBlock: maxUpdatesPerBlock, - UpdateCooldown: updateCooldown, - } - - if err := a.putProviderConfig(ic.DAO, cfg); err != nil { - panic(fmt.Errorf("failed to register provider: %w", err)) - } - - // ARCH-005: Audit log provider registration - a.Audit.LogGovernance(ic.DAO, ic.VM.GetCallingScriptHash(), "ancora.provider_registered", - fmt.Sprintf("dataType=%d provider=%s desc=%s", dataType, provider.StringLE(), description), - AuditOutcomeSuccess, ic.BlockHeight()) - - ic.AddNotification(a.Hash, "ProviderRegistered", stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(dataType))), - stackitem.NewByteArray(provider.BytesBE()), - stackitem.NewByteArray([]byte(description)), - })) - - return stackitem.NewBool(true) -} - -func (a *Ancora) revokeProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !a.Tutus.CheckCommittee(ic) { - panic("invalid committee signature") - } - - dataType := state.DataType(toUint32(args[0])) - provider := toUint160(args[1]) - - cfg := a.getProviderConfig(ic.DAO, dataType, provider) - if cfg == nil { - panic(ErrAncoraProviderNotFound) - } - - cfg.Active = false - if err := a.putProviderConfig(ic.DAO, cfg); err != nil { - panic(fmt.Errorf("failed to revoke provider: %w", err)) - } - - // ARCH-005: Audit log provider revocation - a.Audit.LogGovernance(ic.DAO, ic.VM.GetCallingScriptHash(), "ancora.provider_revoked", - fmt.Sprintf("dataType=%d provider=%s", dataType, provider.StringLE()), - AuditOutcomeSuccess, ic.BlockHeight()) - - ic.AddNotification(a.Hash, "ProviderRevoked", stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(dataType))), - stackitem.NewByteArray(provider.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (a *Ancora) getProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { - dataType := state.DataType(toUint32(args[0])) - provider := toUint160(args[1]) - - cfg := a.getProviderConfig(ic.DAO, dataType, provider) - if cfg == nil { - return stackitem.Null{} - } - item, err := cfg.ToStackItem() - if err != nil { - panic(fmt.Errorf("failed to convert provider config to stack item: %w", err)) - } - return item -} - -func (a *Ancora) isProviderActive(ic *interop.Context, args []stackitem.Item) stackitem.Item { - dataType := state.DataType(toUint32(args[0])) - provider := toUint160(args[1]) - - cfg := a.getProviderConfig(ic.DAO, dataType, provider) - return stackitem.NewBool(cfg != nil && cfg.Active) -} - -func (a *Ancora) getProviderConfig(d *dao.Simple, dataType state.DataType, provider util.Uint160) *state.ProviderConfig { - key := append([]byte{ancoraProviderPrefix, byte(dataType)}, provider.BytesBE()...) - cfg := new(state.ProviderConfig) - err := getConvertibleFromDAO(a.ID, d, key, cfg) - if err != nil { - if errors.Is(err, storage.ErrKeyNotFound) { - return nil - } - panic(fmt.Errorf("failed to get provider config: %w", err)) - } - return cfg -} - -func (a *Ancora) putProviderConfig(d *dao.Simple, cfg *state.ProviderConfig) error { - key := append([]byte{ancoraProviderPrefix, byte(cfg.DataType)}, cfg.Provider.BytesBE()...) - return putConvertibleToDAO(a.ID, d, key, cfg) -} - -// ========== Root Management ========== - -func (a *Ancora) updateDataRoot(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - root, err := args[2].TryBytes() - if err != nil { - panic(ErrAncoraInvalidRoot) - } - if len(root) != 32 { - panic(ErrAncoraInvalidRoot) - } - leafCount := toUint64(args[3]) - treeAlgorithm := state.TreeAlgorithm(toUint32(args[4])) - schemaVersion := toString(args[5]) - contentHash, _ := args[6].TryBytes() - - // Verify Vita exists - if !a.Vita.ExistsInternal(ic.DAO, vitaID) { - panic(ErrAncoraVitaNotFound) - } - - // Verify caller is authorized provider or Vita owner - caller := ic.VM.GetCallingScriptHash() - providerCfg := a.getProviderConfig(ic.DAO, dataType, caller) - - if providerCfg == nil || !providerCfg.Active { - // Check if caller is Vita owner - owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) - ok, err := runtime.CheckHashedWitness(ic, owner) - if err != nil || !ok { - panic(ErrAncoraUnauthorized) - } - } else { - // Rate limiting for providers - if err := a.checkRateLimit(ic, providerCfg, vitaID, dataType); err != nil { - panic(err) - } - } - - // Get current root to determine version - currentRoot := a.getRootInfo(ic.DAO, vitaID, dataType) - var version uint64 = 1 - if currentRoot != nil { - version = currentRoot.Version + 1 - // Archive current root to history - if err := a.archiveRoot(ic.DAO, vitaID, dataType, currentRoot); err != nil { - panic(fmt.Errorf("failed to archive previous root: %w", err)) - } - } - - newRoot := &state.RootInfo{ - Root: root, - LeafCount: leafCount, - UpdatedAt: ic.BlockHeight(), - UpdatedBy: caller, - Version: version, - TreeAlgorithm: treeAlgorithm, - SchemaVersion: schemaVersion, - ContentHash: contentHash, - } - - if err := a.putRootInfo(ic.DAO, vitaID, dataType, newRoot); err != nil { - panic(fmt.Errorf("failed to update root: %w", err)) - } - - // Update rate limit tracking - a.recordUpdate(ic.DAO, ic.BlockHeight(), caller, vitaID, dataType) - - // ARCH-005: Audit log data root update - // For GDPR compliance: the on-chain audit entry serves as immutable proof - // that an update occurred, even if off-chain data is later deleted - prevStateHash := util.Uint256{} - newStateHash := hash.Sha256(root) - if currentRoot != nil { - prevStateHash = hash.Sha256(currentRoot.Root) - } - resourceID := make([]byte, 9) - putUint64(resourceID[0:8], vitaID) - resourceID[8] = byte(dataType) - a.Audit.LogDataChange(ic.DAO, caller, nativeids.Ancora, "ancora.data_root_updated", - resourceID, prevStateHash, newStateHash, ic.BlockHeight()) - - ic.AddNotification(a.Hash, "DataRootUpdated", stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(dataType))), - stackitem.NewByteArray(root), - stackitem.NewBigInteger(big.NewInt(int64(version))), - stackitem.NewByteArray(caller.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (a *Ancora) getDataRoot(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - - root := a.getRootInfo(ic.DAO, vitaID, dataType) - if root == nil { - return stackitem.Null{} - } - item, err := root.ToStackItem() - if err != nil { - panic(fmt.Errorf("failed to convert root to stack item: %w", err)) - } - return item -} - -func (a *Ancora) getDataRootAtVersion(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - version := toUint64(args[2]) - - // First check current version - current := a.getRootInfo(ic.DAO, vitaID, dataType) - if current != nil && current.Version == version { - item, err := current.ToStackItem() - if err != nil { - panic(fmt.Errorf("failed to convert root to stack item: %w", err)) - } - return item - } - - // Check history - root := a.getHistoricalRoot(ic.DAO, vitaID, dataType, version) - if root == nil { - return stackitem.Null{} - } - item, err := root.ToStackItem() - if err != nil { - panic(fmt.Errorf("failed to convert root to stack item: %w", err)) - } - return item -} - -func (a *Ancora) getRootInfo(d *dao.Simple, vitaID uint64, dataType state.DataType) *state.RootInfo { - key := a.makeRootKey(vitaID, dataType) - root := new(state.RootInfo) - err := getConvertibleFromDAO(a.ID, d, key, root) - if err != nil { - if errors.Is(err, storage.ErrKeyNotFound) { - return nil - } - panic(fmt.Errorf("failed to get root info: %w", err)) - } - return root -} - -func (a *Ancora) putRootInfo(d *dao.Simple, vitaID uint64, dataType state.DataType, root *state.RootInfo) error { - key := a.makeRootKey(vitaID, dataType) - return putConvertibleToDAO(a.ID, d, key, root) -} - -func (a *Ancora) getHistoricalRoot(d *dao.Simple, vitaID uint64, dataType state.DataType, version uint64) *state.RootInfo { - key := a.makeHistoryKey(vitaID, dataType, version) - root := new(state.RootInfo) - err := getConvertibleFromDAO(a.ID, d, key, root) - if err != nil { - if errors.Is(err, storage.ErrKeyNotFound) { - return nil - } - panic(fmt.Errorf("failed to get historical root: %w", err)) - } - return root -} - -func (a *Ancora) archiveRoot(d *dao.Simple, vitaID uint64, dataType state.DataType, root *state.RootInfo) error { - key := a.makeHistoryKey(vitaID, dataType, root.Version) - return putConvertibleToDAO(a.ID, d, key, root) -} - -func (a *Ancora) makeRootKey(vitaID uint64, dataType state.DataType) []byte { - key := make([]byte, 1+8+1) - key[0] = ancoraRootPrefix - putUint64(key[1:9], vitaID) - key[9] = byte(dataType) - return key -} - -func (a *Ancora) makeHistoryKey(vitaID uint64, dataType state.DataType, version uint64) []byte { - key := make([]byte, 1+8+1+8) - key[0] = ancoraHistoryPrefix - putUint64(key[1:9], vitaID) - key[9] = byte(dataType) - putUint64(key[10:18], version) - return key -} - -// ========== Proof Verification ========== - -func (a *Ancora) verifyProof(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - leaf, err := args[2].TryBytes() - if err != nil { - panic(ErrAncoraInvalidProof) - } - - proofArr, ok := args[3].Value().([]stackitem.Item) - if !ok { - panic(ErrAncoraInvalidProof) - } - - proof := make([][]byte, len(proofArr)) - for i, item := range proofArr { - proof[i], err = item.TryBytes() - if err != nil { - panic(ErrAncoraInvalidProof) - } - } - - index := toUint64(args[4]) - - rootInfo := a.getRootInfo(ic.DAO, vitaID, dataType) - if rootInfo == nil { - return stackitem.NewBool(false) - } - - valid := a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) - - // ARCH-005: Audit log proof verification (access tracking) - caller := ic.VM.GetCallingScriptHash() - owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) - resourceID := make([]byte, 9) - putUint64(resourceID[0:8], vitaID) - resourceID[8] = byte(dataType) - outcome := AuditOutcomeSuccess - if !valid { - outcome = AuditOutcomeFailure - } - a.Audit.LogAccess(ic.DAO, caller, owner, "ancora.proof_verified", resourceID, outcome, ic.BlockHeight()) - - return stackitem.NewBool(valid) -} - -func (a *Ancora) verifyProofAtVersion(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - version := toUint64(args[2]) - leaf, err := args[3].TryBytes() - if err != nil { - panic(ErrAncoraInvalidProof) - } - - proofArr, ok := args[4].Value().([]stackitem.Item) - if !ok { - panic(ErrAncoraInvalidProof) - } - - proof := make([][]byte, len(proofArr)) - for i, item := range proofArr { - proof[i], err = item.TryBytes() - if err != nil { - panic(ErrAncoraInvalidProof) - } - } - - index := toUint64(args[5]) - - // Check current version first - rootInfo := a.getRootInfo(ic.DAO, vitaID, dataType) - if rootInfo != nil && rootInfo.Version == version { - valid := a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) - a.logProofVerification(ic, vitaID, dataType, version, valid) - return stackitem.NewBool(valid) - } - - // Check historical version - rootInfo = a.getHistoricalRoot(ic.DAO, vitaID, dataType, version) - if rootInfo == nil { - return stackitem.NewBool(false) - } - - valid := a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) - a.logProofVerification(ic, vitaID, dataType, version, valid) - return stackitem.NewBool(valid) -} - -func (a *Ancora) verifyMerkleProofInternal(root, leaf []byte, proof [][]byte, index uint64, algorithm state.TreeAlgorithm) bool { - if len(root) != 32 { - return false - } - - // Check proof depth - cache := a.getCache(nil) - if cache != nil && uint32(len(proof)) > cache.config.MaxProofDepth { - return false - } - - // Compute hash based on algorithm - // Note: Currently only SHA256 is implemented. Keccak256 and Poseidon are TODO. - var computed []byte - switch algorithm { - case state.TreeAlgorithmSHA256: - computed = hash.Sha256(leaf).BytesBE() - case state.TreeAlgorithmKeccak256: - // TODO: Implement Keccak256 when needed for EVM compatibility - computed = hash.Sha256(leaf).BytesBE() - default: - // Poseidon not yet implemented, fallback to SHA256 - computed = hash.Sha256(leaf).BytesBE() - } - - // Traverse the proof - for _, sibling := range proof { - if len(sibling) != 32 { - return false - } - - var combined []byte - if index%2 == 0 { - combined = append(computed, sibling...) - } else { - combined = append(sibling, computed...) - } - - switch algorithm { - case state.TreeAlgorithmSHA256: - computed = hash.Sha256(combined).BytesBE() - case state.TreeAlgorithmKeccak256: - // TODO: Implement Keccak256 when needed for EVM compatibility - computed = hash.Sha256(combined).BytesBE() - default: - computed = hash.Sha256(combined).BytesBE() - } - - index /= 2 - } - - // Compare computed root with stored root - if len(computed) != len(root) { - return false - } - for i := range computed { - if computed[i] != root[i] { - return false - } - } - return true -} - -// VerifyProofInternal is a cross-contract method for other native contracts. -func (a *Ancora) VerifyProofInternal(d *dao.Simple, vitaID uint64, dataType state.DataType, leaf []byte, proof [][]byte, index uint64) bool { - rootInfo := a.getRootInfo(d, vitaID, dataType) - if rootInfo == nil { - return false - } - return a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) -} - -// RequireValidRoot is a cross-contract method that panics if no valid root exists. -func (a *Ancora) RequireValidRoot(d *dao.Simple, vitaID uint64, dataType state.DataType) { - root := a.getRootInfo(d, vitaID, dataType) - if root == nil { - panic(ErrAncoraNoRoot) - } -} - -// logProofVerification logs a proof verification to the audit trail. -func (a *Ancora) logProofVerification(ic *interop.Context, vitaID uint64, dataType state.DataType, version uint64, valid bool) { - caller := ic.VM.GetCallingScriptHash() - owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) - resourceID := make([]byte, 17) - putUint64(resourceID[0:8], vitaID) - resourceID[8] = byte(dataType) - putUint64(resourceID[9:17], version) - outcome := AuditOutcomeSuccess - if !valid { - outcome = AuditOutcomeFailure - } - a.Audit.LogAccess(ic.DAO, caller, owner, "ancora.proof_verified_at_version", resourceID, outcome, ic.BlockHeight()) -} - -// ========== GDPR Erasure ========== - -func (a *Ancora) requestErasure(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - reason := toString(args[2]) - - // Verify caller is Vita owner - owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) - ok, err := runtime.CheckHashedWitness(ic, owner) - if err != nil || !ok { - panic(ErrAncoraUnauthorized) - } - - // Check for existing erasure request - existing := a.getErasureInfoInternal(ic.DAO, vitaID, dataType) - if existing != nil && existing.Status == state.ErasurePending { - panic(ErrAncoraErasurePending) - } - - erasure := &state.ErasureInfo{ - RequestedAt: ic.BlockHeight(), - RequestedBy: owner, - Reason: reason, - Status: state.ErasurePending, - } - - if err := a.putErasureInfo(ic.DAO, vitaID, dataType, erasure); err != nil { - panic(fmt.Errorf("failed to save erasure request: %w", err)) - } - - // ARCH-005: Audit log erasure request (GDPR Article 17 - Right to be forgotten) - // This audit entry is CRITICAL for compliance - proves user exercised their right - resourceID := make([]byte, 9) - putUint64(resourceID[0:8], vitaID) - resourceID[8] = byte(dataType) - a.Audit.LogCompliance(ic.DAO, owner, util.Uint160{}, "ancora.erasure_requested", - fmt.Sprintf("vitaID=%d dataType=%d reason=%s", vitaID, dataType, reason), ic.BlockHeight()) - - ic.AddNotification(a.Hash, "ErasureRequested", stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(dataType))), - stackitem.NewByteArray([]byte(reason)), - stackitem.NewBigInteger(big.NewInt(int64(ic.BlockHeight()))), - })) - - return stackitem.NewBool(true) -} - -func (a *Ancora) confirmErasure(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - - // Verify caller is authorized provider - caller := ic.VM.GetCallingScriptHash() - providerCfg := a.getProviderConfig(ic.DAO, dataType, caller) - if providerCfg == nil || !providerCfg.Active { - panic(ErrAncoraUnauthorized) - } - - erasure := a.getErasureInfoInternal(ic.DAO, vitaID, dataType) - if erasure == nil || erasure.Status != state.ErasurePending { - panic(ErrAncoraErasureNotPending) - } - - erasure.Status = state.ErasureConfirmed - erasure.ProcessedAt = ic.BlockHeight() - erasure.ConfirmedBy = caller - - if err := a.putErasureInfo(ic.DAO, vitaID, dataType, erasure); err != nil { - panic(fmt.Errorf("failed to confirm erasure: %w", err)) - } - - // Clear the data root - rootKey := a.makeRootKey(vitaID, dataType) - ic.DAO.DeleteStorageItem(a.ID, rootKey) - - // ARCH-005: Audit log erasure confirmation (GDPR Article 17 fulfilled) - // This audit entry proves we complied with the erasure request - // Note: The on-chain hash of this audit entry remains as proof of compliance - a.Audit.LogCompliance(ic.DAO, caller, erasure.RequestedBy, "ancora.erasure_confirmed", - fmt.Sprintf("vitaID=%d dataType=%d requestedAt=%d", vitaID, dataType, erasure.RequestedAt), - ic.BlockHeight()) - - ic.AddNotification(a.Hash, "ErasureConfirmed", stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(dataType))), - stackitem.NewByteArray(caller.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (a *Ancora) denyErasure(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - reason := toString(args[2]) - - // Check grace period - erasure := a.getErasureInfoInternal(ic.DAO, vitaID, dataType) - if erasure == nil || erasure.Status != state.ErasurePending { - panic(ErrAncoraErasureNotPending) - } - - cache := ic.DAO.GetROCache(a.ID).(*AncoraCache) - if ic.BlockHeight() < erasure.RequestedAt+cache.config.ErasureGracePeriod { - panic(ErrAncoraErasureGracePeriod) - } - - // Verify caller is authorized provider or committee - caller := ic.VM.GetCallingScriptHash() - providerCfg := a.getProviderConfig(ic.DAO, dataType, caller) - if (providerCfg == nil || !providerCfg.Active) && !a.Tutus.CheckCommittee(ic) { - panic(ErrAncoraUnauthorized) - } - - erasure.Status = state.ErasureDenied - erasure.ProcessedAt = ic.BlockHeight() - erasure.ConfirmedBy = caller - erasure.DeniedReason = reason - - if err := a.putErasureInfo(ic.DAO, vitaID, dataType, erasure); err != nil { - panic(fmt.Errorf("failed to deny erasure: %w", err)) - } - - // ARCH-005: Audit log erasure denial (GDPR Article 17 exception invoked) - // Critical for legal compliance - documents the legal basis for denial - a.Audit.LogCompliance(ic.DAO, caller, erasure.RequestedBy, "ancora.erasure_denied", - fmt.Sprintf("vitaID=%d dataType=%d reason=%s", vitaID, dataType, reason), - ic.BlockHeight()) - - ic.AddNotification(a.Hash, "ErasureDenied", stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(dataType))), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -func (a *Ancora) getErasureInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataType := state.DataType(toUint32(args[1])) - - erasure := a.getErasureInfoFromDAO(ic.DAO, vitaID, dataType) - if erasure == nil { - return stackitem.Null{} - } - item, err := erasure.ToStackItem() - if err != nil { - panic(fmt.Errorf("failed to convert erasure to stack item: %w", err)) - } - return item -} - -func (a *Ancora) getErasureInfoFromDAO(d *dao.Simple, vitaID uint64, dataType state.DataType) *state.ErasureInfo { - return a.getErasureInfoInternal(d, vitaID, dataType) -} - -func (a *Ancora) getErasureInfoInternal(d *dao.Simple, vitaID uint64, dataType state.DataType) *state.ErasureInfo { - key := a.makeErasureKey(vitaID, dataType) - erasure := new(state.ErasureInfo) - err := getConvertibleFromDAO(a.ID, d, key, erasure) - if err != nil { - if errors.Is(err, storage.ErrKeyNotFound) { - return nil - } - panic(fmt.Errorf("failed to get erasure info: %w", err)) - } - return erasure -} - -func (a *Ancora) putErasureInfo(d *dao.Simple, vitaID uint64, dataType state.DataType, erasure *state.ErasureInfo) error { - key := a.makeErasureKey(vitaID, dataType) - return putConvertibleToDAO(a.ID, d, key, erasure) -} - -func (a *Ancora) makeErasureKey(vitaID uint64, dataType state.DataType) []byte { - key := make([]byte, 1+8+1) - key[0] = ancoraErasurePrefix - putUint64(key[1:9], vitaID) - key[9] = byte(dataType) - return key -} - -// ========== Data Portability ========== - -func (a *Ancora) generatePortabilityAttestation(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - dataTypesArr, ok := args[1].Value().([]stackitem.Item) - if !ok { - panic("invalid dataTypes array") - } - - // Verify caller is Vita owner - owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) - ok2, err := runtime.CheckHashedWitness(ic, owner) - if err != nil || !ok2 { - panic(ErrAncoraUnauthorized) - } - - // Build attestation data - attestData := make([]byte, 0, 8+4+32*len(dataTypesArr)) - attestData = appendUint64(attestData, vitaID) - attestData = appendUint32(attestData, ic.BlockHeight()) - - for _, item := range dataTypesArr { - dataType := state.DataType(toUint32(item)) - rootInfo := a.getRootInfo(ic.DAO, vitaID, dataType) - if rootInfo != nil { - attestData = append(attestData, byte(dataType)) - attestData = append(attestData, rootInfo.Root...) - } - } - - // Hash the attestation - attestHash := hash.Sha256(attestData).BytesBE() - - // Store attestation with expiry - cache := ic.DAO.GetROCache(a.ID).(*AncoraCache) - expiryBlock := ic.BlockHeight() + cache.config.AttestationValidBlocks - - key := append([]byte{ancoraAttestationPrefix}, attestHash...) - value := make([]byte, 4) - putUint32(value, expiryBlock) - ic.DAO.PutStorageItem(a.ID, key, value) - - ic.AddNotification(a.Hash, "PortabilityAttestationGenerated", stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewByteArray(attestHash), - stackitem.NewBigInteger(big.NewInt(int64(expiryBlock))), - })) - - return stackitem.NewByteArray(attestData) -} - -func (a *Ancora) verifyPortabilityAttestation(ic *interop.Context, args []stackitem.Item) stackitem.Item { - attestation, err := args[0].TryBytes() - if err != nil { - panic(ErrAncoraInvalidAttestation) - } - - attestHash := hash.Sha256(attestation).BytesBE() - key := append([]byte{ancoraAttestationPrefix}, attestHash...) - - value := ic.DAO.GetStorageItem(a.ID, key) - if value == nil { - return stackitem.NewBool(false) - } - - expiryBlock := getUint32(value) - if ic.BlockHeight() > expiryBlock { - return stackitem.NewBool(false) - } - - return stackitem.NewBool(true) -} - -// ========== Audit Log Query ========== - -// getAuditLog retrieves audit log entries for a specific Vita or globally. -// This provides GDPR-compliant transparency: citizens can see who accessed their data. -func (a *Ancora) getAuditLog(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - startBlock := toUint32(args[1]) - endBlock := toUint32(args[2]) - limit := int(toUint32(args[3])) - - if limit <= 0 || limit > 100 { - limit = 100 // Cap at 100 entries per query - } - - // If vitaID is specified, verify caller is the owner or committee - if vitaID > 0 { - owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) - caller := ic.VM.GetCallingScriptHash() - - // Check if caller is the Vita owner - ok, err := runtime.CheckHashedWitness(ic, owner) - if err != nil || !ok { - // Not the owner - check if committee - if !a.Tutus.CheckCommittee(ic) { - // Check if caller is an authorized provider for this data - isProvider := false - for dt := state.DataType(0); dt <= state.DataTypeCustom; dt++ { - if cfg := a.getProviderConfig(ic.DAO, dt, caller); cfg != nil && cfg.Active { - isProvider = true - break - } - } - if !isProvider { - panic(ErrAncoraUnauthorized) - } - } - } - } else { - // Global audit query - requires committee - if !a.Tutus.CheckCommittee(ic) { - panic(ErrAncoraUnauthorized) - } - } - - // Build query - query := &AuditQuery{ - StartBlock: startBlock, - EndBlock: endBlock, - Limit: limit, - } - - // If vitaID specified, query by target (owner of Vita) - if vitaID > 0 { - owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) - query.Target = &owner - } - - // Query the audit log - entries := a.Audit.Query(ic.DAO, query) - - // Convert entries to stack items - items := make([]stackitem.Item, 0, len(entries)) - for _, entry := range entries { - items = append(items, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(entry.EntryID))), - stackitem.NewBigInteger(big.NewInt(int64(entry.Timestamp))), - stackitem.NewBigInteger(big.NewInt(int64(entry.Category))), - stackitem.NewBigInteger(big.NewInt(int64(entry.Severity))), - stackitem.NewBigInteger(big.NewInt(int64(entry.Outcome))), - stackitem.NewByteArray(entry.Actor.BytesBE()), - stackitem.NewByteArray(entry.Target.BytesBE()), - stackitem.NewByteArray([]byte(entry.Action)), - stackitem.NewByteArray(entry.ResourceID), - stackitem.NewByteArray([]byte(entry.Details)), - })) - } - - return stackitem.NewArray(items) -} - -// ========== Rate Limiting Helpers ========== - -func (a *Ancora) checkRateLimit(ic *interop.Context, cfg *state.ProviderConfig, vitaID uint64, dataType state.DataType) error { - // Check per-block rate limit - blockHeight := ic.BlockHeight() - updateCount := a.getUpdateCount(ic.DAO, blockHeight, cfg.Provider) - if updateCount >= cfg.MaxUpdatesPerBlock { - return ErrAncoraRateLimited - } - - // Check per-vitaID cooldown - lastUpdate := a.getLastUpdate(ic.DAO, vitaID, dataType, cfg.Provider) - if lastUpdate > 0 && blockHeight < lastUpdate+cfg.UpdateCooldown { - return ErrAncoraUpdateCooldown - } - - return nil -} - -func (a *Ancora) recordUpdate(d *dao.Simple, blockHeight uint32, provider util.Uint160, vitaID uint64, dataType state.DataType) { - // Increment block update count - countKey := append([]byte{ancoraUpdateCountPrefix}, provider.BytesBE()...) - countKey = appendUint32(countKey, blockHeight) - - var count uint32 - data := d.GetStorageItem(a.ID, countKey) - if data != nil && len(data) >= 4 { - count = getUint32(data) - } - count++ - countData := make([]byte, 4) - putUint32(countData, count) - d.PutStorageItem(a.ID, countKey, countData) - - // Record last update for vitaID+dataType+provider - lastKey := a.makeLastUpdateKey(vitaID, dataType, provider) - lastData := make([]byte, 4) - putUint32(lastData, blockHeight) - d.PutStorageItem(a.ID, lastKey, lastData) -} - -func (a *Ancora) getUpdateCount(d *dao.Simple, blockHeight uint32, provider util.Uint160) uint32 { - countKey := append([]byte{ancoraUpdateCountPrefix}, provider.BytesBE()...) - countKey = appendUint32(countKey, blockHeight) - - data := d.GetStorageItem(a.ID, countKey) - if data == nil || len(data) < 4 { - return 0 - } - return getUint32(data) -} - -func (a *Ancora) getLastUpdate(d *dao.Simple, vitaID uint64, dataType state.DataType, provider util.Uint160) uint32 { - key := a.makeLastUpdateKey(vitaID, dataType, provider) - data := d.GetStorageItem(a.ID, key) - if data == nil || len(data) < 4 { - return 0 - } - return getUint32(data) -} - -func (a *Ancora) makeLastUpdateKey(vitaID uint64, dataType state.DataType, provider util.Uint160) []byte { - key := make([]byte, 1+8+1+20) - key[0] = ancoraLastUpdatePrefix - putUint64(key[1:9], vitaID) - key[9] = byte(dataType) - copy(key[10:30], provider.BytesBE()) - return key -} - -// ========== Utility Helpers ========== - -func (a *Ancora) getCache(d *dao.Simple) *AncoraCache { - if d == nil { - return nil - } - cache := d.GetROCache(a.ID) - if cache == nil { - return nil - } - return cache.(*AncoraCache) -} - -func putUint32(b []byte, v uint32) { - b[0] = byte(v) - b[1] = byte(v >> 8) - b[2] = byte(v >> 16) - b[3] = byte(v >> 24) -} - -func getUint32(b []byte) uint32 { - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 -} - -func putUint64(b []byte, v uint64) { - b[0] = byte(v) - b[1] = byte(v >> 8) - b[2] = byte(v >> 16) - b[3] = byte(v >> 24) - b[4] = byte(v >> 32) - b[5] = byte(v >> 40) - b[6] = byte(v >> 48) - b[7] = byte(v >> 56) -} - -func appendUint32(b []byte, v uint32) []byte { - return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24)) -} - -func appendUint64(b []byte, v uint64) []byte { - return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24), - byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56)) -} +package native + +import ( + "errors" + "fmt" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Ancora represents the Ancora native contract for anchoring Merkle roots of off-chain data. +// Latin: "ancora" = anchor - anchors off-chain data to on-chain verification. +type Ancora struct { + interop.ContractMD + Vita IVita + Tutus ITutus + Audit *AuditLogger // ARCH-005: Comprehensive audit logging +} + +// AncoraCache holds cached configuration for the Ancora contract. +type AncoraCache struct { + config state.StateAnchorsConfig +} + +// Storage prefixes for Ancora contract. +const ( + ancoraConfigPrefix = 0x01 + ancoraRootPrefix = 0x10 // vitaID + dataType -> RootInfo + ancoraHistoryPrefix = 0x11 // vitaID + dataType + version -> RootInfo + ancoraProviderPrefix = 0x20 // dataType + provider -> ProviderConfig + ancoraErasurePrefix = 0x30 // vitaID + dataType -> ErasureInfo + ancoraUpdateCountPrefix = 0x40 // blockHeight + provider -> count + ancoraLastUpdatePrefix = 0x41 // vitaID + dataType + provider -> blockHeight + ancoraAttestationPrefix = 0x50 // attestationHash -> AttestationInfo +) + +// Errors for Ancora contract. +var ( + ErrAncoraInvalidDataType = errors.New("invalid data type") + ErrAncoraInvalidRoot = errors.New("invalid Merkle root: must be 32 bytes") + ErrAncoraProviderNotFound = errors.New("provider not found") + ErrAncoraProviderInactive = errors.New("provider is inactive") + ErrAncoraProviderExists = errors.New("provider already registered") + ErrAncoraUnauthorized = errors.New("unauthorized: caller is not authorized provider") + ErrAncoraRateLimited = errors.New("rate limit exceeded") + ErrAncoraUpdateCooldown = errors.New("update cooldown not elapsed") + ErrAncoraNoRoot = errors.New("no root found for vitaID and dataType") + ErrAncoraInvalidProof = errors.New("invalid Merkle proof") + ErrAncoraProofTooDeep = errors.New("proof exceeds maximum depth") + ErrAncoraVersionNotFound = errors.New("version not found in history") + ErrAncoraErasurePending = errors.New("erasure already pending") + ErrAncoraErasureNotPending = errors.New("no pending erasure request") + ErrAncoraErasureGracePeriod = errors.New("erasure grace period not elapsed") + ErrAncoraInvalidAttestation = errors.New("invalid attestation") + ErrAncoraAttestationExpired = errors.New("attestation has expired") + ErrAncoraVitaNotFound = errors.New("vita not found") +) + +var ( + _ interop.Contract = (*Ancora)(nil) + _ dao.NativeContractCache = (*AncoraCache)(nil) +) + +// Copy implements NativeContractCache interface. +func (c *AncoraCache) Copy() dao.NativeContractCache { + cp := &AncoraCache{config: c.config} + return cp +} + +// newAncora returns a new Ancora native contract. +func newAncora() *Ancora { + a := &Ancora{ + ContractMD: *interop.NewContractMD(nativenames.Ancora, nativeids.Ancora, nil), + Audit: NewAuditLogger(nativeids.Ancora), + } + defer a.BuildHFSpecificMD(a.ActiveIn()) + + // Configuration methods + desc := NewDescriptor("getConfig", smartcontract.ArrayType) + md := NewMethodAndPrice(a.getConfig, 1<<15, callflag.ReadStates) + a.AddMethod(md, desc) + + desc = NewDescriptor("setConfig", smartcontract.VoidType, + manifest.NewParameter("config", smartcontract.ArrayType)) + md = NewMethodAndPrice(a.setConfig, 1<<15, callflag.States) + a.AddMethod(md, desc) + + // Provider management + desc = NewDescriptor("registerProvider", smartcontract.BoolType, + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("provider", smartcontract.Hash160Type), + manifest.NewParameter("description", smartcontract.StringType), + manifest.NewParameter("maxUpdatesPerBlock", smartcontract.IntegerType), + manifest.NewParameter("updateCooldown", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.registerProvider, 1<<15, callflag.States) + a.AddMethod(md, desc) + + desc = NewDescriptor("revokeProvider", smartcontract.BoolType, + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("provider", smartcontract.Hash160Type)) + md = NewMethodAndPrice(a.revokeProvider, 1<<15, callflag.States) + a.AddMethod(md, desc) + + desc = NewDescriptor("getProvider", smartcontract.ArrayType, + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("provider", smartcontract.Hash160Type)) + md = NewMethodAndPrice(a.getProvider, 1<<15, callflag.ReadStates) + a.AddMethod(md, desc) + + desc = NewDescriptor("isProviderActive", smartcontract.BoolType, + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("provider", smartcontract.Hash160Type)) + md = NewMethodAndPrice(a.isProviderActive, 1<<15, callflag.ReadStates) + a.AddMethod(md, desc) + + // Root management + desc = NewDescriptor("updateDataRoot", smartcontract.BoolType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("root", smartcontract.ByteArrayType), + manifest.NewParameter("leafCount", smartcontract.IntegerType), + manifest.NewParameter("treeAlgorithm", smartcontract.IntegerType), + manifest.NewParameter("schemaVersion", smartcontract.StringType), + manifest.NewParameter("contentHash", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(a.updateDataRoot, 1<<16, callflag.States) + a.AddMethod(md, desc) + + desc = NewDescriptor("getDataRoot", smartcontract.ArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.getDataRoot, 1<<15, callflag.ReadStates) + a.AddMethod(md, desc) + + desc = NewDescriptor("getDataRootAtVersion", smartcontract.ArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("version", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.getDataRootAtVersion, 1<<15, callflag.ReadStates) + a.AddMethod(md, desc) + + // Proof verification + desc = NewDescriptor("verifyProof", smartcontract.BoolType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("leaf", smartcontract.ByteArrayType), + manifest.NewParameter("proof", smartcontract.ArrayType), + manifest.NewParameter("index", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.verifyProof, 1<<16, callflag.ReadStates) + a.AddMethod(md, desc) + + desc = NewDescriptor("verifyProofAtVersion", smartcontract.BoolType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("version", smartcontract.IntegerType), + manifest.NewParameter("leaf", smartcontract.ByteArrayType), + manifest.NewParameter("proof", smartcontract.ArrayType), + manifest.NewParameter("index", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.verifyProofAtVersion, 1<<16, callflag.ReadStates) + a.AddMethod(md, desc) + + // GDPR erasure + desc = NewDescriptor("requestErasure", smartcontract.BoolType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(a.requestErasure, 1<<15, callflag.States) + a.AddMethod(md, desc) + + desc = NewDescriptor("confirmErasure", smartcontract.BoolType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.confirmErasure, 1<<15, callflag.States) + a.AddMethod(md, desc) + + desc = NewDescriptor("denyErasure", smartcontract.BoolType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(a.denyErasure, 1<<15, callflag.States) + a.AddMethod(md, desc) + + desc = NewDescriptor("getErasureInfo", smartcontract.ArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataType", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.getErasureInfo, 1<<15, callflag.ReadStates) + a.AddMethod(md, desc) + + // Data portability + desc = NewDescriptor("generatePortabilityAttestation", smartcontract.ByteArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("dataTypes", smartcontract.ArrayType)) + md = NewMethodAndPrice(a.generatePortabilityAttestation, 1<<16, callflag.States) + a.AddMethod(md, desc) + + desc = NewDescriptor("verifyPortabilityAttestation", smartcontract.BoolType, + manifest.NewParameter("attestation", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(a.verifyPortabilityAttestation, 1<<15, callflag.ReadStates) + a.AddMethod(md, desc) + + // Audit log query (ARCH-005) + desc = NewDescriptor("getAuditLog", smartcontract.ArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("startBlock", smartcontract.IntegerType), + manifest.NewParameter("endBlock", smartcontract.IntegerType), + manifest.NewParameter("limit", smartcontract.IntegerType)) + md = NewMethodAndPrice(a.getAuditLog, 1<<16, callflag.ReadStates) + a.AddMethod(md, desc) + + return a +} + +// Metadata implements the Contract interface. +func (a *Ancora) Metadata() *interop.ContractMD { + return &a.ContractMD +} + +// Initialize initializes Ancora native contract and implements the Contract interface. +func (a *Ancora) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != a.ActiveIn() { + return nil + } + + cfg := state.DefaultStateAnchorsConfig() + if err := a.putConfig(ic.DAO, &cfg); err != nil { + return fmt.Errorf("failed to initialize config: %w", err) + } + + cache := &AncoraCache{config: cfg} + ic.DAO.SetCache(a.ID, cache) + return nil +} + +// InitializeCache implements the Contract interface. +func (a *Ancora) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cfg, err := a.loadConfig(d) + if err != nil { + return fmt.Errorf("failed to load config: %w", err) + } + + cache := &AncoraCache{config: *cfg} + d.SetCache(a.ID, cache) + return nil +} + +// OnPersist implements the Contract interface. +func (a *Ancora) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (a *Ancora) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn implements the Contract interface. +func (a *Ancora) ActiveIn() *config.Hardfork { + return nil // Active from genesis +} + +// Address returns the contract's script hash. +func (a *Ancora) Address() util.Uint160 { + return a.Hash +} + +// ========== Configuration Methods ========== + +func (a *Ancora) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(a.ID).(*AncoraCache) + cfg := cache.config + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(cfg.DefaultTreeAlgorithm))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.MaxProofDepth))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.DefaultMaxUpdatesPerBlock))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.DefaultUpdateCooldown))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.MaxHistoryVersions))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.ErasureGracePeriod))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.AttestationValidBlocks))), + }) +} + +func (a *Ancora) setConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !a.Tutus.CheckCommittee(ic) { + panic("invalid committee signature") + } + + arr, ok := args[0].Value().([]stackitem.Item) + if !ok || len(arr) != 7 { + panic("invalid config array") + } + + cfg := state.StateAnchorsConfig{ + DefaultTreeAlgorithm: state.TreeAlgorithm(toUint32(arr[0])), + MaxProofDepth: toUint32(arr[1]), + DefaultMaxUpdatesPerBlock: toUint32(arr[2]), + DefaultUpdateCooldown: toUint32(arr[3]), + MaxHistoryVersions: toUint32(arr[4]), + ErasureGracePeriod: toUint32(arr[5]), + AttestationValidBlocks: toUint32(arr[6]), + } + + if err := a.putConfig(ic.DAO, &cfg); err != nil { + panic(fmt.Errorf("failed to save config: %w", err)) + } + + cache := ic.DAO.GetRWCache(a.ID).(*AncoraCache) + cache.config = cfg + return stackitem.Null{} +} + +func (a *Ancora) putConfig(d *dao.Simple, cfg *state.StateAnchorsConfig) error { + key := []byte{ancoraConfigPrefix} + data := make([]byte, 28) // 7 * 4 bytes + // Simple serialization + putUint32(data[0:4], uint32(cfg.DefaultTreeAlgorithm)) + putUint32(data[4:8], cfg.MaxProofDepth) + putUint32(data[8:12], cfg.DefaultMaxUpdatesPerBlock) + putUint32(data[12:16], cfg.DefaultUpdateCooldown) + putUint32(data[16:20], cfg.MaxHistoryVersions) + putUint32(data[20:24], cfg.ErasureGracePeriod) + putUint32(data[24:28], cfg.AttestationValidBlocks) + d.PutStorageItem(a.ID, key, data) + return nil +} + +func (a *Ancora) loadConfig(d *dao.Simple) (*state.StateAnchorsConfig, error) { + key := []byte{ancoraConfigPrefix} + data := d.GetStorageItem(a.ID, key) + if data == nil { + return nil, errors.New("config not found") + } + if len(data) != 28 { + return nil, errors.New("invalid config data length") + } + cfg := &state.StateAnchorsConfig{ + DefaultTreeAlgorithm: state.TreeAlgorithm(getUint32(data[0:4])), + MaxProofDepth: getUint32(data[4:8]), + DefaultMaxUpdatesPerBlock: getUint32(data[8:12]), + DefaultUpdateCooldown: getUint32(data[12:16]), + MaxHistoryVersions: getUint32(data[16:20]), + ErasureGracePeriod: getUint32(data[20:24]), + AttestationValidBlocks: getUint32(data[24:28]), + } + return cfg, nil +} + +// ========== Provider Management ========== + +func (a *Ancora) registerProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !a.Tutus.CheckCommittee(ic) { + panic("invalid committee signature") + } + + dataType := state.DataType(toUint32(args[0])) + if dataType > state.DataTypeCustom { + panic(ErrAncoraInvalidDataType) + } + // Note: Valid types are 0-5 (Medical, Education, Investment, Documents, Presence, Custom) + + provider := toUint160(args[1]) + description := toString(args[2]) + maxUpdatesPerBlock := toUint32(args[3]) + updateCooldown := toUint32(args[4]) + + // Check if provider already exists + existing := a.getProviderConfig(ic.DAO, dataType, provider) + if existing != nil { + panic(ErrAncoraProviderExists) + } + + cfg := &state.ProviderConfig{ + DataType: dataType, + Provider: provider, + Description: description, + RegisteredAt: ic.BlockHeight(), + Active: true, + MaxUpdatesPerBlock: maxUpdatesPerBlock, + UpdateCooldown: updateCooldown, + } + + if err := a.putProviderConfig(ic.DAO, cfg); err != nil { + panic(fmt.Errorf("failed to register provider: %w", err)) + } + + // ARCH-005: Audit log provider registration + a.Audit.LogGovernance(ic.DAO, ic.VM.GetCallingScriptHash(), "ancora.provider_registered", + fmt.Sprintf("dataType=%d provider=%s desc=%s", dataType, provider.StringLE(), description), + AuditOutcomeSuccess, ic.BlockHeight()) + + ic.AddNotification(a.Hash, "ProviderRegistered", stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(dataType))), + stackitem.NewByteArray(provider.BytesBE()), + stackitem.NewByteArray([]byte(description)), + })) + + return stackitem.NewBool(true) +} + +func (a *Ancora) revokeProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !a.Tutus.CheckCommittee(ic) { + panic("invalid committee signature") + } + + dataType := state.DataType(toUint32(args[0])) + provider := toUint160(args[1]) + + cfg := a.getProviderConfig(ic.DAO, dataType, provider) + if cfg == nil { + panic(ErrAncoraProviderNotFound) + } + + cfg.Active = false + if err := a.putProviderConfig(ic.DAO, cfg); err != nil { + panic(fmt.Errorf("failed to revoke provider: %w", err)) + } + + // ARCH-005: Audit log provider revocation + a.Audit.LogGovernance(ic.DAO, ic.VM.GetCallingScriptHash(), "ancora.provider_revoked", + fmt.Sprintf("dataType=%d provider=%s", dataType, provider.StringLE()), + AuditOutcomeSuccess, ic.BlockHeight()) + + ic.AddNotification(a.Hash, "ProviderRevoked", stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(dataType))), + stackitem.NewByteArray(provider.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (a *Ancora) getProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { + dataType := state.DataType(toUint32(args[0])) + provider := toUint160(args[1]) + + cfg := a.getProviderConfig(ic.DAO, dataType, provider) + if cfg == nil { + return stackitem.Null{} + } + item, err := cfg.ToStackItem() + if err != nil { + panic(fmt.Errorf("failed to convert provider config to stack item: %w", err)) + } + return item +} + +func (a *Ancora) isProviderActive(ic *interop.Context, args []stackitem.Item) stackitem.Item { + dataType := state.DataType(toUint32(args[0])) + provider := toUint160(args[1]) + + cfg := a.getProviderConfig(ic.DAO, dataType, provider) + return stackitem.NewBool(cfg != nil && cfg.Active) +} + +func (a *Ancora) getProviderConfig(d *dao.Simple, dataType state.DataType, provider util.Uint160) *state.ProviderConfig { + key := append([]byte{ancoraProviderPrefix, byte(dataType)}, provider.BytesBE()...) + cfg := new(state.ProviderConfig) + err := getConvertibleFromDAO(a.ID, d, key, cfg) + if err != nil { + if errors.Is(err, storage.ErrKeyNotFound) { + return nil + } + panic(fmt.Errorf("failed to get provider config: %w", err)) + } + return cfg +} + +func (a *Ancora) putProviderConfig(d *dao.Simple, cfg *state.ProviderConfig) error { + key := append([]byte{ancoraProviderPrefix, byte(cfg.DataType)}, cfg.Provider.BytesBE()...) + return putConvertibleToDAO(a.ID, d, key, cfg) +} + +// ========== Root Management ========== + +func (a *Ancora) updateDataRoot(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + root, err := args[2].TryBytes() + if err != nil { + panic(ErrAncoraInvalidRoot) + } + if len(root) != 32 { + panic(ErrAncoraInvalidRoot) + } + leafCount := toUint64(args[3]) + treeAlgorithm := state.TreeAlgorithm(toUint32(args[4])) + schemaVersion := toString(args[5]) + contentHash, _ := args[6].TryBytes() + + // Verify Vita exists + if !a.Vita.ExistsInternal(ic.DAO, vitaID) { + panic(ErrAncoraVitaNotFound) + } + + // Verify caller is authorized provider or Vita owner + caller := ic.VM.GetCallingScriptHash() + providerCfg := a.getProviderConfig(ic.DAO, dataType, caller) + + if providerCfg == nil || !providerCfg.Active { + // Check if caller is Vita owner + owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) + ok, err := runtime.CheckHashedWitness(ic, owner) + if err != nil || !ok { + panic(ErrAncoraUnauthorized) + } + } else { + // Rate limiting for providers + if err := a.checkRateLimit(ic, providerCfg, vitaID, dataType); err != nil { + panic(err) + } + } + + // Get current root to determine version + currentRoot := a.getRootInfo(ic.DAO, vitaID, dataType) + var version uint64 = 1 + if currentRoot != nil { + version = currentRoot.Version + 1 + // Archive current root to history + if err := a.archiveRoot(ic.DAO, vitaID, dataType, currentRoot); err != nil { + panic(fmt.Errorf("failed to archive previous root: %w", err)) + } + } + + newRoot := &state.RootInfo{ + Root: root, + LeafCount: leafCount, + UpdatedAt: ic.BlockHeight(), + UpdatedBy: caller, + Version: version, + TreeAlgorithm: treeAlgorithm, + SchemaVersion: schemaVersion, + ContentHash: contentHash, + } + + if err := a.putRootInfo(ic.DAO, vitaID, dataType, newRoot); err != nil { + panic(fmt.Errorf("failed to update root: %w", err)) + } + + // Update rate limit tracking + a.recordUpdate(ic.DAO, ic.BlockHeight(), caller, vitaID, dataType) + + // ARCH-005: Audit log data root update + // For GDPR compliance: the on-chain audit entry serves as immutable proof + // that an update occurred, even if off-chain data is later deleted + prevStateHash := util.Uint256{} + newStateHash := hash.Sha256(root) + if currentRoot != nil { + prevStateHash = hash.Sha256(currentRoot.Root) + } + resourceID := make([]byte, 9) + putUint64(resourceID[0:8], vitaID) + resourceID[8] = byte(dataType) + a.Audit.LogDataChange(ic.DAO, caller, nativeids.Ancora, "ancora.data_root_updated", + resourceID, prevStateHash, newStateHash, ic.BlockHeight()) + + ic.AddNotification(a.Hash, "DataRootUpdated", stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(dataType))), + stackitem.NewByteArray(root), + stackitem.NewBigInteger(big.NewInt(int64(version))), + stackitem.NewByteArray(caller.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (a *Ancora) getDataRoot(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + + root := a.getRootInfo(ic.DAO, vitaID, dataType) + if root == nil { + return stackitem.Null{} + } + item, err := root.ToStackItem() + if err != nil { + panic(fmt.Errorf("failed to convert root to stack item: %w", err)) + } + return item +} + +func (a *Ancora) getDataRootAtVersion(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + version := toUint64(args[2]) + + // First check current version + current := a.getRootInfo(ic.DAO, vitaID, dataType) + if current != nil && current.Version == version { + item, err := current.ToStackItem() + if err != nil { + panic(fmt.Errorf("failed to convert root to stack item: %w", err)) + } + return item + } + + // Check history + root := a.getHistoricalRoot(ic.DAO, vitaID, dataType, version) + if root == nil { + return stackitem.Null{} + } + item, err := root.ToStackItem() + if err != nil { + panic(fmt.Errorf("failed to convert root to stack item: %w", err)) + } + return item +} + +func (a *Ancora) getRootInfo(d *dao.Simple, vitaID uint64, dataType state.DataType) *state.RootInfo { + key := a.makeRootKey(vitaID, dataType) + root := new(state.RootInfo) + err := getConvertibleFromDAO(a.ID, d, key, root) + if err != nil { + if errors.Is(err, storage.ErrKeyNotFound) { + return nil + } + panic(fmt.Errorf("failed to get root info: %w", err)) + } + return root +} + +func (a *Ancora) putRootInfo(d *dao.Simple, vitaID uint64, dataType state.DataType, root *state.RootInfo) error { + key := a.makeRootKey(vitaID, dataType) + return putConvertibleToDAO(a.ID, d, key, root) +} + +func (a *Ancora) getHistoricalRoot(d *dao.Simple, vitaID uint64, dataType state.DataType, version uint64) *state.RootInfo { + key := a.makeHistoryKey(vitaID, dataType, version) + root := new(state.RootInfo) + err := getConvertibleFromDAO(a.ID, d, key, root) + if err != nil { + if errors.Is(err, storage.ErrKeyNotFound) { + return nil + } + panic(fmt.Errorf("failed to get historical root: %w", err)) + } + return root +} + +func (a *Ancora) archiveRoot(d *dao.Simple, vitaID uint64, dataType state.DataType, root *state.RootInfo) error { + key := a.makeHistoryKey(vitaID, dataType, root.Version) + return putConvertibleToDAO(a.ID, d, key, root) +} + +func (a *Ancora) makeRootKey(vitaID uint64, dataType state.DataType) []byte { + key := make([]byte, 1+8+1) + key[0] = ancoraRootPrefix + putUint64(key[1:9], vitaID) + key[9] = byte(dataType) + return key +} + +func (a *Ancora) makeHistoryKey(vitaID uint64, dataType state.DataType, version uint64) []byte { + key := make([]byte, 1+8+1+8) + key[0] = ancoraHistoryPrefix + putUint64(key[1:9], vitaID) + key[9] = byte(dataType) + putUint64(key[10:18], version) + return key +} + +// ========== Proof Verification ========== + +func (a *Ancora) verifyProof(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + leaf, err := args[2].TryBytes() + if err != nil { + panic(ErrAncoraInvalidProof) + } + + proofArr, ok := args[3].Value().([]stackitem.Item) + if !ok { + panic(ErrAncoraInvalidProof) + } + + proof := make([][]byte, len(proofArr)) + for i, item := range proofArr { + proof[i], err = item.TryBytes() + if err != nil { + panic(ErrAncoraInvalidProof) + } + } + + index := toUint64(args[4]) + + rootInfo := a.getRootInfo(ic.DAO, vitaID, dataType) + if rootInfo == nil { + return stackitem.NewBool(false) + } + + valid := a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) + + // ARCH-005: Audit log proof verification (access tracking) + caller := ic.VM.GetCallingScriptHash() + owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) + resourceID := make([]byte, 9) + putUint64(resourceID[0:8], vitaID) + resourceID[8] = byte(dataType) + outcome := AuditOutcomeSuccess + if !valid { + outcome = AuditOutcomeFailure + } + a.Audit.LogAccess(ic.DAO, caller, owner, "ancora.proof_verified", resourceID, outcome, ic.BlockHeight()) + + return stackitem.NewBool(valid) +} + +func (a *Ancora) verifyProofAtVersion(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + version := toUint64(args[2]) + leaf, err := args[3].TryBytes() + if err != nil { + panic(ErrAncoraInvalidProof) + } + + proofArr, ok := args[4].Value().([]stackitem.Item) + if !ok { + panic(ErrAncoraInvalidProof) + } + + proof := make([][]byte, len(proofArr)) + for i, item := range proofArr { + proof[i], err = item.TryBytes() + if err != nil { + panic(ErrAncoraInvalidProof) + } + } + + index := toUint64(args[5]) + + // Check current version first + rootInfo := a.getRootInfo(ic.DAO, vitaID, dataType) + if rootInfo != nil && rootInfo.Version == version { + valid := a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) + a.logProofVerification(ic, vitaID, dataType, version, valid) + return stackitem.NewBool(valid) + } + + // Check historical version + rootInfo = a.getHistoricalRoot(ic.DAO, vitaID, dataType, version) + if rootInfo == nil { + return stackitem.NewBool(false) + } + + valid := a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) + a.logProofVerification(ic, vitaID, dataType, version, valid) + return stackitem.NewBool(valid) +} + +func (a *Ancora) verifyMerkleProofInternal(root, leaf []byte, proof [][]byte, index uint64, algorithm state.TreeAlgorithm) bool { + if len(root) != 32 { + return false + } + + // Check proof depth + cache := a.getCache(nil) + if cache != nil && uint32(len(proof)) > cache.config.MaxProofDepth { + return false + } + + // Compute hash based on algorithm + // Note: Currently only SHA256 is implemented. Keccak256 and Poseidon are TODO. + var computed []byte + switch algorithm { + case state.TreeAlgorithmSHA256: + computed = hash.Sha256(leaf).BytesBE() + case state.TreeAlgorithmKeccak256: + // TODO: Implement Keccak256 when needed for EVM compatibility + computed = hash.Sha256(leaf).BytesBE() + default: + // Poseidon not yet implemented, fallback to SHA256 + computed = hash.Sha256(leaf).BytesBE() + } + + // Traverse the proof + for _, sibling := range proof { + if len(sibling) != 32 { + return false + } + + var combined []byte + if index%2 == 0 { + combined = append(computed, sibling...) + } else { + combined = append(sibling, computed...) + } + + switch algorithm { + case state.TreeAlgorithmSHA256: + computed = hash.Sha256(combined).BytesBE() + case state.TreeAlgorithmKeccak256: + // TODO: Implement Keccak256 when needed for EVM compatibility + computed = hash.Sha256(combined).BytesBE() + default: + computed = hash.Sha256(combined).BytesBE() + } + + index /= 2 + } + + // Compare computed root with stored root + if len(computed) != len(root) { + return false + } + for i := range computed { + if computed[i] != root[i] { + return false + } + } + return true +} + +// VerifyProofInternal is a cross-contract method for other native contracts. +func (a *Ancora) VerifyProofInternal(d *dao.Simple, vitaID uint64, dataType state.DataType, leaf []byte, proof [][]byte, index uint64) bool { + rootInfo := a.getRootInfo(d, vitaID, dataType) + if rootInfo == nil { + return false + } + return a.verifyMerkleProofInternal(rootInfo.Root, leaf, proof, index, rootInfo.TreeAlgorithm) +} + +// RequireValidRoot is a cross-contract method that panics if no valid root exists. +func (a *Ancora) RequireValidRoot(d *dao.Simple, vitaID uint64, dataType state.DataType) { + root := a.getRootInfo(d, vitaID, dataType) + if root == nil { + panic(ErrAncoraNoRoot) + } +} + +// logProofVerification logs a proof verification to the audit trail. +func (a *Ancora) logProofVerification(ic *interop.Context, vitaID uint64, dataType state.DataType, version uint64, valid bool) { + caller := ic.VM.GetCallingScriptHash() + owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) + resourceID := make([]byte, 17) + putUint64(resourceID[0:8], vitaID) + resourceID[8] = byte(dataType) + putUint64(resourceID[9:17], version) + outcome := AuditOutcomeSuccess + if !valid { + outcome = AuditOutcomeFailure + } + a.Audit.LogAccess(ic.DAO, caller, owner, "ancora.proof_verified_at_version", resourceID, outcome, ic.BlockHeight()) +} + +// ========== GDPR Erasure ========== + +func (a *Ancora) requestErasure(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + reason := toString(args[2]) + + // Verify caller is Vita owner + owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) + ok, err := runtime.CheckHashedWitness(ic, owner) + if err != nil || !ok { + panic(ErrAncoraUnauthorized) + } + + // Check for existing erasure request + existing := a.getErasureInfoInternal(ic.DAO, vitaID, dataType) + if existing != nil && existing.Status == state.ErasurePending { + panic(ErrAncoraErasurePending) + } + + erasure := &state.ErasureInfo{ + RequestedAt: ic.BlockHeight(), + RequestedBy: owner, + Reason: reason, + Status: state.ErasurePending, + } + + if err := a.putErasureInfo(ic.DAO, vitaID, dataType, erasure); err != nil { + panic(fmt.Errorf("failed to save erasure request: %w", err)) + } + + // ARCH-005: Audit log erasure request (GDPR Article 17 - Right to be forgotten) + // This audit entry is CRITICAL for compliance - proves user exercised their right + resourceID := make([]byte, 9) + putUint64(resourceID[0:8], vitaID) + resourceID[8] = byte(dataType) + a.Audit.LogCompliance(ic.DAO, owner, util.Uint160{}, "ancora.erasure_requested", + fmt.Sprintf("vitaID=%d dataType=%d reason=%s", vitaID, dataType, reason), ic.BlockHeight()) + + ic.AddNotification(a.Hash, "ErasureRequested", stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(dataType))), + stackitem.NewByteArray([]byte(reason)), + stackitem.NewBigInteger(big.NewInt(int64(ic.BlockHeight()))), + })) + + return stackitem.NewBool(true) +} + +func (a *Ancora) confirmErasure(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + + // Verify caller is authorized provider + caller := ic.VM.GetCallingScriptHash() + providerCfg := a.getProviderConfig(ic.DAO, dataType, caller) + if providerCfg == nil || !providerCfg.Active { + panic(ErrAncoraUnauthorized) + } + + erasure := a.getErasureInfoInternal(ic.DAO, vitaID, dataType) + if erasure == nil || erasure.Status != state.ErasurePending { + panic(ErrAncoraErasureNotPending) + } + + erasure.Status = state.ErasureConfirmed + erasure.ProcessedAt = ic.BlockHeight() + erasure.ConfirmedBy = caller + + if err := a.putErasureInfo(ic.DAO, vitaID, dataType, erasure); err != nil { + panic(fmt.Errorf("failed to confirm erasure: %w", err)) + } + + // Clear the data root + rootKey := a.makeRootKey(vitaID, dataType) + ic.DAO.DeleteStorageItem(a.ID, rootKey) + + // ARCH-005: Audit log erasure confirmation (GDPR Article 17 fulfilled) + // This audit entry proves we complied with the erasure request + // Note: The on-chain hash of this audit entry remains as proof of compliance + a.Audit.LogCompliance(ic.DAO, caller, erasure.RequestedBy, "ancora.erasure_confirmed", + fmt.Sprintf("vitaID=%d dataType=%d requestedAt=%d", vitaID, dataType, erasure.RequestedAt), + ic.BlockHeight()) + + ic.AddNotification(a.Hash, "ErasureConfirmed", stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(dataType))), + stackitem.NewByteArray(caller.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (a *Ancora) denyErasure(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + reason := toString(args[2]) + + // Check grace period + erasure := a.getErasureInfoInternal(ic.DAO, vitaID, dataType) + if erasure == nil || erasure.Status != state.ErasurePending { + panic(ErrAncoraErasureNotPending) + } + + cache := ic.DAO.GetROCache(a.ID).(*AncoraCache) + if ic.BlockHeight() < erasure.RequestedAt+cache.config.ErasureGracePeriod { + panic(ErrAncoraErasureGracePeriod) + } + + // Verify caller is authorized provider or committee + caller := ic.VM.GetCallingScriptHash() + providerCfg := a.getProviderConfig(ic.DAO, dataType, caller) + if (providerCfg == nil || !providerCfg.Active) && !a.Tutus.CheckCommittee(ic) { + panic(ErrAncoraUnauthorized) + } + + erasure.Status = state.ErasureDenied + erasure.ProcessedAt = ic.BlockHeight() + erasure.ConfirmedBy = caller + erasure.DeniedReason = reason + + if err := a.putErasureInfo(ic.DAO, vitaID, dataType, erasure); err != nil { + panic(fmt.Errorf("failed to deny erasure: %w", err)) + } + + // ARCH-005: Audit log erasure denial (GDPR Article 17 exception invoked) + // Critical for legal compliance - documents the legal basis for denial + a.Audit.LogCompliance(ic.DAO, caller, erasure.RequestedBy, "ancora.erasure_denied", + fmt.Sprintf("vitaID=%d dataType=%d reason=%s", vitaID, dataType, reason), + ic.BlockHeight()) + + ic.AddNotification(a.Hash, "ErasureDenied", stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(dataType))), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +func (a *Ancora) getErasureInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataType := state.DataType(toUint32(args[1])) + + erasure := a.getErasureInfoFromDAO(ic.DAO, vitaID, dataType) + if erasure == nil { + return stackitem.Null{} + } + item, err := erasure.ToStackItem() + if err != nil { + panic(fmt.Errorf("failed to convert erasure to stack item: %w", err)) + } + return item +} + +func (a *Ancora) getErasureInfoFromDAO(d *dao.Simple, vitaID uint64, dataType state.DataType) *state.ErasureInfo { + return a.getErasureInfoInternal(d, vitaID, dataType) +} + +func (a *Ancora) getErasureInfoInternal(d *dao.Simple, vitaID uint64, dataType state.DataType) *state.ErasureInfo { + key := a.makeErasureKey(vitaID, dataType) + erasure := new(state.ErasureInfo) + err := getConvertibleFromDAO(a.ID, d, key, erasure) + if err != nil { + if errors.Is(err, storage.ErrKeyNotFound) { + return nil + } + panic(fmt.Errorf("failed to get erasure info: %w", err)) + } + return erasure +} + +func (a *Ancora) putErasureInfo(d *dao.Simple, vitaID uint64, dataType state.DataType, erasure *state.ErasureInfo) error { + key := a.makeErasureKey(vitaID, dataType) + return putConvertibleToDAO(a.ID, d, key, erasure) +} + +func (a *Ancora) makeErasureKey(vitaID uint64, dataType state.DataType) []byte { + key := make([]byte, 1+8+1) + key[0] = ancoraErasurePrefix + putUint64(key[1:9], vitaID) + key[9] = byte(dataType) + return key +} + +// ========== Data Portability ========== + +func (a *Ancora) generatePortabilityAttestation(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + dataTypesArr, ok := args[1].Value().([]stackitem.Item) + if !ok { + panic("invalid dataTypes array") + } + + // Verify caller is Vita owner + owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) + ok2, err := runtime.CheckHashedWitness(ic, owner) + if err != nil || !ok2 { + panic(ErrAncoraUnauthorized) + } + + // Build attestation data + attestData := make([]byte, 0, 8+4+32*len(dataTypesArr)) + attestData = appendUint64(attestData, vitaID) + attestData = appendUint32(attestData, ic.BlockHeight()) + + for _, item := range dataTypesArr { + dataType := state.DataType(toUint32(item)) + rootInfo := a.getRootInfo(ic.DAO, vitaID, dataType) + if rootInfo != nil { + attestData = append(attestData, byte(dataType)) + attestData = append(attestData, rootInfo.Root...) + } + } + + // Hash the attestation + attestHash := hash.Sha256(attestData).BytesBE() + + // Store attestation with expiry + cache := ic.DAO.GetROCache(a.ID).(*AncoraCache) + expiryBlock := ic.BlockHeight() + cache.config.AttestationValidBlocks + + key := append([]byte{ancoraAttestationPrefix}, attestHash...) + value := make([]byte, 4) + putUint32(value, expiryBlock) + ic.DAO.PutStorageItem(a.ID, key, value) + + ic.AddNotification(a.Hash, "PortabilityAttestationGenerated", stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewByteArray(attestHash), + stackitem.NewBigInteger(big.NewInt(int64(expiryBlock))), + })) + + return stackitem.NewByteArray(attestData) +} + +func (a *Ancora) verifyPortabilityAttestation(ic *interop.Context, args []stackitem.Item) stackitem.Item { + attestation, err := args[0].TryBytes() + if err != nil { + panic(ErrAncoraInvalidAttestation) + } + + attestHash := hash.Sha256(attestation).BytesBE() + key := append([]byte{ancoraAttestationPrefix}, attestHash...) + + value := ic.DAO.GetStorageItem(a.ID, key) + if value == nil { + return stackitem.NewBool(false) + } + + expiryBlock := getUint32(value) + if ic.BlockHeight() > expiryBlock { + return stackitem.NewBool(false) + } + + return stackitem.NewBool(true) +} + +// ========== Audit Log Query ========== + +// getAuditLog retrieves audit log entries for a specific Vita or globally. +// This provides GDPR-compliant transparency: citizens can see who accessed their data. +func (a *Ancora) getAuditLog(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + startBlock := toUint32(args[1]) + endBlock := toUint32(args[2]) + limit := int(toUint32(args[3])) + + if limit <= 0 || limit > 100 { + limit = 100 // Cap at 100 entries per query + } + + // If vitaID is specified, verify caller is the owner or committee + if vitaID > 0 { + owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) + caller := ic.VM.GetCallingScriptHash() + + // Check if caller is the Vita owner + ok, err := runtime.CheckHashedWitness(ic, owner) + if err != nil || !ok { + // Not the owner - check if committee + if !a.Tutus.CheckCommittee(ic) { + // Check if caller is an authorized provider for this data + isProvider := false + for dt := state.DataType(0); dt <= state.DataTypeCustom; dt++ { + if cfg := a.getProviderConfig(ic.DAO, dt, caller); cfg != nil && cfg.Active { + isProvider = true + break + } + } + if !isProvider { + panic(ErrAncoraUnauthorized) + } + } + } + } else { + // Global audit query - requires committee + if !a.Tutus.CheckCommittee(ic) { + panic(ErrAncoraUnauthorized) + } + } + + // Build query + query := &AuditQuery{ + StartBlock: startBlock, + EndBlock: endBlock, + Limit: limit, + } + + // If vitaID specified, query by target (owner of Vita) + if vitaID > 0 { + owner := a.Vita.OwnerOfInternal(ic.DAO, vitaID) + query.Target = &owner + } + + // Query the audit log + entries := a.Audit.Query(ic.DAO, query) + + // Convert entries to stack items + items := make([]stackitem.Item, 0, len(entries)) + for _, entry := range entries { + items = append(items, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(entry.EntryID))), + stackitem.NewBigInteger(big.NewInt(int64(entry.Timestamp))), + stackitem.NewBigInteger(big.NewInt(int64(entry.Category))), + stackitem.NewBigInteger(big.NewInt(int64(entry.Severity))), + stackitem.NewBigInteger(big.NewInt(int64(entry.Outcome))), + stackitem.NewByteArray(entry.Actor.BytesBE()), + stackitem.NewByteArray(entry.Target.BytesBE()), + stackitem.NewByteArray([]byte(entry.Action)), + stackitem.NewByteArray(entry.ResourceID), + stackitem.NewByteArray([]byte(entry.Details)), + })) + } + + return stackitem.NewArray(items) +} + +// ========== Rate Limiting Helpers ========== + +func (a *Ancora) checkRateLimit(ic *interop.Context, cfg *state.ProviderConfig, vitaID uint64, dataType state.DataType) error { + // Check per-block rate limit + blockHeight := ic.BlockHeight() + updateCount := a.getUpdateCount(ic.DAO, blockHeight, cfg.Provider) + if updateCount >= cfg.MaxUpdatesPerBlock { + return ErrAncoraRateLimited + } + + // Check per-vitaID cooldown + lastUpdate := a.getLastUpdate(ic.DAO, vitaID, dataType, cfg.Provider) + if lastUpdate > 0 && blockHeight < lastUpdate+cfg.UpdateCooldown { + return ErrAncoraUpdateCooldown + } + + return nil +} + +func (a *Ancora) recordUpdate(d *dao.Simple, blockHeight uint32, provider util.Uint160, vitaID uint64, dataType state.DataType) { + // Increment block update count + countKey := append([]byte{ancoraUpdateCountPrefix}, provider.BytesBE()...) + countKey = appendUint32(countKey, blockHeight) + + var count uint32 + data := d.GetStorageItem(a.ID, countKey) + if data != nil && len(data) >= 4 { + count = getUint32(data) + } + count++ + countData := make([]byte, 4) + putUint32(countData, count) + d.PutStorageItem(a.ID, countKey, countData) + + // Record last update for vitaID+dataType+provider + lastKey := a.makeLastUpdateKey(vitaID, dataType, provider) + lastData := make([]byte, 4) + putUint32(lastData, blockHeight) + d.PutStorageItem(a.ID, lastKey, lastData) +} + +func (a *Ancora) getUpdateCount(d *dao.Simple, blockHeight uint32, provider util.Uint160) uint32 { + countKey := append([]byte{ancoraUpdateCountPrefix}, provider.BytesBE()...) + countKey = appendUint32(countKey, blockHeight) + + data := d.GetStorageItem(a.ID, countKey) + if data == nil || len(data) < 4 { + return 0 + } + return getUint32(data) +} + +func (a *Ancora) getLastUpdate(d *dao.Simple, vitaID uint64, dataType state.DataType, provider util.Uint160) uint32 { + key := a.makeLastUpdateKey(vitaID, dataType, provider) + data := d.GetStorageItem(a.ID, key) + if data == nil || len(data) < 4 { + return 0 + } + return getUint32(data) +} + +func (a *Ancora) makeLastUpdateKey(vitaID uint64, dataType state.DataType, provider util.Uint160) []byte { + key := make([]byte, 1+8+1+20) + key[0] = ancoraLastUpdatePrefix + putUint64(key[1:9], vitaID) + key[9] = byte(dataType) + copy(key[10:30], provider.BytesBE()) + return key +} + +// ========== Utility Helpers ========== + +func (a *Ancora) getCache(d *dao.Simple) *AncoraCache { + if d == nil { + return nil + } + cache := d.GetROCache(a.ID) + if cache == nil { + return nil + } + return cache.(*AncoraCache) +} + +func putUint32(b []byte, v uint32) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) +} + +func getUint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func putUint64(b []byte, v uint64) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} + +func appendUint32(b []byte, v uint32) []byte { + return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24)) +} + +func appendUint64(b []byte, v uint64) []byte { + return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24), + byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56)) +} diff --git a/pkg/core/native/annos.go b/pkg/core/native/annos.go index fcd81b7..340f3ac 100644 --- a/pkg/core/native/annos.go +++ b/pkg/core/native/annos.go @@ -5,17 +5,17 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Annos represents the lifespan/years native contract for tracking Vita holder ages. diff --git a/pkg/core/native/audit_logger.go b/pkg/core/native/audit_logger.go index 39a5dd0..3919bd9 100644 --- a/pkg/core/native/audit_logger.go +++ b/pkg/core/native/audit_logger.go @@ -1,776 +1,776 @@ -package native - -import ( - "encoding/binary" - - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" -) - -// ARCH-005: Comprehensive Audit Logging -// Provides structured audit logging for all sensitive operations, -// supporting compliance requirements, incident investigation, and -// security monitoring. - -// AuditCategory categorizes audit events. -type AuditCategory uint8 - -const ( - AuditCategoryAuth AuditCategory = iota - AuditCategoryAccess - AuditCategoryData - AuditCategoryGovernance - AuditCategoryFinancial - AuditCategorySecurity - AuditCategorySystem - AuditCategoryCompliance -) - -// AuditSeverity indicates the importance of an audit event. -type AuditSeverity uint8 - -const ( - AuditSeverityInfo AuditSeverity = iota - AuditSeverityNotice - AuditSeverityWarning - AuditSeverityAlert - AuditSeverityCritical -) - -// AuditOutcome indicates the result of an audited operation. -type AuditOutcome uint8 - -const ( - AuditOutcomeSuccess AuditOutcome = iota - AuditOutcomeFailure - AuditOutcomeDenied - AuditOutcomeError - AuditOutcomePartial -) - -// AuditEntry represents a single audit log entry. -type AuditEntry struct { - // EntryID is unique identifier for this entry - EntryID uint64 - // Timestamp is the block height when event occurred - Timestamp uint32 - // Category classifies the event - Category AuditCategory - // Severity indicates importance - Severity AuditSeverity - // Outcome is the result of the operation - Outcome AuditOutcome - // ContractID identifies the originating contract - ContractID int32 - // Actor is who performed the action - Actor util.Uint160 - // Target is the subject of the action (if applicable) - Target util.Uint160 - // Action is the operation performed - Action string - // ResourceID identifies the affected resource - ResourceID []byte - // Details contains additional context - Details string - // IPHash is hash of source IP (for off-chain correlation) - IPHash util.Uint256 - // PreviousState is hash of state before change - PreviousState util.Uint256 - // NewState is hash of state after change - NewState util.Uint256 -} - -// AuditQuery defines parameters for searching audit logs. -type AuditQuery struct { - // StartBlock is the earliest block to search - StartBlock uint32 - // EndBlock is the latest block to search - EndBlock uint32 - // Category filters by event category (nil = all) - Category *AuditCategory - // Severity filters by minimum severity - MinSeverity *AuditSeverity - // Actor filters by actor address - Actor *util.Uint160 - // Target filters by target address - Target *util.Uint160 - // ContractID filters by contract - ContractID *int32 - // Limit is maximum results to return - Limit int - // Offset is the starting position - Offset int -} - -// Storage prefixes for audit logging. -const ( - auditPrefixEntry byte = 0xA0 // entryID -> AuditEntry - auditPrefixByBlock byte = 0xA1 // block + entryID -> exists - auditPrefixByActor byte = 0xA2 // actor + block + entryID -> exists - auditPrefixByTarget byte = 0xA3 // target + block + entryID -> exists - auditPrefixByCategory byte = 0xA4 // category + block + entryID -> exists - auditPrefixBySeverity byte = 0xA5 // severity + block + entryID -> exists - auditPrefixByContract byte = 0xA6 // contractID + block + entryID -> exists - auditPrefixCounter byte = 0xAF // -> next entryID - auditPrefixRetention byte = 0xAE // -> retention config -) - -// AuditLogger provides comprehensive audit logging. -type AuditLogger struct { - contractID int32 -} - -// NewAuditLogger creates a new audit logger. -func NewAuditLogger(contractID int32) *AuditLogger { - return &AuditLogger{contractID: contractID} -} - -// Log records an audit entry. -func (al *AuditLogger) Log(d *dao.Simple, entry *AuditEntry) uint64 { - // Get and increment entry counter - entry.EntryID = al.getNextEntryID(d) - - // Store main entry - al.putEntry(d, entry) - - // Create indices for efficient querying - al.indexEntry(d, entry) - - return entry.EntryID -} - -// LogAuth logs an authentication event. -func (al *AuditLogger) LogAuth(d *dao.Simple, actor util.Uint160, action string, outcome AuditOutcome, - blockHeight uint32, details string) uint64 { - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategoryAuth, - Severity: al.severityForOutcome(outcome, AuditCategoryAuth), - Outcome: outcome, - Actor: actor, - Action: action, - Details: details, - }) -} - -// LogAccess logs a data access event. -func (al *AuditLogger) LogAccess(d *dao.Simple, actor, target util.Uint160, action string, - resourceID []byte, outcome AuditOutcome, blockHeight uint32) uint64 { - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategoryAccess, - Severity: al.severityForOutcome(outcome, AuditCategoryAccess), - Outcome: outcome, - Actor: actor, - Target: target, - Action: action, - ResourceID: resourceID, - }) -} - -// LogDataChange logs a data modification event. -func (al *AuditLogger) LogDataChange(d *dao.Simple, actor util.Uint160, contractID int32, - action string, resourceID []byte, prevState, newState util.Uint256, blockHeight uint32) uint64 { - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategoryData, - Severity: AuditSeverityNotice, - Outcome: AuditOutcomeSuccess, - ContractID: contractID, - Actor: actor, - Action: action, - ResourceID: resourceID, - PreviousState: prevState, - NewState: newState, - }) -} - -// LogGovernance logs a governance action. -func (al *AuditLogger) LogGovernance(d *dao.Simple, actor util.Uint160, action string, - details string, outcome AuditOutcome, blockHeight uint32) uint64 { - severity := AuditSeverityNotice - if outcome == AuditOutcomeSuccess { - severity = AuditSeverityWarning // Governance changes warrant attention - } - - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategoryGovernance, - Severity: severity, - Outcome: outcome, - Actor: actor, - Action: action, - Details: details, - }) -} - -// LogFinancial logs a financial transaction. -func (al *AuditLogger) LogFinancial(d *dao.Simple, actor, target util.Uint160, - action string, resourceID []byte, outcome AuditOutcome, blockHeight uint32) uint64 { - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategoryFinancial, - Severity: AuditSeverityNotice, - Outcome: outcome, - Actor: actor, - Target: target, - Action: action, - ResourceID: resourceID, - }) -} - -// LogSecurity logs a security-related event. -func (al *AuditLogger) LogSecurity(d *dao.Simple, actor util.Uint160, action string, - severity AuditSeverity, outcome AuditOutcome, details string, blockHeight uint32) uint64 { - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategorySecurity, - Severity: severity, - Outcome: outcome, - Actor: actor, - Action: action, - Details: details, - }) -} - -// LogCompliance logs a compliance-related event. -func (al *AuditLogger) LogCompliance(d *dao.Simple, actor, target util.Uint160, - action string, details string, blockHeight uint32) uint64 { - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategoryCompliance, - Severity: AuditSeverityNotice, - Outcome: AuditOutcomeSuccess, - Actor: actor, - Target: target, - Action: action, - Details: details, - }) -} - -// LogRightsAccess logs access to rights-protected resources. -func (al *AuditLogger) LogRightsAccess(d *dao.Simple, actor, subject util.Uint160, - rightID uint8, action string, outcome AuditOutcome, blockHeight uint32) uint64 { - return al.Log(d, &AuditEntry{ - Timestamp: blockHeight, - Category: AuditCategoryCompliance, - Severity: AuditSeverityWarning, - Outcome: outcome, - Actor: actor, - Target: subject, - Action: action, - ResourceID: []byte{rightID}, - }) -} - -// GetEntry retrieves an audit entry by ID. -func (al *AuditLogger) GetEntry(d *dao.Simple, entryID uint64) *AuditEntry { - key := al.makeEntryKey(entryID) - si := d.GetStorageItem(al.contractID, key) - if si == nil { - return nil - } - return al.deserializeEntry(si) -} - -// Query searches audit logs based on query parameters. -func (al *AuditLogger) Query(d *dao.Simple, query *AuditQuery) []*AuditEntry { - var entries []*AuditEntry - var entryIDs []uint64 - - // Choose the most selective index - if query.Actor != nil { - entryIDs = al.queryByActor(d, *query.Actor, query.StartBlock, query.EndBlock, query.Limit+query.Offset) - } else if query.Target != nil { - entryIDs = al.queryByTarget(d, *query.Target, query.StartBlock, query.EndBlock, query.Limit+query.Offset) - } else if query.Category != nil { - entryIDs = al.queryByCategory(d, *query.Category, query.StartBlock, query.EndBlock, query.Limit+query.Offset) - } else if query.MinSeverity != nil { - entryIDs = al.queryBySeverity(d, *query.MinSeverity, query.StartBlock, query.EndBlock, query.Limit+query.Offset) - } else { - entryIDs = al.queryByBlock(d, query.StartBlock, query.EndBlock, query.Limit+query.Offset) - } - - // Apply offset and limit - start := query.Offset - if start > len(entryIDs) { - return entries - } - end := start + query.Limit - if end > len(entryIDs) { - end = len(entryIDs) - } - - // Fetch entries - for _, id := range entryIDs[start:end] { - if entry := al.GetEntry(d, id); entry != nil { - // Apply additional filters - if al.matchesQuery(entry, query) { - entries = append(entries, entry) - } - } - } - - return entries -} - -// GetEntriesForActor retrieves audit entries for a specific actor. -func (al *AuditLogger) GetEntriesForActor(d *dao.Simple, actor util.Uint160, limit int) []*AuditEntry { - return al.Query(d, &AuditQuery{ - Actor: &actor, - Limit: limit, - }) -} - -// GetEntriesForTarget retrieves audit entries for a specific target. -func (al *AuditLogger) GetEntriesForTarget(d *dao.Simple, target util.Uint160, limit int) []*AuditEntry { - return al.Query(d, &AuditQuery{ - Target: &target, - Limit: limit, - }) -} - -// GetSecurityAlerts retrieves high-severity security events. -func (al *AuditLogger) GetSecurityAlerts(d *dao.Simple, startBlock, endBlock uint32, limit int) []*AuditEntry { - severity := AuditSeverityAlert - category := AuditCategorySecurity - return al.Query(d, &AuditQuery{ - StartBlock: startBlock, - EndBlock: endBlock, - Category: &category, - MinSeverity: &severity, - Limit: limit, - }) -} - -// GetComplianceLog retrieves compliance-related entries for reporting. -func (al *AuditLogger) GetComplianceLog(d *dao.Simple, startBlock, endBlock uint32, limit int) []*AuditEntry { - category := AuditCategoryCompliance - return al.Query(d, &AuditQuery{ - StartBlock: startBlock, - EndBlock: endBlock, - Category: &category, - Limit: limit, - }) -} - -// severityForOutcome determines severity based on outcome and category. -func (al *AuditLogger) severityForOutcome(outcome AuditOutcome, category AuditCategory) AuditSeverity { - switch outcome { - case AuditOutcomeSuccess: - return AuditSeverityInfo - case AuditOutcomeFailure: - return AuditSeverityNotice - case AuditOutcomeDenied: - if category == AuditCategorySecurity || category == AuditCategoryAuth { - return AuditSeverityAlert - } - return AuditSeverityWarning - case AuditOutcomeError: - return AuditSeverityWarning - default: - return AuditSeverityInfo - } -} - -// matchesQuery checks if an entry matches additional query filters. -func (al *AuditLogger) matchesQuery(entry *AuditEntry, query *AuditQuery) bool { - if query.Category != nil && entry.Category != *query.Category { - return false - } - if query.MinSeverity != nil && entry.Severity < *query.MinSeverity { - return false - } - if query.ContractID != nil && entry.ContractID != *query.ContractID { - return false - } - if query.Actor != nil && entry.Actor != *query.Actor { - return false - } - if query.Target != nil && entry.Target != *query.Target { - return false - } - return true -} - -// Index query methods. -func (al *AuditLogger) queryByBlock(d *dao.Simple, startBlock, endBlock uint32, limit int) []uint64 { - var ids []uint64 - prefix := []byte{auditPrefixByBlock} - - d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 12 { - return true - } - block := binary.BigEndian.Uint32(k[0:4]) - if block < startBlock || (endBlock > 0 && block > endBlock) { - return true - } - entryID := binary.BigEndian.Uint64(k[4:12]) - ids = append(ids, entryID) - return len(ids) < limit - }) - - return ids -} - -func (al *AuditLogger) queryByActor(d *dao.Simple, actor util.Uint160, startBlock, endBlock uint32, limit int) []uint64 { - var ids []uint64 - prefix := make([]byte, 21) - prefix[0] = auditPrefixByActor - copy(prefix[1:], actor.BytesBE()) - - d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 12 { - return true - } - block := binary.BigEndian.Uint32(k[0:4]) - if block < startBlock || (endBlock > 0 && block > endBlock) { - return true - } - entryID := binary.BigEndian.Uint64(k[4:12]) - ids = append(ids, entryID) - return len(ids) < limit - }) - - return ids -} - -func (al *AuditLogger) queryByTarget(d *dao.Simple, target util.Uint160, startBlock, endBlock uint32, limit int) []uint64 { - var ids []uint64 - prefix := make([]byte, 21) - prefix[0] = auditPrefixByTarget - copy(prefix[1:], target.BytesBE()) - - d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 12 { - return true - } - block := binary.BigEndian.Uint32(k[0:4]) - if block < startBlock || (endBlock > 0 && block > endBlock) { - return true - } - entryID := binary.BigEndian.Uint64(k[4:12]) - ids = append(ids, entryID) - return len(ids) < limit - }) - - return ids -} - -func (al *AuditLogger) queryByCategory(d *dao.Simple, category AuditCategory, startBlock, endBlock uint32, limit int) []uint64 { - var ids []uint64 - prefix := []byte{auditPrefixByCategory, byte(category)} - - d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 12 { - return true - } - block := binary.BigEndian.Uint32(k[0:4]) - if block < startBlock || (endBlock > 0 && block > endBlock) { - return true - } - entryID := binary.BigEndian.Uint64(k[4:12]) - ids = append(ids, entryID) - return len(ids) < limit - }) - - return ids -} - -func (al *AuditLogger) queryBySeverity(d *dao.Simple, minSeverity AuditSeverity, startBlock, endBlock uint32, limit int) []uint64 { - var ids []uint64 - - for sev := minSeverity; sev <= AuditSeverityCritical; sev++ { - prefix := []byte{auditPrefixBySeverity, byte(sev)} - - d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 12 { - return true - } - block := binary.BigEndian.Uint32(k[0:4]) - if block < startBlock || (endBlock > 0 && block > endBlock) { - return true - } - entryID := binary.BigEndian.Uint64(k[4:12]) - ids = append(ids, entryID) - return len(ids) < limit - }) - - if len(ids) >= limit { - break - } - } - - return ids -} - -// Storage key helpers. -func (al *AuditLogger) makeEntryKey(entryID uint64) []byte { - key := make([]byte, 9) - key[0] = auditPrefixEntry - binary.BigEndian.PutUint64(key[1:], entryID) - return key -} - -func (al *AuditLogger) getNextEntryID(d *dao.Simple) uint64 { - key := []byte{auditPrefixCounter} - si := d.GetStorageItem(al.contractID, key) - - var nextID uint64 = 1 - if si != nil && len(si) >= 8 { - nextID = binary.BigEndian.Uint64(si) + 1 - } - - data := make([]byte, 8) - binary.BigEndian.PutUint64(data, nextID) - d.PutStorageItem(al.contractID, key, data) - - return nextID -} - -func (al *AuditLogger) putEntry(d *dao.Simple, entry *AuditEntry) { - key := al.makeEntryKey(entry.EntryID) - data := al.serializeEntry(entry) - d.PutStorageItem(al.contractID, key, data) -} - -func (al *AuditLogger) indexEntry(d *dao.Simple, entry *AuditEntry) { - // Index by block - blockKey := make([]byte, 13) - blockKey[0] = auditPrefixByBlock - binary.BigEndian.PutUint32(blockKey[1:5], entry.Timestamp) - binary.BigEndian.PutUint64(blockKey[5:13], entry.EntryID) - d.PutStorageItem(al.contractID, blockKey, []byte{1}) - - // Index by actor - if entry.Actor != (util.Uint160{}) { - actorKey := make([]byte, 33) - actorKey[0] = auditPrefixByActor - copy(actorKey[1:21], entry.Actor.BytesBE()) - binary.BigEndian.PutUint32(actorKey[21:25], entry.Timestamp) - binary.BigEndian.PutUint64(actorKey[25:33], entry.EntryID) - d.PutStorageItem(al.contractID, actorKey, []byte{1}) - } - - // Index by target - if entry.Target != (util.Uint160{}) { - targetKey := make([]byte, 33) - targetKey[0] = auditPrefixByTarget - copy(targetKey[1:21], entry.Target.BytesBE()) - binary.BigEndian.PutUint32(targetKey[21:25], entry.Timestamp) - binary.BigEndian.PutUint64(targetKey[25:33], entry.EntryID) - d.PutStorageItem(al.contractID, targetKey, []byte{1}) - } - - // Index by category - catKey := make([]byte, 14) - catKey[0] = auditPrefixByCategory - catKey[1] = byte(entry.Category) - binary.BigEndian.PutUint32(catKey[2:6], entry.Timestamp) - binary.BigEndian.PutUint64(catKey[6:14], entry.EntryID) - d.PutStorageItem(al.contractID, catKey, []byte{1}) - - // Index by severity - sevKey := make([]byte, 14) - sevKey[0] = auditPrefixBySeverity - sevKey[1] = byte(entry.Severity) - binary.BigEndian.PutUint32(sevKey[2:6], entry.Timestamp) - binary.BigEndian.PutUint64(sevKey[6:14], entry.EntryID) - d.PutStorageItem(al.contractID, sevKey, []byte{1}) - - // Index by contract - if entry.ContractID != 0 { - contractKey := make([]byte, 17) - contractKey[0] = auditPrefixByContract - binary.BigEndian.PutUint32(contractKey[1:5], uint32(entry.ContractID)) - binary.BigEndian.PutUint32(contractKey[5:9], entry.Timestamp) - binary.BigEndian.PutUint64(contractKey[9:17], entry.EntryID) - d.PutStorageItem(al.contractID, contractKey, []byte{1}) - } -} - -// Serialization helpers. -func (al *AuditLogger) serializeEntry(e *AuditEntry) []byte { - actionBytes := []byte(e.Action) - detailsBytes := []byte(e.Details) - - size := 8 + 4 + 1 + 1 + 1 + 4 + 20 + 20 + - 4 + len(actionBytes) + - 4 + len(e.ResourceID) + - 4 + len(detailsBytes) + - 32 + 32 + 32 - - data := make([]byte, size) - offset := 0 - - binary.BigEndian.PutUint64(data[offset:], e.EntryID) - offset += 8 - binary.BigEndian.PutUint32(data[offset:], e.Timestamp) - offset += 4 - data[offset] = byte(e.Category) - offset++ - data[offset] = byte(e.Severity) - offset++ - data[offset] = byte(e.Outcome) - offset++ - binary.BigEndian.PutUint32(data[offset:], uint32(e.ContractID)) - offset += 4 - copy(data[offset:], e.Actor.BytesBE()) - offset += 20 - copy(data[offset:], e.Target.BytesBE()) - offset += 20 - - binary.BigEndian.PutUint32(data[offset:], uint32(len(actionBytes))) - offset += 4 - copy(data[offset:], actionBytes) - offset += len(actionBytes) - - binary.BigEndian.PutUint32(data[offset:], uint32(len(e.ResourceID))) - offset += 4 - copy(data[offset:], e.ResourceID) - offset += len(e.ResourceID) - - binary.BigEndian.PutUint32(data[offset:], uint32(len(detailsBytes))) - offset += 4 - copy(data[offset:], detailsBytes) - offset += len(detailsBytes) - - copy(data[offset:], e.IPHash[:]) - offset += 32 - copy(data[offset:], e.PreviousState[:]) - offset += 32 - copy(data[offset:], e.NewState[:]) - - return data -} - -func (al *AuditLogger) deserializeEntry(data []byte) *AuditEntry { - if len(data) < 60 { - return nil - } - - e := &AuditEntry{} - offset := 0 - - e.EntryID = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - e.Timestamp = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - e.Category = AuditCategory(data[offset]) - offset++ - e.Severity = AuditSeverity(data[offset]) - offset++ - e.Outcome = AuditOutcome(data[offset]) - offset++ - e.ContractID = int32(binary.BigEndian.Uint32(data[offset:])) - offset += 4 - e.Actor, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) - offset += 20 - e.Target, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) - offset += 20 - - if offset+4 > len(data) { - return nil - } - actionLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(actionLen) > len(data) { - return nil - } - e.Action = string(data[offset : offset+int(actionLen)]) - offset += int(actionLen) - - if offset+4 > len(data) { - return nil - } - resourceLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(resourceLen) > len(data) { - return nil - } - e.ResourceID = make([]byte, resourceLen) - copy(e.ResourceID, data[offset:offset+int(resourceLen)]) - offset += int(resourceLen) - - if offset+4 > len(data) { - return nil - } - detailsLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(detailsLen) > len(data) { - return nil - } - e.Details = string(data[offset : offset+int(detailsLen)]) - offset += int(detailsLen) - - if offset+96 > len(data) { - return e // Return partial if hashes missing - } - copy(e.IPHash[:], data[offset:offset+32]) - offset += 32 - copy(e.PreviousState[:], data[offset:offset+32]) - offset += 32 - copy(e.NewState[:], data[offset:offset+32]) - - return e -} - -// StandardAuditActions defines common audit action names. -var StandardAuditActions = struct { - // Auth actions - AuthLogin string - AuthLogout string - AuthFailedLogin string - AuthRoleAssigned string - AuthRoleRevoked string - - // Access actions - AccessRead string - AccessWrite string - AccessDelete string - AccessDenied string - - // Financial actions - FinTransfer string - FinMint string - FinBurn string - FinInvest string - FinWithdraw string - - // Governance actions - GovProposalCreate string - GovVote string - GovProposalPass string - GovProposalReject string - - // Security actions - SecCircuitTrip string - SecRollback string - SecInvariantFail string - SecRightsRestrict string -}{ - AuthLogin: "auth.login", - AuthLogout: "auth.logout", - AuthFailedLogin: "auth.failed_login", - AuthRoleAssigned: "auth.role_assigned", - AuthRoleRevoked: "auth.role_revoked", - AccessRead: "access.read", - AccessWrite: "access.write", - AccessDelete: "access.delete", - AccessDenied: "access.denied", - FinTransfer: "financial.transfer", - FinMint: "financial.mint", - FinBurn: "financial.burn", - FinInvest: "financial.invest", - FinWithdraw: "financial.withdraw", - GovProposalCreate: "governance.proposal_create", - GovVote: "governance.vote", - GovProposalPass: "governance.proposal_pass", - GovProposalReject: "governance.proposal_reject", - SecCircuitTrip: "security.circuit_trip", - SecRollback: "security.rollback", - SecInvariantFail: "security.invariant_fail", - SecRightsRestrict: "security.rights_restrict", -} +package native + +import ( + "encoding/binary" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" +) + +// ARCH-005: Comprehensive Audit Logging +// Provides structured audit logging for all sensitive operations, +// supporting compliance requirements, incident investigation, and +// security monitoring. + +// AuditCategory categorizes audit events. +type AuditCategory uint8 + +const ( + AuditCategoryAuth AuditCategory = iota + AuditCategoryAccess + AuditCategoryData + AuditCategoryGovernance + AuditCategoryFinancial + AuditCategorySecurity + AuditCategorySystem + AuditCategoryCompliance +) + +// AuditSeverity indicates the importance of an audit event. +type AuditSeverity uint8 + +const ( + AuditSeverityInfo AuditSeverity = iota + AuditSeverityNotice + AuditSeverityWarning + AuditSeverityAlert + AuditSeverityCritical +) + +// AuditOutcome indicates the result of an audited operation. +type AuditOutcome uint8 + +const ( + AuditOutcomeSuccess AuditOutcome = iota + AuditOutcomeFailure + AuditOutcomeDenied + AuditOutcomeError + AuditOutcomePartial +) + +// AuditEntry represents a single audit log entry. +type AuditEntry struct { + // EntryID is unique identifier for this entry + EntryID uint64 + // Timestamp is the block height when event occurred + Timestamp uint32 + // Category classifies the event + Category AuditCategory + // Severity indicates importance + Severity AuditSeverity + // Outcome is the result of the operation + Outcome AuditOutcome + // ContractID identifies the originating contract + ContractID int32 + // Actor is who performed the action + Actor util.Uint160 + // Target is the subject of the action (if applicable) + Target util.Uint160 + // Action is the operation performed + Action string + // ResourceID identifies the affected resource + ResourceID []byte + // Details contains additional context + Details string + // IPHash is hash of source IP (for off-chain correlation) + IPHash util.Uint256 + // PreviousState is hash of state before change + PreviousState util.Uint256 + // NewState is hash of state after change + NewState util.Uint256 +} + +// AuditQuery defines parameters for searching audit logs. +type AuditQuery struct { + // StartBlock is the earliest block to search + StartBlock uint32 + // EndBlock is the latest block to search + EndBlock uint32 + // Category filters by event category (nil = all) + Category *AuditCategory + // Severity filters by minimum severity + MinSeverity *AuditSeverity + // Actor filters by actor address + Actor *util.Uint160 + // Target filters by target address + Target *util.Uint160 + // ContractID filters by contract + ContractID *int32 + // Limit is maximum results to return + Limit int + // Offset is the starting position + Offset int +} + +// Storage prefixes for audit logging. +const ( + auditPrefixEntry byte = 0xA0 // entryID -> AuditEntry + auditPrefixByBlock byte = 0xA1 // block + entryID -> exists + auditPrefixByActor byte = 0xA2 // actor + block + entryID -> exists + auditPrefixByTarget byte = 0xA3 // target + block + entryID -> exists + auditPrefixByCategory byte = 0xA4 // category + block + entryID -> exists + auditPrefixBySeverity byte = 0xA5 // severity + block + entryID -> exists + auditPrefixByContract byte = 0xA6 // contractID + block + entryID -> exists + auditPrefixCounter byte = 0xAF // -> next entryID + auditPrefixRetention byte = 0xAE // -> retention config +) + +// AuditLogger provides comprehensive audit logging. +type AuditLogger struct { + contractID int32 +} + +// NewAuditLogger creates a new audit logger. +func NewAuditLogger(contractID int32) *AuditLogger { + return &AuditLogger{contractID: contractID} +} + +// Log records an audit entry. +func (al *AuditLogger) Log(d *dao.Simple, entry *AuditEntry) uint64 { + // Get and increment entry counter + entry.EntryID = al.getNextEntryID(d) + + // Store main entry + al.putEntry(d, entry) + + // Create indices for efficient querying + al.indexEntry(d, entry) + + return entry.EntryID +} + +// LogAuth logs an authentication event. +func (al *AuditLogger) LogAuth(d *dao.Simple, actor util.Uint160, action string, outcome AuditOutcome, + blockHeight uint32, details string) uint64 { + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategoryAuth, + Severity: al.severityForOutcome(outcome, AuditCategoryAuth), + Outcome: outcome, + Actor: actor, + Action: action, + Details: details, + }) +} + +// LogAccess logs a data access event. +func (al *AuditLogger) LogAccess(d *dao.Simple, actor, target util.Uint160, action string, + resourceID []byte, outcome AuditOutcome, blockHeight uint32) uint64 { + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategoryAccess, + Severity: al.severityForOutcome(outcome, AuditCategoryAccess), + Outcome: outcome, + Actor: actor, + Target: target, + Action: action, + ResourceID: resourceID, + }) +} + +// LogDataChange logs a data modification event. +func (al *AuditLogger) LogDataChange(d *dao.Simple, actor util.Uint160, contractID int32, + action string, resourceID []byte, prevState, newState util.Uint256, blockHeight uint32) uint64 { + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategoryData, + Severity: AuditSeverityNotice, + Outcome: AuditOutcomeSuccess, + ContractID: contractID, + Actor: actor, + Action: action, + ResourceID: resourceID, + PreviousState: prevState, + NewState: newState, + }) +} + +// LogGovernance logs a governance action. +func (al *AuditLogger) LogGovernance(d *dao.Simple, actor util.Uint160, action string, + details string, outcome AuditOutcome, blockHeight uint32) uint64 { + severity := AuditSeverityNotice + if outcome == AuditOutcomeSuccess { + severity = AuditSeverityWarning // Governance changes warrant attention + } + + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategoryGovernance, + Severity: severity, + Outcome: outcome, + Actor: actor, + Action: action, + Details: details, + }) +} + +// LogFinancial logs a financial transaction. +func (al *AuditLogger) LogFinancial(d *dao.Simple, actor, target util.Uint160, + action string, resourceID []byte, outcome AuditOutcome, blockHeight uint32) uint64 { + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategoryFinancial, + Severity: AuditSeverityNotice, + Outcome: outcome, + Actor: actor, + Target: target, + Action: action, + ResourceID: resourceID, + }) +} + +// LogSecurity logs a security-related event. +func (al *AuditLogger) LogSecurity(d *dao.Simple, actor util.Uint160, action string, + severity AuditSeverity, outcome AuditOutcome, details string, blockHeight uint32) uint64 { + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategorySecurity, + Severity: severity, + Outcome: outcome, + Actor: actor, + Action: action, + Details: details, + }) +} + +// LogCompliance logs a compliance-related event. +func (al *AuditLogger) LogCompliance(d *dao.Simple, actor, target util.Uint160, + action string, details string, blockHeight uint32) uint64 { + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategoryCompliance, + Severity: AuditSeverityNotice, + Outcome: AuditOutcomeSuccess, + Actor: actor, + Target: target, + Action: action, + Details: details, + }) +} + +// LogRightsAccess logs access to rights-protected resources. +func (al *AuditLogger) LogRightsAccess(d *dao.Simple, actor, subject util.Uint160, + rightID uint8, action string, outcome AuditOutcome, blockHeight uint32) uint64 { + return al.Log(d, &AuditEntry{ + Timestamp: blockHeight, + Category: AuditCategoryCompliance, + Severity: AuditSeverityWarning, + Outcome: outcome, + Actor: actor, + Target: subject, + Action: action, + ResourceID: []byte{rightID}, + }) +} + +// GetEntry retrieves an audit entry by ID. +func (al *AuditLogger) GetEntry(d *dao.Simple, entryID uint64) *AuditEntry { + key := al.makeEntryKey(entryID) + si := d.GetStorageItem(al.contractID, key) + if si == nil { + return nil + } + return al.deserializeEntry(si) +} + +// Query searches audit logs based on query parameters. +func (al *AuditLogger) Query(d *dao.Simple, query *AuditQuery) []*AuditEntry { + var entries []*AuditEntry + var entryIDs []uint64 + + // Choose the most selective index + if query.Actor != nil { + entryIDs = al.queryByActor(d, *query.Actor, query.StartBlock, query.EndBlock, query.Limit+query.Offset) + } else if query.Target != nil { + entryIDs = al.queryByTarget(d, *query.Target, query.StartBlock, query.EndBlock, query.Limit+query.Offset) + } else if query.Category != nil { + entryIDs = al.queryByCategory(d, *query.Category, query.StartBlock, query.EndBlock, query.Limit+query.Offset) + } else if query.MinSeverity != nil { + entryIDs = al.queryBySeverity(d, *query.MinSeverity, query.StartBlock, query.EndBlock, query.Limit+query.Offset) + } else { + entryIDs = al.queryByBlock(d, query.StartBlock, query.EndBlock, query.Limit+query.Offset) + } + + // Apply offset and limit + start := query.Offset + if start > len(entryIDs) { + return entries + } + end := start + query.Limit + if end > len(entryIDs) { + end = len(entryIDs) + } + + // Fetch entries + for _, id := range entryIDs[start:end] { + if entry := al.GetEntry(d, id); entry != nil { + // Apply additional filters + if al.matchesQuery(entry, query) { + entries = append(entries, entry) + } + } + } + + return entries +} + +// GetEntriesForActor retrieves audit entries for a specific actor. +func (al *AuditLogger) GetEntriesForActor(d *dao.Simple, actor util.Uint160, limit int) []*AuditEntry { + return al.Query(d, &AuditQuery{ + Actor: &actor, + Limit: limit, + }) +} + +// GetEntriesForTarget retrieves audit entries for a specific target. +func (al *AuditLogger) GetEntriesForTarget(d *dao.Simple, target util.Uint160, limit int) []*AuditEntry { + return al.Query(d, &AuditQuery{ + Target: &target, + Limit: limit, + }) +} + +// GetSecurityAlerts retrieves high-severity security events. +func (al *AuditLogger) GetSecurityAlerts(d *dao.Simple, startBlock, endBlock uint32, limit int) []*AuditEntry { + severity := AuditSeverityAlert + category := AuditCategorySecurity + return al.Query(d, &AuditQuery{ + StartBlock: startBlock, + EndBlock: endBlock, + Category: &category, + MinSeverity: &severity, + Limit: limit, + }) +} + +// GetComplianceLog retrieves compliance-related entries for reporting. +func (al *AuditLogger) GetComplianceLog(d *dao.Simple, startBlock, endBlock uint32, limit int) []*AuditEntry { + category := AuditCategoryCompliance + return al.Query(d, &AuditQuery{ + StartBlock: startBlock, + EndBlock: endBlock, + Category: &category, + Limit: limit, + }) +} + +// severityForOutcome determines severity based on outcome and category. +func (al *AuditLogger) severityForOutcome(outcome AuditOutcome, category AuditCategory) AuditSeverity { + switch outcome { + case AuditOutcomeSuccess: + return AuditSeverityInfo + case AuditOutcomeFailure: + return AuditSeverityNotice + case AuditOutcomeDenied: + if category == AuditCategorySecurity || category == AuditCategoryAuth { + return AuditSeverityAlert + } + return AuditSeverityWarning + case AuditOutcomeError: + return AuditSeverityWarning + default: + return AuditSeverityInfo + } +} + +// matchesQuery checks if an entry matches additional query filters. +func (al *AuditLogger) matchesQuery(entry *AuditEntry, query *AuditQuery) bool { + if query.Category != nil && entry.Category != *query.Category { + return false + } + if query.MinSeverity != nil && entry.Severity < *query.MinSeverity { + return false + } + if query.ContractID != nil && entry.ContractID != *query.ContractID { + return false + } + if query.Actor != nil && entry.Actor != *query.Actor { + return false + } + if query.Target != nil && entry.Target != *query.Target { + return false + } + return true +} + +// Index query methods. +func (al *AuditLogger) queryByBlock(d *dao.Simple, startBlock, endBlock uint32, limit int) []uint64 { + var ids []uint64 + prefix := []byte{auditPrefixByBlock} + + d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 12 { + return true + } + block := binary.BigEndian.Uint32(k[0:4]) + if block < startBlock || (endBlock > 0 && block > endBlock) { + return true + } + entryID := binary.BigEndian.Uint64(k[4:12]) + ids = append(ids, entryID) + return len(ids) < limit + }) + + return ids +} + +func (al *AuditLogger) queryByActor(d *dao.Simple, actor util.Uint160, startBlock, endBlock uint32, limit int) []uint64 { + var ids []uint64 + prefix := make([]byte, 21) + prefix[0] = auditPrefixByActor + copy(prefix[1:], actor.BytesBE()) + + d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 12 { + return true + } + block := binary.BigEndian.Uint32(k[0:4]) + if block < startBlock || (endBlock > 0 && block > endBlock) { + return true + } + entryID := binary.BigEndian.Uint64(k[4:12]) + ids = append(ids, entryID) + return len(ids) < limit + }) + + return ids +} + +func (al *AuditLogger) queryByTarget(d *dao.Simple, target util.Uint160, startBlock, endBlock uint32, limit int) []uint64 { + var ids []uint64 + prefix := make([]byte, 21) + prefix[0] = auditPrefixByTarget + copy(prefix[1:], target.BytesBE()) + + d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 12 { + return true + } + block := binary.BigEndian.Uint32(k[0:4]) + if block < startBlock || (endBlock > 0 && block > endBlock) { + return true + } + entryID := binary.BigEndian.Uint64(k[4:12]) + ids = append(ids, entryID) + return len(ids) < limit + }) + + return ids +} + +func (al *AuditLogger) queryByCategory(d *dao.Simple, category AuditCategory, startBlock, endBlock uint32, limit int) []uint64 { + var ids []uint64 + prefix := []byte{auditPrefixByCategory, byte(category)} + + d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 12 { + return true + } + block := binary.BigEndian.Uint32(k[0:4]) + if block < startBlock || (endBlock > 0 && block > endBlock) { + return true + } + entryID := binary.BigEndian.Uint64(k[4:12]) + ids = append(ids, entryID) + return len(ids) < limit + }) + + return ids +} + +func (al *AuditLogger) queryBySeverity(d *dao.Simple, minSeverity AuditSeverity, startBlock, endBlock uint32, limit int) []uint64 { + var ids []uint64 + + for sev := minSeverity; sev <= AuditSeverityCritical; sev++ { + prefix := []byte{auditPrefixBySeverity, byte(sev)} + + d.Seek(al.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 12 { + return true + } + block := binary.BigEndian.Uint32(k[0:4]) + if block < startBlock || (endBlock > 0 && block > endBlock) { + return true + } + entryID := binary.BigEndian.Uint64(k[4:12]) + ids = append(ids, entryID) + return len(ids) < limit + }) + + if len(ids) >= limit { + break + } + } + + return ids +} + +// Storage key helpers. +func (al *AuditLogger) makeEntryKey(entryID uint64) []byte { + key := make([]byte, 9) + key[0] = auditPrefixEntry + binary.BigEndian.PutUint64(key[1:], entryID) + return key +} + +func (al *AuditLogger) getNextEntryID(d *dao.Simple) uint64 { + key := []byte{auditPrefixCounter} + si := d.GetStorageItem(al.contractID, key) + + var nextID uint64 = 1 + if si != nil && len(si) >= 8 { + nextID = binary.BigEndian.Uint64(si) + 1 + } + + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, nextID) + d.PutStorageItem(al.contractID, key, data) + + return nextID +} + +func (al *AuditLogger) putEntry(d *dao.Simple, entry *AuditEntry) { + key := al.makeEntryKey(entry.EntryID) + data := al.serializeEntry(entry) + d.PutStorageItem(al.contractID, key, data) +} + +func (al *AuditLogger) indexEntry(d *dao.Simple, entry *AuditEntry) { + // Index by block + blockKey := make([]byte, 13) + blockKey[0] = auditPrefixByBlock + binary.BigEndian.PutUint32(blockKey[1:5], entry.Timestamp) + binary.BigEndian.PutUint64(blockKey[5:13], entry.EntryID) + d.PutStorageItem(al.contractID, blockKey, []byte{1}) + + // Index by actor + if entry.Actor != (util.Uint160{}) { + actorKey := make([]byte, 33) + actorKey[0] = auditPrefixByActor + copy(actorKey[1:21], entry.Actor.BytesBE()) + binary.BigEndian.PutUint32(actorKey[21:25], entry.Timestamp) + binary.BigEndian.PutUint64(actorKey[25:33], entry.EntryID) + d.PutStorageItem(al.contractID, actorKey, []byte{1}) + } + + // Index by target + if entry.Target != (util.Uint160{}) { + targetKey := make([]byte, 33) + targetKey[0] = auditPrefixByTarget + copy(targetKey[1:21], entry.Target.BytesBE()) + binary.BigEndian.PutUint32(targetKey[21:25], entry.Timestamp) + binary.BigEndian.PutUint64(targetKey[25:33], entry.EntryID) + d.PutStorageItem(al.contractID, targetKey, []byte{1}) + } + + // Index by category + catKey := make([]byte, 14) + catKey[0] = auditPrefixByCategory + catKey[1] = byte(entry.Category) + binary.BigEndian.PutUint32(catKey[2:6], entry.Timestamp) + binary.BigEndian.PutUint64(catKey[6:14], entry.EntryID) + d.PutStorageItem(al.contractID, catKey, []byte{1}) + + // Index by severity + sevKey := make([]byte, 14) + sevKey[0] = auditPrefixBySeverity + sevKey[1] = byte(entry.Severity) + binary.BigEndian.PutUint32(sevKey[2:6], entry.Timestamp) + binary.BigEndian.PutUint64(sevKey[6:14], entry.EntryID) + d.PutStorageItem(al.contractID, sevKey, []byte{1}) + + // Index by contract + if entry.ContractID != 0 { + contractKey := make([]byte, 17) + contractKey[0] = auditPrefixByContract + binary.BigEndian.PutUint32(contractKey[1:5], uint32(entry.ContractID)) + binary.BigEndian.PutUint32(contractKey[5:9], entry.Timestamp) + binary.BigEndian.PutUint64(contractKey[9:17], entry.EntryID) + d.PutStorageItem(al.contractID, contractKey, []byte{1}) + } +} + +// Serialization helpers. +func (al *AuditLogger) serializeEntry(e *AuditEntry) []byte { + actionBytes := []byte(e.Action) + detailsBytes := []byte(e.Details) + + size := 8 + 4 + 1 + 1 + 1 + 4 + 20 + 20 + + 4 + len(actionBytes) + + 4 + len(e.ResourceID) + + 4 + len(detailsBytes) + + 32 + 32 + 32 + + data := make([]byte, size) + offset := 0 + + binary.BigEndian.PutUint64(data[offset:], e.EntryID) + offset += 8 + binary.BigEndian.PutUint32(data[offset:], e.Timestamp) + offset += 4 + data[offset] = byte(e.Category) + offset++ + data[offset] = byte(e.Severity) + offset++ + data[offset] = byte(e.Outcome) + offset++ + binary.BigEndian.PutUint32(data[offset:], uint32(e.ContractID)) + offset += 4 + copy(data[offset:], e.Actor.BytesBE()) + offset += 20 + copy(data[offset:], e.Target.BytesBE()) + offset += 20 + + binary.BigEndian.PutUint32(data[offset:], uint32(len(actionBytes))) + offset += 4 + copy(data[offset:], actionBytes) + offset += len(actionBytes) + + binary.BigEndian.PutUint32(data[offset:], uint32(len(e.ResourceID))) + offset += 4 + copy(data[offset:], e.ResourceID) + offset += len(e.ResourceID) + + binary.BigEndian.PutUint32(data[offset:], uint32(len(detailsBytes))) + offset += 4 + copy(data[offset:], detailsBytes) + offset += len(detailsBytes) + + copy(data[offset:], e.IPHash[:]) + offset += 32 + copy(data[offset:], e.PreviousState[:]) + offset += 32 + copy(data[offset:], e.NewState[:]) + + return data +} + +func (al *AuditLogger) deserializeEntry(data []byte) *AuditEntry { + if len(data) < 60 { + return nil + } + + e := &AuditEntry{} + offset := 0 + + e.EntryID = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + e.Timestamp = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + e.Category = AuditCategory(data[offset]) + offset++ + e.Severity = AuditSeverity(data[offset]) + offset++ + e.Outcome = AuditOutcome(data[offset]) + offset++ + e.ContractID = int32(binary.BigEndian.Uint32(data[offset:])) + offset += 4 + e.Actor, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) + offset += 20 + e.Target, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) + offset += 20 + + if offset+4 > len(data) { + return nil + } + actionLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(actionLen) > len(data) { + return nil + } + e.Action = string(data[offset : offset+int(actionLen)]) + offset += int(actionLen) + + if offset+4 > len(data) { + return nil + } + resourceLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(resourceLen) > len(data) { + return nil + } + e.ResourceID = make([]byte, resourceLen) + copy(e.ResourceID, data[offset:offset+int(resourceLen)]) + offset += int(resourceLen) + + if offset+4 > len(data) { + return nil + } + detailsLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(detailsLen) > len(data) { + return nil + } + e.Details = string(data[offset : offset+int(detailsLen)]) + offset += int(detailsLen) + + if offset+96 > len(data) { + return e // Return partial if hashes missing + } + copy(e.IPHash[:], data[offset:offset+32]) + offset += 32 + copy(e.PreviousState[:], data[offset:offset+32]) + offset += 32 + copy(e.NewState[:], data[offset:offset+32]) + + return e +} + +// StandardAuditActions defines common audit action names. +var StandardAuditActions = struct { + // Auth actions + AuthLogin string + AuthLogout string + AuthFailedLogin string + AuthRoleAssigned string + AuthRoleRevoked string + + // Access actions + AccessRead string + AccessWrite string + AccessDelete string + AccessDenied string + + // Financial actions + FinTransfer string + FinMint string + FinBurn string + FinInvest string + FinWithdraw string + + // Governance actions + GovProposalCreate string + GovVote string + GovProposalPass string + GovProposalReject string + + // Security actions + SecCircuitTrip string + SecRollback string + SecInvariantFail string + SecRightsRestrict string +}{ + AuthLogin: "auth.login", + AuthLogout: "auth.logout", + AuthFailedLogin: "auth.failed_login", + AuthRoleAssigned: "auth.role_assigned", + AuthRoleRevoked: "auth.role_revoked", + AccessRead: "access.read", + AccessWrite: "access.write", + AccessDelete: "access.delete", + AccessDenied: "access.denied", + FinTransfer: "financial.transfer", + FinMint: "financial.mint", + FinBurn: "financial.burn", + FinInvest: "financial.invest", + FinWithdraw: "financial.withdraw", + GovProposalCreate: "governance.proposal_create", + GovVote: "governance.vote", + GovProposalPass: "governance.proposal_pass", + GovProposalReject: "governance.proposal_reject", + SecCircuitTrip: "security.circuit_trip", + SecRollback: "security.rollback", + SecInvariantFail: "security.invariant_fail", + SecRightsRestrict: "security.rights_restrict", +} diff --git a/pkg/core/native/canary_deployment.go b/pkg/core/native/canary_deployment.go index 49021af..c450c6c 100644 --- a/pkg/core/native/canary_deployment.go +++ b/pkg/core/native/canary_deployment.go @@ -1,652 +1,652 @@ -package native - -import ( - "encoding/binary" - "errors" - - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" -) - -// ARCH-004: Canary Deployment System -// Provides infrastructure for safe, gradual rollouts of new features -// with automatic rollback capabilities based on error rates and invariant violations. - -// FeatureStatus represents the deployment status of a feature. -type FeatureStatus uint8 - -const ( - // FeatureDisabled means feature is not active - FeatureDisabled FeatureStatus = iota - // FeatureCanary means feature is active for a percentage of users - FeatureCanary - // FeatureRollingOut means feature is gradually expanding - FeatureRollingOut - // FeatureEnabled means feature is fully active - FeatureEnabled - // FeatureRolledBack means feature was active but reverted - FeatureRolledBack -) - -// RollbackReason identifies why a feature was rolled back. -type RollbackReason uint8 - -const ( - RollbackReasonManual RollbackReason = iota - RollbackReasonErrorRate - RollbackReasonInvariantViolation - RollbackReasonCircuitBreaker - RollbackReasonConsensusFailure - RollbackReasonPerformance -) - -// FeatureFlag represents a canary feature flag. -type FeatureFlag struct { - // Name is the unique identifier for this feature - Name string - // Description explains what this feature does - Description string - // Status is the current deployment status - Status FeatureStatus - // RolloutPercent is percentage of users who see this feature (0-100) - RolloutPercent uint8 - // TargetPercent is the goal percentage for gradual rollout - TargetPercent uint8 - // IncrementPercent is how much to increase per rollout step - IncrementPercent uint8 - // IncrementBlocks is blocks between rollout increments - IncrementBlocks uint32 - // StartBlock is when canary started - StartBlock uint32 - // LastIncrementBlock is when last rollout increment happened - LastIncrementBlock uint32 - // ErrorCount is errors encountered during canary - ErrorCount uint64 - // SuccessCount is successful operations during canary - SuccessCount uint64 - // MaxErrorRate is error rate that triggers rollback (per 10000) - MaxErrorRate uint32 - // MinSuccessCount is minimum successes before considering rollout - MinSuccessCount uint64 - // EnabledBy is who enabled the feature - EnabledBy util.Uint160 - // RollbackReason if rolled back - RollbackReason RollbackReason -} - -// FeatureMetrics tracks canary performance metrics. -type FeatureMetrics struct { - FeatureName string - TotalOperations uint64 - SuccessfulOps uint64 - FailedOps uint64 - AverageLatency uint64 // In block units - PeakLatency uint64 - InvariantChecks uint64 - InvariantFailures uint64 -} - -// Storage prefixes for canary deployment. -const ( - canaryPrefixFeature byte = 0xD0 // name -> FeatureFlag - canaryPrefixMetrics byte = 0xD1 // name -> FeatureMetrics - canaryPrefixHistory byte = 0xD2 // name + block -> status change - canaryPrefixUserEnabled byte = 0xD3 // name + address -> enabled flag - canaryPrefixGlobal byte = 0xD4 // -> GlobalCanaryState -) - -// Canary deployment errors. -var ( - ErrFeatureNotFound = errors.New("feature flag not found") - ErrFeatureAlreadyExists = errors.New("feature flag already exists") - ErrFeatureDisabled = errors.New("feature is disabled") - ErrRolloutInProgress = errors.New("rollout already in progress") - ErrInsufficientData = errors.New("insufficient data for rollout decision") - ErrErrorRateExceeded = errors.New("error rate exceeded threshold") -) - -// Default canary settings. -const ( - DefaultMaxErrorRate = 100 // 1% (per 10000) - DefaultMinSuccessCount = 100 - DefaultIncrementPercent = 10 - DefaultIncrementBlocks = 8640 // ~1 day at 10s blocks - DefaultCanaryPercent = 5 -) - -// CanaryDeployment manages feature flags and canary deployments. -type CanaryDeployment struct { - contractID int32 -} - -// NewCanaryDeployment creates a new canary deployment manager. -func NewCanaryDeployment(contractID int32) *CanaryDeployment { - return &CanaryDeployment{contractID: contractID} -} - -// CreateFeature creates a new feature flag. -func (cd *CanaryDeployment) CreateFeature(d *dao.Simple, name, description string, enabledBy util.Uint160) error { - if cd.GetFeature(d, name) != nil { - return ErrFeatureAlreadyExists - } - - feature := &FeatureFlag{ - Name: name, - Description: description, - Status: FeatureDisabled, - RolloutPercent: 0, - TargetPercent: 100, - IncrementPercent: DefaultIncrementPercent, - IncrementBlocks: DefaultIncrementBlocks, - MaxErrorRate: DefaultMaxErrorRate, - MinSuccessCount: DefaultMinSuccessCount, - EnabledBy: enabledBy, - } - - cd.putFeature(d, feature) - cd.initMetrics(d, name) - return nil -} - -// GetFeature retrieves a feature flag. -func (cd *CanaryDeployment) GetFeature(d *dao.Simple, name string) *FeatureFlag { - key := cd.makeFeatureKey(name) - si := d.GetStorageItem(cd.contractID, key) - if si == nil { - return nil - } - return cd.deserializeFeature(si) -} - -// StartCanary starts a canary deployment for a feature. -func (cd *CanaryDeployment) StartCanary(d *dao.Simple, name string, percent uint8, currentBlock uint32) error { - feature := cd.GetFeature(d, name) - if feature == nil { - return ErrFeatureNotFound - } - - if percent > 100 { - percent = 100 - } - - feature.Status = FeatureCanary - feature.RolloutPercent = percent - feature.StartBlock = currentBlock - feature.LastIncrementBlock = currentBlock - feature.ErrorCount = 0 - feature.SuccessCount = 0 - - cd.putFeature(d, feature) - cd.recordHistory(d, name, currentBlock, FeatureCanary) - return nil -} - -// StartRollout begins gradual rollout to target percentage. -func (cd *CanaryDeployment) StartRollout(d *dao.Simple, name string, targetPercent uint8, currentBlock uint32) error { - feature := cd.GetFeature(d, name) - if feature == nil { - return ErrFeatureNotFound - } - - if feature.Status == FeatureRollingOut { - return ErrRolloutInProgress - } - - // Require minimum success count before rollout - if feature.SuccessCount < feature.MinSuccessCount { - return ErrInsufficientData - } - - feature.Status = FeatureRollingOut - feature.TargetPercent = targetPercent - feature.LastIncrementBlock = currentBlock - - cd.putFeature(d, feature) - cd.recordHistory(d, name, currentBlock, FeatureRollingOut) - return nil -} - -// ProcessRolloutIncrement checks if rollout should increment. -func (cd *CanaryDeployment) ProcessRolloutIncrement(d *dao.Simple, name string, currentBlock uint32) { - feature := cd.GetFeature(d, name) - if feature == nil || feature.Status != FeatureRollingOut { - return - } - - // Check if enough blocks have passed - if currentBlock < feature.LastIncrementBlock+feature.IncrementBlocks { - return - } - - // Check error rate - if cd.shouldRollback(feature) { - cd.Rollback(d, name, RollbackReasonErrorRate, currentBlock) - return - } - - // Increment rollout - newPercent := feature.RolloutPercent + feature.IncrementPercent - if newPercent >= feature.TargetPercent { - newPercent = feature.TargetPercent - feature.Status = FeatureEnabled - } - - feature.RolloutPercent = newPercent - feature.LastIncrementBlock = currentBlock - cd.putFeature(d, feature) - - if feature.Status == FeatureEnabled { - cd.recordHistory(d, name, currentBlock, FeatureEnabled) - } -} - -// IsEnabled checks if a feature is enabled for a specific user. -func (cd *CanaryDeployment) IsEnabled(d *dao.Simple, name string, user util.Uint160) bool { - feature := cd.GetFeature(d, name) - if feature == nil { - return false - } - - switch feature.Status { - case FeatureEnabled: - return true - case FeatureDisabled, FeatureRolledBack: - return false - case FeatureCanary, FeatureRollingOut: - return cd.isInRolloutGroup(user, feature.RolloutPercent) - } - - return false -} - -// isInRolloutGroup determines if a user is in the rollout group. -func (cd *CanaryDeployment) isInRolloutGroup(user util.Uint160, percent uint8) bool { - if percent >= 100 { - return true - } - if percent == 0 { - return false - } - - // Use first byte of address as deterministic bucket - bucket := user[0] % 100 - return bucket < percent -} - -// RecordSuccess records a successful feature operation. -func (cd *CanaryDeployment) RecordSuccess(d *dao.Simple, name string) { - feature := cd.GetFeature(d, name) - if feature == nil { - return - } - - feature.SuccessCount++ - cd.putFeature(d, feature) - - metrics := cd.GetMetrics(d, name) - if metrics != nil { - metrics.TotalOperations++ - metrics.SuccessfulOps++ - cd.putMetrics(d, metrics) - } -} - -// RecordError records a feature operation error. -func (cd *CanaryDeployment) RecordError(d *dao.Simple, name string, currentBlock uint32) { - feature := cd.GetFeature(d, name) - if feature == nil { - return - } - - feature.ErrorCount++ - cd.putFeature(d, feature) - - metrics := cd.GetMetrics(d, name) - if metrics != nil { - metrics.TotalOperations++ - metrics.FailedOps++ - cd.putMetrics(d, metrics) - } - - // Check if should auto-rollback - if cd.shouldRollback(feature) { - cd.Rollback(d, name, RollbackReasonErrorRate, currentBlock) - } -} - -// shouldRollback checks if error rate exceeds threshold. -func (cd *CanaryDeployment) shouldRollback(feature *FeatureFlag) bool { - if feature.SuccessCount+feature.ErrorCount < 10 { - return false // Not enough data - } - - errorRate := (feature.ErrorCount * 10000) / (feature.SuccessCount + feature.ErrorCount) - return uint32(errorRate) > feature.MaxErrorRate -} - -// Rollback rolls back a feature to disabled state. -func (cd *CanaryDeployment) Rollback(d *dao.Simple, name string, reason RollbackReason, currentBlock uint32) error { - feature := cd.GetFeature(d, name) - if feature == nil { - return ErrFeatureNotFound - } - - feature.Status = FeatureRolledBack - feature.RolloutPercent = 0 - feature.RollbackReason = reason - - cd.putFeature(d, feature) - cd.recordHistory(d, name, currentBlock, FeatureRolledBack) - return nil -} - -// Enable fully enables a feature (100% rollout). -func (cd *CanaryDeployment) Enable(d *dao.Simple, name string, currentBlock uint32) error { - feature := cd.GetFeature(d, name) - if feature == nil { - return ErrFeatureNotFound - } - - feature.Status = FeatureEnabled - feature.RolloutPercent = 100 - - cd.putFeature(d, feature) - cd.recordHistory(d, name, currentBlock, FeatureEnabled) - return nil -} - -// Disable disables a feature. -func (cd *CanaryDeployment) Disable(d *dao.Simple, name string, currentBlock uint32) error { - feature := cd.GetFeature(d, name) - if feature == nil { - return ErrFeatureNotFound - } - - feature.Status = FeatureDisabled - feature.RolloutPercent = 0 - - cd.putFeature(d, feature) - cd.recordHistory(d, name, currentBlock, FeatureDisabled) - return nil -} - -// GetMetrics retrieves feature metrics. -func (cd *CanaryDeployment) GetMetrics(d *dao.Simple, name string) *FeatureMetrics { - key := cd.makeMetricsKey(name) - si := d.GetStorageItem(cd.contractID, key) - if si == nil { - return nil - } - return cd.deserializeMetrics(si) -} - -// GetAllFeatures retrieves all feature flags. -func (cd *CanaryDeployment) GetAllFeatures(d *dao.Simple) []*FeatureFlag { - var features []*FeatureFlag - prefix := []byte{canaryPrefixFeature} - - d.Seek(cd.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if feature := cd.deserializeFeature(v); feature != nil { - features = append(features, feature) - } - return true - }) - - return features -} - -// GetActiveCanaries retrieves features currently in canary/rollout. -func (cd *CanaryDeployment) GetActiveCanaries(d *dao.Simple) []*FeatureFlag { - var features []*FeatureFlag - prefix := []byte{canaryPrefixFeature} - - d.Seek(cd.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if feature := cd.deserializeFeature(v); feature != nil { - if feature.Status == FeatureCanary || feature.Status == FeatureRollingOut { - features = append(features, feature) - } - } - return true - }) - - return features -} - -// Helper methods. -func (cd *CanaryDeployment) makeFeatureKey(name string) []byte { - key := make([]byte, 1+len(name)) - key[0] = canaryPrefixFeature - copy(key[1:], name) - return key -} - -func (cd *CanaryDeployment) makeMetricsKey(name string) []byte { - key := make([]byte, 1+len(name)) - key[0] = canaryPrefixMetrics - copy(key[1:], name) - return key -} - -func (cd *CanaryDeployment) putFeature(d *dao.Simple, feature *FeatureFlag) { - key := cd.makeFeatureKey(feature.Name) - data := cd.serializeFeature(feature) - d.PutStorageItem(cd.contractID, key, data) -} - -func (cd *CanaryDeployment) initMetrics(d *dao.Simple, name string) { - metrics := &FeatureMetrics{FeatureName: name} - cd.putMetrics(d, metrics) -} - -func (cd *CanaryDeployment) putMetrics(d *dao.Simple, metrics *FeatureMetrics) { - key := cd.makeMetricsKey(metrics.FeatureName) - data := cd.serializeMetrics(metrics) - d.PutStorageItem(cd.contractID, key, data) -} - -func (cd *CanaryDeployment) recordHistory(d *dao.Simple, name string, blockHeight uint32, status FeatureStatus) { - key := make([]byte, 1+len(name)+4) - key[0] = canaryPrefixHistory - copy(key[1:], name) - binary.BigEndian.PutUint32(key[1+len(name):], blockHeight) - - data := []byte{byte(status)} - d.PutStorageItem(cd.contractID, key, data) -} - -// Serialization helpers. -func (cd *CanaryDeployment) serializeFeature(f *FeatureFlag) []byte { - nameBytes := []byte(f.Name) - descBytes := []byte(f.Description) - - size := 4 + len(nameBytes) + 4 + len(descBytes) + 1 + 4 + 4 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 20 + 1 - data := make([]byte, size) - offset := 0 - - binary.BigEndian.PutUint32(data[offset:], uint32(len(nameBytes))) - offset += 4 - copy(data[offset:], nameBytes) - offset += len(nameBytes) - - binary.BigEndian.PutUint32(data[offset:], uint32(len(descBytes))) - offset += 4 - copy(data[offset:], descBytes) - offset += len(descBytes) - - data[offset] = byte(f.Status) - offset++ - - data[offset] = f.RolloutPercent - offset++ - data[offset] = f.TargetPercent - offset++ - data[offset] = f.IncrementPercent - offset++ - - binary.BigEndian.PutUint32(data[offset:], f.IncrementBlocks) - offset += 4 - binary.BigEndian.PutUint32(data[offset:], f.StartBlock) - offset += 4 - binary.BigEndian.PutUint32(data[offset:], f.LastIncrementBlock) - offset += 4 - - binary.BigEndian.PutUint64(data[offset:], f.ErrorCount) - offset += 8 - binary.BigEndian.PutUint64(data[offset:], f.SuccessCount) - offset += 8 - - binary.BigEndian.PutUint32(data[offset:], f.MaxErrorRate) - offset += 4 - binary.BigEndian.PutUint64(data[offset:], f.MinSuccessCount) - offset += 8 - - copy(data[offset:], f.EnabledBy.BytesBE()) - offset += 20 - - data[offset] = byte(f.RollbackReason) - - return data -} - -func (cd *CanaryDeployment) deserializeFeature(data []byte) *FeatureFlag { - if len(data) < 8 { - return nil - } - - f := &FeatureFlag{} - offset := 0 - - nameLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(nameLen) > len(data) { - return nil - } - f.Name = string(data[offset : offset+int(nameLen)]) - offset += int(nameLen) - - if offset+4 > len(data) { - return nil - } - descLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(descLen) > len(data) { - return nil - } - f.Description = string(data[offset : offset+int(descLen)]) - offset += int(descLen) - - if offset+61 > len(data) { - return nil - } - - f.Status = FeatureStatus(data[offset]) - offset++ - f.RolloutPercent = data[offset] - offset++ - f.TargetPercent = data[offset] - offset++ - f.IncrementPercent = data[offset] - offset++ - - f.IncrementBlocks = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - f.StartBlock = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - f.LastIncrementBlock = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - - f.ErrorCount = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - f.SuccessCount = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - - f.MaxErrorRate = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - f.MinSuccessCount = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - - f.EnabledBy, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) - offset += 20 - - f.RollbackReason = RollbackReason(data[offset]) - - return f -} - -func (cd *CanaryDeployment) serializeMetrics(m *FeatureMetrics) []byte { - nameBytes := []byte(m.FeatureName) - data := make([]byte, 4+len(nameBytes)+48) - offset := 0 - - binary.BigEndian.PutUint32(data[offset:], uint32(len(nameBytes))) - offset += 4 - copy(data[offset:], nameBytes) - offset += len(nameBytes) - - binary.BigEndian.PutUint64(data[offset:], m.TotalOperations) - offset += 8 - binary.BigEndian.PutUint64(data[offset:], m.SuccessfulOps) - offset += 8 - binary.BigEndian.PutUint64(data[offset:], m.FailedOps) - offset += 8 - binary.BigEndian.PutUint64(data[offset:], m.AverageLatency) - offset += 8 - binary.BigEndian.PutUint64(data[offset:], m.PeakLatency) - offset += 8 - binary.BigEndian.PutUint64(data[offset:], m.InvariantChecks) - - return data -} - -func (cd *CanaryDeployment) deserializeMetrics(data []byte) *FeatureMetrics { - if len(data) < 8 { - return nil - } - - m := &FeatureMetrics{} - offset := 0 - - nameLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(nameLen) > len(data) { - return nil - } - m.FeatureName = string(data[offset : offset+int(nameLen)]) - offset += int(nameLen) - - if offset+48 > len(data) { - return nil - } - - m.TotalOperations = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - m.SuccessfulOps = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - m.FailedOps = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - m.AverageLatency = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - m.PeakLatency = binary.BigEndian.Uint64(data[offset:]) - offset += 8 - m.InvariantChecks = binary.BigEndian.Uint64(data[offset:]) - - return m -} - -// StandardFeatureFlags defines common feature flag names. -var StandardFeatureFlags = struct { - VitaRecoveryV2 string - TributeGamingDetect string - CrossChainProofs string - CommitRevealInvest string - EnhancedAuditLogging string - CircuitBreakerAuto string -}{ - VitaRecoveryV2: "vita_recovery_v2", - TributeGamingDetect: "tribute_gaming_detect", - CrossChainProofs: "cross_chain_proofs", - CommitRevealInvest: "commit_reveal_invest", - EnhancedAuditLogging: "enhanced_audit_logging", - CircuitBreakerAuto: "circuit_breaker_auto", -} +package native + +import ( + "encoding/binary" + "errors" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" +) + +// ARCH-004: Canary Deployment System +// Provides infrastructure for safe, gradual rollouts of new features +// with automatic rollback capabilities based on error rates and invariant violations. + +// FeatureStatus represents the deployment status of a feature. +type FeatureStatus uint8 + +const ( + // FeatureDisabled means feature is not active + FeatureDisabled FeatureStatus = iota + // FeatureCanary means feature is active for a percentage of users + FeatureCanary + // FeatureRollingOut means feature is gradually expanding + FeatureRollingOut + // FeatureEnabled means feature is fully active + FeatureEnabled + // FeatureRolledBack means feature was active but reverted + FeatureRolledBack +) + +// RollbackReason identifies why a feature was rolled back. +type RollbackReason uint8 + +const ( + RollbackReasonManual RollbackReason = iota + RollbackReasonErrorRate + RollbackReasonInvariantViolation + RollbackReasonCircuitBreaker + RollbackReasonConsensusFailure + RollbackReasonPerformance +) + +// FeatureFlag represents a canary feature flag. +type FeatureFlag struct { + // Name is the unique identifier for this feature + Name string + // Description explains what this feature does + Description string + // Status is the current deployment status + Status FeatureStatus + // RolloutPercent is percentage of users who see this feature (0-100) + RolloutPercent uint8 + // TargetPercent is the goal percentage for gradual rollout + TargetPercent uint8 + // IncrementPercent is how much to increase per rollout step + IncrementPercent uint8 + // IncrementBlocks is blocks between rollout increments + IncrementBlocks uint32 + // StartBlock is when canary started + StartBlock uint32 + // LastIncrementBlock is when last rollout increment happened + LastIncrementBlock uint32 + // ErrorCount is errors encountered during canary + ErrorCount uint64 + // SuccessCount is successful operations during canary + SuccessCount uint64 + // MaxErrorRate is error rate that triggers rollback (per 10000) + MaxErrorRate uint32 + // MinSuccessCount is minimum successes before considering rollout + MinSuccessCount uint64 + // EnabledBy is who enabled the feature + EnabledBy util.Uint160 + // RollbackReason if rolled back + RollbackReason RollbackReason +} + +// FeatureMetrics tracks canary performance metrics. +type FeatureMetrics struct { + FeatureName string + TotalOperations uint64 + SuccessfulOps uint64 + FailedOps uint64 + AverageLatency uint64 // In block units + PeakLatency uint64 + InvariantChecks uint64 + InvariantFailures uint64 +} + +// Storage prefixes for canary deployment. +const ( + canaryPrefixFeature byte = 0xD0 // name -> FeatureFlag + canaryPrefixMetrics byte = 0xD1 // name -> FeatureMetrics + canaryPrefixHistory byte = 0xD2 // name + block -> status change + canaryPrefixUserEnabled byte = 0xD3 // name + address -> enabled flag + canaryPrefixGlobal byte = 0xD4 // -> GlobalCanaryState +) + +// Canary deployment errors. +var ( + ErrFeatureNotFound = errors.New("feature flag not found") + ErrFeatureAlreadyExists = errors.New("feature flag already exists") + ErrFeatureDisabled = errors.New("feature is disabled") + ErrRolloutInProgress = errors.New("rollout already in progress") + ErrInsufficientData = errors.New("insufficient data for rollout decision") + ErrErrorRateExceeded = errors.New("error rate exceeded threshold") +) + +// Default canary settings. +const ( + DefaultMaxErrorRate = 100 // 1% (per 10000) + DefaultMinSuccessCount = 100 + DefaultIncrementPercent = 10 + DefaultIncrementBlocks = 8640 // ~1 day at 10s blocks + DefaultCanaryPercent = 5 +) + +// CanaryDeployment manages feature flags and canary deployments. +type CanaryDeployment struct { + contractID int32 +} + +// NewCanaryDeployment creates a new canary deployment manager. +func NewCanaryDeployment(contractID int32) *CanaryDeployment { + return &CanaryDeployment{contractID: contractID} +} + +// CreateFeature creates a new feature flag. +func (cd *CanaryDeployment) CreateFeature(d *dao.Simple, name, description string, enabledBy util.Uint160) error { + if cd.GetFeature(d, name) != nil { + return ErrFeatureAlreadyExists + } + + feature := &FeatureFlag{ + Name: name, + Description: description, + Status: FeatureDisabled, + RolloutPercent: 0, + TargetPercent: 100, + IncrementPercent: DefaultIncrementPercent, + IncrementBlocks: DefaultIncrementBlocks, + MaxErrorRate: DefaultMaxErrorRate, + MinSuccessCount: DefaultMinSuccessCount, + EnabledBy: enabledBy, + } + + cd.putFeature(d, feature) + cd.initMetrics(d, name) + return nil +} + +// GetFeature retrieves a feature flag. +func (cd *CanaryDeployment) GetFeature(d *dao.Simple, name string) *FeatureFlag { + key := cd.makeFeatureKey(name) + si := d.GetStorageItem(cd.contractID, key) + if si == nil { + return nil + } + return cd.deserializeFeature(si) +} + +// StartCanary starts a canary deployment for a feature. +func (cd *CanaryDeployment) StartCanary(d *dao.Simple, name string, percent uint8, currentBlock uint32) error { + feature := cd.GetFeature(d, name) + if feature == nil { + return ErrFeatureNotFound + } + + if percent > 100 { + percent = 100 + } + + feature.Status = FeatureCanary + feature.RolloutPercent = percent + feature.StartBlock = currentBlock + feature.LastIncrementBlock = currentBlock + feature.ErrorCount = 0 + feature.SuccessCount = 0 + + cd.putFeature(d, feature) + cd.recordHistory(d, name, currentBlock, FeatureCanary) + return nil +} + +// StartRollout begins gradual rollout to target percentage. +func (cd *CanaryDeployment) StartRollout(d *dao.Simple, name string, targetPercent uint8, currentBlock uint32) error { + feature := cd.GetFeature(d, name) + if feature == nil { + return ErrFeatureNotFound + } + + if feature.Status == FeatureRollingOut { + return ErrRolloutInProgress + } + + // Require minimum success count before rollout + if feature.SuccessCount < feature.MinSuccessCount { + return ErrInsufficientData + } + + feature.Status = FeatureRollingOut + feature.TargetPercent = targetPercent + feature.LastIncrementBlock = currentBlock + + cd.putFeature(d, feature) + cd.recordHistory(d, name, currentBlock, FeatureRollingOut) + return nil +} + +// ProcessRolloutIncrement checks if rollout should increment. +func (cd *CanaryDeployment) ProcessRolloutIncrement(d *dao.Simple, name string, currentBlock uint32) { + feature := cd.GetFeature(d, name) + if feature == nil || feature.Status != FeatureRollingOut { + return + } + + // Check if enough blocks have passed + if currentBlock < feature.LastIncrementBlock+feature.IncrementBlocks { + return + } + + // Check error rate + if cd.shouldRollback(feature) { + cd.Rollback(d, name, RollbackReasonErrorRate, currentBlock) + return + } + + // Increment rollout + newPercent := feature.RolloutPercent + feature.IncrementPercent + if newPercent >= feature.TargetPercent { + newPercent = feature.TargetPercent + feature.Status = FeatureEnabled + } + + feature.RolloutPercent = newPercent + feature.LastIncrementBlock = currentBlock + cd.putFeature(d, feature) + + if feature.Status == FeatureEnabled { + cd.recordHistory(d, name, currentBlock, FeatureEnabled) + } +} + +// IsEnabled checks if a feature is enabled for a specific user. +func (cd *CanaryDeployment) IsEnabled(d *dao.Simple, name string, user util.Uint160) bool { + feature := cd.GetFeature(d, name) + if feature == nil { + return false + } + + switch feature.Status { + case FeatureEnabled: + return true + case FeatureDisabled, FeatureRolledBack: + return false + case FeatureCanary, FeatureRollingOut: + return cd.isInRolloutGroup(user, feature.RolloutPercent) + } + + return false +} + +// isInRolloutGroup determines if a user is in the rollout group. +func (cd *CanaryDeployment) isInRolloutGroup(user util.Uint160, percent uint8) bool { + if percent >= 100 { + return true + } + if percent == 0 { + return false + } + + // Use first byte of address as deterministic bucket + bucket := user[0] % 100 + return bucket < percent +} + +// RecordSuccess records a successful feature operation. +func (cd *CanaryDeployment) RecordSuccess(d *dao.Simple, name string) { + feature := cd.GetFeature(d, name) + if feature == nil { + return + } + + feature.SuccessCount++ + cd.putFeature(d, feature) + + metrics := cd.GetMetrics(d, name) + if metrics != nil { + metrics.TotalOperations++ + metrics.SuccessfulOps++ + cd.putMetrics(d, metrics) + } +} + +// RecordError records a feature operation error. +func (cd *CanaryDeployment) RecordError(d *dao.Simple, name string, currentBlock uint32) { + feature := cd.GetFeature(d, name) + if feature == nil { + return + } + + feature.ErrorCount++ + cd.putFeature(d, feature) + + metrics := cd.GetMetrics(d, name) + if metrics != nil { + metrics.TotalOperations++ + metrics.FailedOps++ + cd.putMetrics(d, metrics) + } + + // Check if should auto-rollback + if cd.shouldRollback(feature) { + cd.Rollback(d, name, RollbackReasonErrorRate, currentBlock) + } +} + +// shouldRollback checks if error rate exceeds threshold. +func (cd *CanaryDeployment) shouldRollback(feature *FeatureFlag) bool { + if feature.SuccessCount+feature.ErrorCount < 10 { + return false // Not enough data + } + + errorRate := (feature.ErrorCount * 10000) / (feature.SuccessCount + feature.ErrorCount) + return uint32(errorRate) > feature.MaxErrorRate +} + +// Rollback rolls back a feature to disabled state. +func (cd *CanaryDeployment) Rollback(d *dao.Simple, name string, reason RollbackReason, currentBlock uint32) error { + feature := cd.GetFeature(d, name) + if feature == nil { + return ErrFeatureNotFound + } + + feature.Status = FeatureRolledBack + feature.RolloutPercent = 0 + feature.RollbackReason = reason + + cd.putFeature(d, feature) + cd.recordHistory(d, name, currentBlock, FeatureRolledBack) + return nil +} + +// Enable fully enables a feature (100% rollout). +func (cd *CanaryDeployment) Enable(d *dao.Simple, name string, currentBlock uint32) error { + feature := cd.GetFeature(d, name) + if feature == nil { + return ErrFeatureNotFound + } + + feature.Status = FeatureEnabled + feature.RolloutPercent = 100 + + cd.putFeature(d, feature) + cd.recordHistory(d, name, currentBlock, FeatureEnabled) + return nil +} + +// Disable disables a feature. +func (cd *CanaryDeployment) Disable(d *dao.Simple, name string, currentBlock uint32) error { + feature := cd.GetFeature(d, name) + if feature == nil { + return ErrFeatureNotFound + } + + feature.Status = FeatureDisabled + feature.RolloutPercent = 0 + + cd.putFeature(d, feature) + cd.recordHistory(d, name, currentBlock, FeatureDisabled) + return nil +} + +// GetMetrics retrieves feature metrics. +func (cd *CanaryDeployment) GetMetrics(d *dao.Simple, name string) *FeatureMetrics { + key := cd.makeMetricsKey(name) + si := d.GetStorageItem(cd.contractID, key) + if si == nil { + return nil + } + return cd.deserializeMetrics(si) +} + +// GetAllFeatures retrieves all feature flags. +func (cd *CanaryDeployment) GetAllFeatures(d *dao.Simple) []*FeatureFlag { + var features []*FeatureFlag + prefix := []byte{canaryPrefixFeature} + + d.Seek(cd.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if feature := cd.deserializeFeature(v); feature != nil { + features = append(features, feature) + } + return true + }) + + return features +} + +// GetActiveCanaries retrieves features currently in canary/rollout. +func (cd *CanaryDeployment) GetActiveCanaries(d *dao.Simple) []*FeatureFlag { + var features []*FeatureFlag + prefix := []byte{canaryPrefixFeature} + + d.Seek(cd.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if feature := cd.deserializeFeature(v); feature != nil { + if feature.Status == FeatureCanary || feature.Status == FeatureRollingOut { + features = append(features, feature) + } + } + return true + }) + + return features +} + +// Helper methods. +func (cd *CanaryDeployment) makeFeatureKey(name string) []byte { + key := make([]byte, 1+len(name)) + key[0] = canaryPrefixFeature + copy(key[1:], name) + return key +} + +func (cd *CanaryDeployment) makeMetricsKey(name string) []byte { + key := make([]byte, 1+len(name)) + key[0] = canaryPrefixMetrics + copy(key[1:], name) + return key +} + +func (cd *CanaryDeployment) putFeature(d *dao.Simple, feature *FeatureFlag) { + key := cd.makeFeatureKey(feature.Name) + data := cd.serializeFeature(feature) + d.PutStorageItem(cd.contractID, key, data) +} + +func (cd *CanaryDeployment) initMetrics(d *dao.Simple, name string) { + metrics := &FeatureMetrics{FeatureName: name} + cd.putMetrics(d, metrics) +} + +func (cd *CanaryDeployment) putMetrics(d *dao.Simple, metrics *FeatureMetrics) { + key := cd.makeMetricsKey(metrics.FeatureName) + data := cd.serializeMetrics(metrics) + d.PutStorageItem(cd.contractID, key, data) +} + +func (cd *CanaryDeployment) recordHistory(d *dao.Simple, name string, blockHeight uint32, status FeatureStatus) { + key := make([]byte, 1+len(name)+4) + key[0] = canaryPrefixHistory + copy(key[1:], name) + binary.BigEndian.PutUint32(key[1+len(name):], blockHeight) + + data := []byte{byte(status)} + d.PutStorageItem(cd.contractID, key, data) +} + +// Serialization helpers. +func (cd *CanaryDeployment) serializeFeature(f *FeatureFlag) []byte { + nameBytes := []byte(f.Name) + descBytes := []byte(f.Description) + + size := 4 + len(nameBytes) + 4 + len(descBytes) + 1 + 4 + 4 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 20 + 1 + data := make([]byte, size) + offset := 0 + + binary.BigEndian.PutUint32(data[offset:], uint32(len(nameBytes))) + offset += 4 + copy(data[offset:], nameBytes) + offset += len(nameBytes) + + binary.BigEndian.PutUint32(data[offset:], uint32(len(descBytes))) + offset += 4 + copy(data[offset:], descBytes) + offset += len(descBytes) + + data[offset] = byte(f.Status) + offset++ + + data[offset] = f.RolloutPercent + offset++ + data[offset] = f.TargetPercent + offset++ + data[offset] = f.IncrementPercent + offset++ + + binary.BigEndian.PutUint32(data[offset:], f.IncrementBlocks) + offset += 4 + binary.BigEndian.PutUint32(data[offset:], f.StartBlock) + offset += 4 + binary.BigEndian.PutUint32(data[offset:], f.LastIncrementBlock) + offset += 4 + + binary.BigEndian.PutUint64(data[offset:], f.ErrorCount) + offset += 8 + binary.BigEndian.PutUint64(data[offset:], f.SuccessCount) + offset += 8 + + binary.BigEndian.PutUint32(data[offset:], f.MaxErrorRate) + offset += 4 + binary.BigEndian.PutUint64(data[offset:], f.MinSuccessCount) + offset += 8 + + copy(data[offset:], f.EnabledBy.BytesBE()) + offset += 20 + + data[offset] = byte(f.RollbackReason) + + return data +} + +func (cd *CanaryDeployment) deserializeFeature(data []byte) *FeatureFlag { + if len(data) < 8 { + return nil + } + + f := &FeatureFlag{} + offset := 0 + + nameLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(nameLen) > len(data) { + return nil + } + f.Name = string(data[offset : offset+int(nameLen)]) + offset += int(nameLen) + + if offset+4 > len(data) { + return nil + } + descLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(descLen) > len(data) { + return nil + } + f.Description = string(data[offset : offset+int(descLen)]) + offset += int(descLen) + + if offset+61 > len(data) { + return nil + } + + f.Status = FeatureStatus(data[offset]) + offset++ + f.RolloutPercent = data[offset] + offset++ + f.TargetPercent = data[offset] + offset++ + f.IncrementPercent = data[offset] + offset++ + + f.IncrementBlocks = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + f.StartBlock = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + f.LastIncrementBlock = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + + f.ErrorCount = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + f.SuccessCount = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + + f.MaxErrorRate = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + f.MinSuccessCount = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + + f.EnabledBy, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) + offset += 20 + + f.RollbackReason = RollbackReason(data[offset]) + + return f +} + +func (cd *CanaryDeployment) serializeMetrics(m *FeatureMetrics) []byte { + nameBytes := []byte(m.FeatureName) + data := make([]byte, 4+len(nameBytes)+48) + offset := 0 + + binary.BigEndian.PutUint32(data[offset:], uint32(len(nameBytes))) + offset += 4 + copy(data[offset:], nameBytes) + offset += len(nameBytes) + + binary.BigEndian.PutUint64(data[offset:], m.TotalOperations) + offset += 8 + binary.BigEndian.PutUint64(data[offset:], m.SuccessfulOps) + offset += 8 + binary.BigEndian.PutUint64(data[offset:], m.FailedOps) + offset += 8 + binary.BigEndian.PutUint64(data[offset:], m.AverageLatency) + offset += 8 + binary.BigEndian.PutUint64(data[offset:], m.PeakLatency) + offset += 8 + binary.BigEndian.PutUint64(data[offset:], m.InvariantChecks) + + return data +} + +func (cd *CanaryDeployment) deserializeMetrics(data []byte) *FeatureMetrics { + if len(data) < 8 { + return nil + } + + m := &FeatureMetrics{} + offset := 0 + + nameLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(nameLen) > len(data) { + return nil + } + m.FeatureName = string(data[offset : offset+int(nameLen)]) + offset += int(nameLen) + + if offset+48 > len(data) { + return nil + } + + m.TotalOperations = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + m.SuccessfulOps = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + m.FailedOps = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + m.AverageLatency = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + m.PeakLatency = binary.BigEndian.Uint64(data[offset:]) + offset += 8 + m.InvariantChecks = binary.BigEndian.Uint64(data[offset:]) + + return m +} + +// StandardFeatureFlags defines common feature flag names. +var StandardFeatureFlags = struct { + VitaRecoveryV2 string + TributeGamingDetect string + CrossChainProofs string + CommitRevealInvest string + EnhancedAuditLogging string + CircuitBreakerAuto string +}{ + VitaRecoveryV2: "vita_recovery_v2", + TributeGamingDetect: "tribute_gaming_detect", + CrossChainProofs: "cross_chain_proofs", + CommitRevealInvest: "commit_reveal_invest", + EnhancedAuditLogging: "enhanced_audit_logging", + CircuitBreakerAuto: "circuit_breaker_auto", +} diff --git a/pkg/core/native/circuit_breaker.go b/pkg/core/native/circuit_breaker.go index a51dccb..7c961da 100644 --- a/pkg/core/native/circuit_breaker.go +++ b/pkg/core/native/circuit_breaker.go @@ -1,555 +1,555 @@ -package native - -import ( - "encoding/binary" - "errors" - - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" -) - -// ARCH-001: Circuit Breaker System -// Provides automatic protection against anomalous behavior by halting -// contract operations when thresholds are exceeded. This is a critical -// safety mechanism for production deployments. - -// CircuitState represents the current state of a circuit breaker. -type CircuitState uint8 - -const ( - // CircuitClosed means normal operation (requests flow through) - CircuitClosed CircuitState = iota - // CircuitOpen means halted (requests are rejected) - CircuitOpen - // CircuitHalfOpen means testing recovery (limited requests allowed) - CircuitHalfOpen -) - -// TripReason identifies why a circuit breaker was tripped. -type TripReason uint8 - -const ( - TripReasonManual TripReason = iota - TripReasonRateLimit - TripReasonBalanceAnomaly - TripReasonSecurityBreach - TripReasonExternalDependency - TripReasonResourceExhaustion - TripReasonConsensusFailure -) - -// CircuitBreakerConfig contains settings for a circuit breaker. -type CircuitBreakerConfig struct { - // Name identifies this circuit breaker - Name string - // ContractID is the contract this breaker protects - ContractID int32 - // FailureThreshold is failures before tripping - FailureThreshold uint32 - // SuccessThreshold is successes needed to close after half-open - SuccessThreshold uint32 - // TimeoutBlocks is blocks before moving from open to half-open - TimeoutBlocks uint32 - // CooldownBlocks is minimum blocks between state changes - CooldownBlocks uint32 - // AutoRecover determines if breaker can auto-recover - AutoRecover bool -} - -// CircuitBreakerState tracks the current state of a circuit breaker. -type CircuitBreakerState struct { - State CircuitState - FailureCount uint32 - SuccessCount uint32 - LastStateChange uint32 - LastFailure uint32 - TripReason TripReason - TrippedBy util.Uint160 - TotalTrips uint64 - ConsecutiveTrips uint32 -} - -// Storage prefixes for circuit breakers. -const ( - circuitPrefixConfig byte = 0xCB // name -> CircuitBreakerConfig - circuitPrefixState byte = 0xCC // name -> CircuitBreakerState - circuitPrefixHistory byte = 0xCD // name + timestamp -> TripEvent - circuitPrefixGlobal byte = 0xCE // -> GlobalCircuitState -) - -// Circuit breaker errors. -var ( - ErrCircuitOpen = errors.New("circuit breaker is open") - ErrCircuitHalfOpen = errors.New("circuit breaker is half-open, limited operations") - ErrCircuitCooldown = errors.New("circuit breaker cooldown period active") - ErrCircuitNotFound = errors.New("circuit breaker not found") - ErrCircuitAutoRecover = errors.New("circuit breaker cannot be manually closed when auto-recover enabled") -) - -// Default circuit breaker settings. -const ( - DefaultFailureThreshold = 10 - DefaultSuccessThreshold = 5 - DefaultTimeoutBlocks = 100 - DefaultCooldownBlocks = 10 -) - -// CircuitBreaker provides circuit breaker functionality for contracts. -type CircuitBreaker struct { - contractID int32 -} - -// NewCircuitBreaker creates a new circuit breaker manager. -func NewCircuitBreaker(contractID int32) *CircuitBreaker { - return &CircuitBreaker{contractID: contractID} -} - -// RegisterBreaker registers a new circuit breaker with configuration. -func (cb *CircuitBreaker) RegisterBreaker(d *dao.Simple, cfg *CircuitBreakerConfig) { - key := cb.makeConfigKey(cfg.Name) - data := cb.serializeConfig(cfg) - d.PutStorageItem(cb.contractID, key, data) - - // Initialize state as closed - state := &CircuitBreakerState{ - State: CircuitClosed, - } - cb.putState(d, cfg.Name, state) -} - -// GetState retrieves the current state of a circuit breaker. -func (cb *CircuitBreaker) GetState(d *dao.Simple, name string) *CircuitBreakerState { - key := cb.makeStateKey(name) - si := d.GetStorageItem(cb.contractID, key) - if si == nil { - return nil - } - return cb.deserializeState(si) -} - -// GetConfig retrieves circuit breaker configuration. -func (cb *CircuitBreaker) GetConfig(d *dao.Simple, name string) *CircuitBreakerConfig { - key := cb.makeConfigKey(name) - si := d.GetStorageItem(cb.contractID, key) - if si == nil { - return nil - } - return cb.deserializeConfig(si) -} - -// AllowRequest checks if a request should be allowed through. -func (cb *CircuitBreaker) AllowRequest(d *dao.Simple, name string, currentBlock uint32) error { - state := cb.GetState(d, name) - if state == nil { - return ErrCircuitNotFound - } - - switch state.State { - case CircuitClosed: - return nil - case CircuitOpen: - cfg := cb.GetConfig(d, name) - if cfg == nil { - return ErrCircuitNotFound - } - // Check if timeout has elapsed for potential recovery - if cfg.AutoRecover && currentBlock >= state.LastStateChange+cfg.TimeoutBlocks { - // Transition to half-open - cb.transitionState(d, name, CircuitHalfOpen, currentBlock) - return ErrCircuitHalfOpen - } - return ErrCircuitOpen - case CircuitHalfOpen: - return ErrCircuitHalfOpen - } - - return nil -} - -// RecordSuccess records a successful operation. -func (cb *CircuitBreaker) RecordSuccess(d *dao.Simple, name string, currentBlock uint32) { - state := cb.GetState(d, name) - if state == nil { - return - } - - if state.State == CircuitHalfOpen { - state.SuccessCount++ - cfg := cb.GetConfig(d, name) - if cfg != nil && state.SuccessCount >= cfg.SuccessThreshold { - // Close the circuit - cb.transitionState(d, name, CircuitClosed, currentBlock) - return - } - } - - // Reset failure count on success when closed - if state.State == CircuitClosed { - state.FailureCount = 0 - } - cb.putState(d, name, state) -} - -// RecordFailure records a failed operation. -func (cb *CircuitBreaker) RecordFailure(d *dao.Simple, name string, currentBlock uint32, reason TripReason) { - state := cb.GetState(d, name) - if state == nil { - return - } - - state.FailureCount++ - state.LastFailure = currentBlock - - cfg := cb.GetConfig(d, name) - if cfg == nil { - return - } - - switch state.State { - case CircuitClosed: - if state.FailureCount >= cfg.FailureThreshold { - cb.tripBreaker(d, name, currentBlock, reason, util.Uint160{}) - } else { - cb.putState(d, name, state) - } - case CircuitHalfOpen: - // Any failure in half-open immediately trips - cb.tripBreaker(d, name, currentBlock, reason, util.Uint160{}) - } -} - -// TripBreaker manually trips a circuit breaker. -func (cb *CircuitBreaker) TripBreaker(d *dao.Simple, name string, currentBlock uint32, reason TripReason, tripper util.Uint160) error { - state := cb.GetState(d, name) - if state == nil { - return ErrCircuitNotFound - } - - cfg := cb.GetConfig(d, name) - if cfg == nil { - return ErrCircuitNotFound - } - - // Check cooldown - if currentBlock < state.LastStateChange+cfg.CooldownBlocks { - return ErrCircuitCooldown - } - - cb.tripBreaker(d, name, currentBlock, reason, tripper) - return nil -} - -// ResetBreaker manually resets a circuit breaker to closed. -func (cb *CircuitBreaker) ResetBreaker(d *dao.Simple, name string, currentBlock uint32) error { - state := cb.GetState(d, name) - if state == nil { - return ErrCircuitNotFound - } - - cfg := cb.GetConfig(d, name) - if cfg == nil { - return ErrCircuitNotFound - } - - // Check cooldown - if currentBlock < state.LastStateChange+cfg.CooldownBlocks { - return ErrCircuitCooldown - } - - cb.transitionState(d, name, CircuitClosed, currentBlock) - return nil -} - -// tripBreaker internal method to trip the breaker. -func (cb *CircuitBreaker) tripBreaker(d *dao.Simple, name string, currentBlock uint32, reason TripReason, tripper util.Uint160) { - state := cb.GetState(d, name) - if state == nil { - return - } - - state.State = CircuitOpen - state.TripReason = reason - state.TrippedBy = tripper - state.LastStateChange = currentBlock - state.TotalTrips++ - state.ConsecutiveTrips++ - state.SuccessCount = 0 - - cb.putState(d, name, state) - cb.recordTripEvent(d, name, currentBlock, reason, tripper) -} - -// transitionState changes the circuit state. -func (cb *CircuitBreaker) transitionState(d *dao.Simple, name string, newState CircuitState, currentBlock uint32) { - state := cb.GetState(d, name) - if state == nil { - return - } - - state.State = newState - state.LastStateChange = currentBlock - - if newState == CircuitClosed { - state.FailureCount = 0 - state.SuccessCount = 0 - state.ConsecutiveTrips = 0 - } else if newState == CircuitHalfOpen { - state.SuccessCount = 0 - } - - cb.putState(d, name, state) -} - -// TripEvent records a circuit breaker trip for auditing. -type TripEvent struct { - Name string - BlockHeight uint32 - Reason TripReason - TrippedBy util.Uint160 -} - -// recordTripEvent stores a trip event in history. -func (cb *CircuitBreaker) recordTripEvent(d *dao.Simple, name string, blockHeight uint32, reason TripReason, tripper util.Uint160) { - key := make([]byte, 1+len(name)+4) - key[0] = circuitPrefixHistory - copy(key[1:], name) - binary.BigEndian.PutUint32(key[1+len(name):], blockHeight) - - data := make([]byte, 21) - data[0] = byte(reason) - copy(data[1:], tripper.BytesBE()) - d.PutStorageItem(cb.contractID, key, data) -} - -// GetTripHistory retrieves trip history for a circuit breaker. -func (cb *CircuitBreaker) GetTripHistory(d *dao.Simple, name string, limit int) []TripEvent { - var events []TripEvent - prefix := make([]byte, 1+len(name)) - prefix[0] = circuitPrefixHistory - copy(prefix[1:], name) - - count := 0 - d.Seek(cb.contractID, storage.SeekRange{Prefix: prefix, Backwards: true}, func(k, v []byte) bool { - if count >= limit || len(k) < 4 || len(v) < 21 { - return false - } - event := TripEvent{ - Name: name, - BlockHeight: binary.BigEndian.Uint32(k[len(k)-4:]), - Reason: TripReason(v[0]), - } - event.TrippedBy, _ = util.Uint160DecodeBytesBE(v[1:21]) - events = append(events, event) - count++ - return true - }) - - return events -} - -// IsOpen returns true if the circuit is open (blocking requests). -func (cb *CircuitBreaker) IsOpen(d *dao.Simple, name string) bool { - state := cb.GetState(d, name) - return state != nil && state.State == CircuitOpen -} - -// IsClosed returns true if the circuit is closed (allowing requests). -func (cb *CircuitBreaker) IsClosed(d *dao.Simple, name string) bool { - state := cb.GetState(d, name) - return state != nil && state.State == CircuitClosed -} - -// GlobalCircuitState tracks system-wide circuit breaker status. -type GlobalCircuitState struct { - // EmergencyShutdown halts all protected operations - EmergencyShutdown bool - // ShutdownBlock is when emergency was triggered - ShutdownBlock uint32 - // ShutdownBy is who triggered emergency - ShutdownBy util.Uint160 - // ActiveBreakers is count of currently open breakers - ActiveBreakers uint32 -} - -// GetGlobalState retrieves the global circuit breaker state. -func (cb *CircuitBreaker) GetGlobalState(d *dao.Simple) *GlobalCircuitState { - key := []byte{circuitPrefixGlobal} - si := d.GetStorageItem(cb.contractID, key) - if si == nil { - return &GlobalCircuitState{} - } - if len(si) < 26 { - return &GlobalCircuitState{} - } - - return &GlobalCircuitState{ - EmergencyShutdown: si[0] == 1, - ShutdownBlock: binary.BigEndian.Uint32(si[1:5]), - ShutdownBy: mustDecodeUint160(si[5:25]), - ActiveBreakers: binary.BigEndian.Uint32(si[25:29]), - } -} - -// SetEmergencyShutdown enables or disables emergency shutdown. -func (cb *CircuitBreaker) SetEmergencyShutdown(d *dao.Simple, enabled bool, blockHeight uint32, triggeredBy util.Uint160) { - state := cb.GetGlobalState(d) - state.EmergencyShutdown = enabled - if enabled { - state.ShutdownBlock = blockHeight - state.ShutdownBy = triggeredBy - } - - key := []byte{circuitPrefixGlobal} - data := make([]byte, 29) - if state.EmergencyShutdown { - data[0] = 1 - } - binary.BigEndian.PutUint32(data[1:5], state.ShutdownBlock) - copy(data[5:25], state.ShutdownBy.BytesBE()) - binary.BigEndian.PutUint32(data[25:29], state.ActiveBreakers) - d.PutStorageItem(cb.contractID, key, data) -} - -// IsEmergencyShutdown returns true if emergency shutdown is active. -func (cb *CircuitBreaker) IsEmergencyShutdown(d *dao.Simple) bool { - state := cb.GetGlobalState(d) - return state.EmergencyShutdown -} - -// Helper methods. -func (cb *CircuitBreaker) makeConfigKey(name string) []byte { - key := make([]byte, 1+len(name)) - key[0] = circuitPrefixConfig - copy(key[1:], name) - return key -} - -func (cb *CircuitBreaker) makeStateKey(name string) []byte { - key := make([]byte, 1+len(name)) - key[0] = circuitPrefixState - copy(key[1:], name) - return key -} - -func (cb *CircuitBreaker) putState(d *dao.Simple, name string, state *CircuitBreakerState) { - key := cb.makeStateKey(name) - data := cb.serializeState(state) - d.PutStorageItem(cb.contractID, key, data) -} - -// Serialization helpers. -func (cb *CircuitBreaker) serializeConfig(cfg *CircuitBreakerConfig) []byte { - nameBytes := []byte(cfg.Name) - data := make([]byte, 4+len(nameBytes)+4+16+1) - offset := 0 - - binary.BigEndian.PutUint32(data[offset:], uint32(len(nameBytes))) - offset += 4 - copy(data[offset:], nameBytes) - offset += len(nameBytes) - - binary.BigEndian.PutUint32(data[offset:], uint32(cfg.ContractID)) - offset += 4 - binary.BigEndian.PutUint32(data[offset:], cfg.FailureThreshold) - offset += 4 - binary.BigEndian.PutUint32(data[offset:], cfg.SuccessThreshold) - offset += 4 - binary.BigEndian.PutUint32(data[offset:], cfg.TimeoutBlocks) - offset += 4 - binary.BigEndian.PutUint32(data[offset:], cfg.CooldownBlocks) - offset += 4 - if cfg.AutoRecover { - data[offset] = 1 - } - - return data -} - -func (cb *CircuitBreaker) deserializeConfig(data []byte) *CircuitBreakerConfig { - if len(data) < 8 { - return nil - } - cfg := &CircuitBreakerConfig{} - offset := 0 - - nameLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(nameLen) > len(data) { - return nil - } - cfg.Name = string(data[offset : offset+int(nameLen)]) - offset += int(nameLen) - - if offset+17 > len(data) { - return nil - } - - cfg.ContractID = int32(binary.BigEndian.Uint32(data[offset:])) - offset += 4 - cfg.FailureThreshold = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - cfg.SuccessThreshold = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - cfg.TimeoutBlocks = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - cfg.CooldownBlocks = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - cfg.AutoRecover = data[offset] == 1 - - return cfg -} - -func (cb *CircuitBreaker) serializeState(state *CircuitBreakerState) []byte { - data := make([]byte, 46) - data[0] = byte(state.State) - binary.BigEndian.PutUint32(data[1:5], state.FailureCount) - binary.BigEndian.PutUint32(data[5:9], state.SuccessCount) - binary.BigEndian.PutUint32(data[9:13], state.LastStateChange) - binary.BigEndian.PutUint32(data[13:17], state.LastFailure) - data[17] = byte(state.TripReason) - copy(data[18:38], state.TrippedBy.BytesBE()) - binary.BigEndian.PutUint64(data[38:46], state.TotalTrips) - return data -} - -func (cb *CircuitBreaker) deserializeState(data []byte) *CircuitBreakerState { - if len(data) < 46 { - return nil - } - state := &CircuitBreakerState{ - State: CircuitState(data[0]), - FailureCount: binary.BigEndian.Uint32(data[1:5]), - SuccessCount: binary.BigEndian.Uint32(data[5:9]), - LastStateChange: binary.BigEndian.Uint32(data[9:13]), - LastFailure: binary.BigEndian.Uint32(data[13:17]), - TripReason: TripReason(data[17]), - TotalTrips: binary.BigEndian.Uint64(data[38:46]), - } - state.TrippedBy, _ = util.Uint160DecodeBytesBE(data[18:38]) - return state -} - -func mustDecodeUint160(data []byte) util.Uint160 { - u, _ := util.Uint160DecodeBytesBE(data) - return u -} - -// StandardCircuitBreakers defines common circuit breaker names. -var StandardCircuitBreakers = struct { - VTSTransfers string - VitaRegistration string - CrossChainBridge string - HealthRecords string - InvestmentOps string - GovernanceVoting string - TributeAssessment string -}{ - VTSTransfers: "vts_transfers", - VitaRegistration: "vita_registration", - CrossChainBridge: "cross_chain_bridge", - HealthRecords: "health_records", - InvestmentOps: "investment_ops", - GovernanceVoting: "governance_voting", - TributeAssessment: "tribute_assessment", -} +package native + +import ( + "encoding/binary" + "errors" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" +) + +// ARCH-001: Circuit Breaker System +// Provides automatic protection against anomalous behavior by halting +// contract operations when thresholds are exceeded. This is a critical +// safety mechanism for production deployments. + +// CircuitState represents the current state of a circuit breaker. +type CircuitState uint8 + +const ( + // CircuitClosed means normal operation (requests flow through) + CircuitClosed CircuitState = iota + // CircuitOpen means halted (requests are rejected) + CircuitOpen + // CircuitHalfOpen means testing recovery (limited requests allowed) + CircuitHalfOpen +) + +// TripReason identifies why a circuit breaker was tripped. +type TripReason uint8 + +const ( + TripReasonManual TripReason = iota + TripReasonRateLimit + TripReasonBalanceAnomaly + TripReasonSecurityBreach + TripReasonExternalDependency + TripReasonResourceExhaustion + TripReasonConsensusFailure +) + +// CircuitBreakerConfig contains settings for a circuit breaker. +type CircuitBreakerConfig struct { + // Name identifies this circuit breaker + Name string + // ContractID is the contract this breaker protects + ContractID int32 + // FailureThreshold is failures before tripping + FailureThreshold uint32 + // SuccessThreshold is successes needed to close after half-open + SuccessThreshold uint32 + // TimeoutBlocks is blocks before moving from open to half-open + TimeoutBlocks uint32 + // CooldownBlocks is minimum blocks between state changes + CooldownBlocks uint32 + // AutoRecover determines if breaker can auto-recover + AutoRecover bool +} + +// CircuitBreakerState tracks the current state of a circuit breaker. +type CircuitBreakerState struct { + State CircuitState + FailureCount uint32 + SuccessCount uint32 + LastStateChange uint32 + LastFailure uint32 + TripReason TripReason + TrippedBy util.Uint160 + TotalTrips uint64 + ConsecutiveTrips uint32 +} + +// Storage prefixes for circuit breakers. +const ( + circuitPrefixConfig byte = 0xCB // name -> CircuitBreakerConfig + circuitPrefixState byte = 0xCC // name -> CircuitBreakerState + circuitPrefixHistory byte = 0xCD // name + timestamp -> TripEvent + circuitPrefixGlobal byte = 0xCE // -> GlobalCircuitState +) + +// Circuit breaker errors. +var ( + ErrCircuitOpen = errors.New("circuit breaker is open") + ErrCircuitHalfOpen = errors.New("circuit breaker is half-open, limited operations") + ErrCircuitCooldown = errors.New("circuit breaker cooldown period active") + ErrCircuitNotFound = errors.New("circuit breaker not found") + ErrCircuitAutoRecover = errors.New("circuit breaker cannot be manually closed when auto-recover enabled") +) + +// Default circuit breaker settings. +const ( + DefaultFailureThreshold = 10 + DefaultSuccessThreshold = 5 + DefaultTimeoutBlocks = 100 + DefaultCooldownBlocks = 10 +) + +// CircuitBreaker provides circuit breaker functionality for contracts. +type CircuitBreaker struct { + contractID int32 +} + +// NewCircuitBreaker creates a new circuit breaker manager. +func NewCircuitBreaker(contractID int32) *CircuitBreaker { + return &CircuitBreaker{contractID: contractID} +} + +// RegisterBreaker registers a new circuit breaker with configuration. +func (cb *CircuitBreaker) RegisterBreaker(d *dao.Simple, cfg *CircuitBreakerConfig) { + key := cb.makeConfigKey(cfg.Name) + data := cb.serializeConfig(cfg) + d.PutStorageItem(cb.contractID, key, data) + + // Initialize state as closed + state := &CircuitBreakerState{ + State: CircuitClosed, + } + cb.putState(d, cfg.Name, state) +} + +// GetState retrieves the current state of a circuit breaker. +func (cb *CircuitBreaker) GetState(d *dao.Simple, name string) *CircuitBreakerState { + key := cb.makeStateKey(name) + si := d.GetStorageItem(cb.contractID, key) + if si == nil { + return nil + } + return cb.deserializeState(si) +} + +// GetConfig retrieves circuit breaker configuration. +func (cb *CircuitBreaker) GetConfig(d *dao.Simple, name string) *CircuitBreakerConfig { + key := cb.makeConfigKey(name) + si := d.GetStorageItem(cb.contractID, key) + if si == nil { + return nil + } + return cb.deserializeConfig(si) +} + +// AllowRequest checks if a request should be allowed through. +func (cb *CircuitBreaker) AllowRequest(d *dao.Simple, name string, currentBlock uint32) error { + state := cb.GetState(d, name) + if state == nil { + return ErrCircuitNotFound + } + + switch state.State { + case CircuitClosed: + return nil + case CircuitOpen: + cfg := cb.GetConfig(d, name) + if cfg == nil { + return ErrCircuitNotFound + } + // Check if timeout has elapsed for potential recovery + if cfg.AutoRecover && currentBlock >= state.LastStateChange+cfg.TimeoutBlocks { + // Transition to half-open + cb.transitionState(d, name, CircuitHalfOpen, currentBlock) + return ErrCircuitHalfOpen + } + return ErrCircuitOpen + case CircuitHalfOpen: + return ErrCircuitHalfOpen + } + + return nil +} + +// RecordSuccess records a successful operation. +func (cb *CircuitBreaker) RecordSuccess(d *dao.Simple, name string, currentBlock uint32) { + state := cb.GetState(d, name) + if state == nil { + return + } + + if state.State == CircuitHalfOpen { + state.SuccessCount++ + cfg := cb.GetConfig(d, name) + if cfg != nil && state.SuccessCount >= cfg.SuccessThreshold { + // Close the circuit + cb.transitionState(d, name, CircuitClosed, currentBlock) + return + } + } + + // Reset failure count on success when closed + if state.State == CircuitClosed { + state.FailureCount = 0 + } + cb.putState(d, name, state) +} + +// RecordFailure records a failed operation. +func (cb *CircuitBreaker) RecordFailure(d *dao.Simple, name string, currentBlock uint32, reason TripReason) { + state := cb.GetState(d, name) + if state == nil { + return + } + + state.FailureCount++ + state.LastFailure = currentBlock + + cfg := cb.GetConfig(d, name) + if cfg == nil { + return + } + + switch state.State { + case CircuitClosed: + if state.FailureCount >= cfg.FailureThreshold { + cb.tripBreaker(d, name, currentBlock, reason, util.Uint160{}) + } else { + cb.putState(d, name, state) + } + case CircuitHalfOpen: + // Any failure in half-open immediately trips + cb.tripBreaker(d, name, currentBlock, reason, util.Uint160{}) + } +} + +// TripBreaker manually trips a circuit breaker. +func (cb *CircuitBreaker) TripBreaker(d *dao.Simple, name string, currentBlock uint32, reason TripReason, tripper util.Uint160) error { + state := cb.GetState(d, name) + if state == nil { + return ErrCircuitNotFound + } + + cfg := cb.GetConfig(d, name) + if cfg == nil { + return ErrCircuitNotFound + } + + // Check cooldown + if currentBlock < state.LastStateChange+cfg.CooldownBlocks { + return ErrCircuitCooldown + } + + cb.tripBreaker(d, name, currentBlock, reason, tripper) + return nil +} + +// ResetBreaker manually resets a circuit breaker to closed. +func (cb *CircuitBreaker) ResetBreaker(d *dao.Simple, name string, currentBlock uint32) error { + state := cb.GetState(d, name) + if state == nil { + return ErrCircuitNotFound + } + + cfg := cb.GetConfig(d, name) + if cfg == nil { + return ErrCircuitNotFound + } + + // Check cooldown + if currentBlock < state.LastStateChange+cfg.CooldownBlocks { + return ErrCircuitCooldown + } + + cb.transitionState(d, name, CircuitClosed, currentBlock) + return nil +} + +// tripBreaker internal method to trip the breaker. +func (cb *CircuitBreaker) tripBreaker(d *dao.Simple, name string, currentBlock uint32, reason TripReason, tripper util.Uint160) { + state := cb.GetState(d, name) + if state == nil { + return + } + + state.State = CircuitOpen + state.TripReason = reason + state.TrippedBy = tripper + state.LastStateChange = currentBlock + state.TotalTrips++ + state.ConsecutiveTrips++ + state.SuccessCount = 0 + + cb.putState(d, name, state) + cb.recordTripEvent(d, name, currentBlock, reason, tripper) +} + +// transitionState changes the circuit state. +func (cb *CircuitBreaker) transitionState(d *dao.Simple, name string, newState CircuitState, currentBlock uint32) { + state := cb.GetState(d, name) + if state == nil { + return + } + + state.State = newState + state.LastStateChange = currentBlock + + if newState == CircuitClosed { + state.FailureCount = 0 + state.SuccessCount = 0 + state.ConsecutiveTrips = 0 + } else if newState == CircuitHalfOpen { + state.SuccessCount = 0 + } + + cb.putState(d, name, state) +} + +// TripEvent records a circuit breaker trip for auditing. +type TripEvent struct { + Name string + BlockHeight uint32 + Reason TripReason + TrippedBy util.Uint160 +} + +// recordTripEvent stores a trip event in history. +func (cb *CircuitBreaker) recordTripEvent(d *dao.Simple, name string, blockHeight uint32, reason TripReason, tripper util.Uint160) { + key := make([]byte, 1+len(name)+4) + key[0] = circuitPrefixHistory + copy(key[1:], name) + binary.BigEndian.PutUint32(key[1+len(name):], blockHeight) + + data := make([]byte, 21) + data[0] = byte(reason) + copy(data[1:], tripper.BytesBE()) + d.PutStorageItem(cb.contractID, key, data) +} + +// GetTripHistory retrieves trip history for a circuit breaker. +func (cb *CircuitBreaker) GetTripHistory(d *dao.Simple, name string, limit int) []TripEvent { + var events []TripEvent + prefix := make([]byte, 1+len(name)) + prefix[0] = circuitPrefixHistory + copy(prefix[1:], name) + + count := 0 + d.Seek(cb.contractID, storage.SeekRange{Prefix: prefix, Backwards: true}, func(k, v []byte) bool { + if count >= limit || len(k) < 4 || len(v) < 21 { + return false + } + event := TripEvent{ + Name: name, + BlockHeight: binary.BigEndian.Uint32(k[len(k)-4:]), + Reason: TripReason(v[0]), + } + event.TrippedBy, _ = util.Uint160DecodeBytesBE(v[1:21]) + events = append(events, event) + count++ + return true + }) + + return events +} + +// IsOpen returns true if the circuit is open (blocking requests). +func (cb *CircuitBreaker) IsOpen(d *dao.Simple, name string) bool { + state := cb.GetState(d, name) + return state != nil && state.State == CircuitOpen +} + +// IsClosed returns true if the circuit is closed (allowing requests). +func (cb *CircuitBreaker) IsClosed(d *dao.Simple, name string) bool { + state := cb.GetState(d, name) + return state != nil && state.State == CircuitClosed +} + +// GlobalCircuitState tracks system-wide circuit breaker status. +type GlobalCircuitState struct { + // EmergencyShutdown halts all protected operations + EmergencyShutdown bool + // ShutdownBlock is when emergency was triggered + ShutdownBlock uint32 + // ShutdownBy is who triggered emergency + ShutdownBy util.Uint160 + // ActiveBreakers is count of currently open breakers + ActiveBreakers uint32 +} + +// GetGlobalState retrieves the global circuit breaker state. +func (cb *CircuitBreaker) GetGlobalState(d *dao.Simple) *GlobalCircuitState { + key := []byte{circuitPrefixGlobal} + si := d.GetStorageItem(cb.contractID, key) + if si == nil { + return &GlobalCircuitState{} + } + if len(si) < 26 { + return &GlobalCircuitState{} + } + + return &GlobalCircuitState{ + EmergencyShutdown: si[0] == 1, + ShutdownBlock: binary.BigEndian.Uint32(si[1:5]), + ShutdownBy: mustDecodeUint160(si[5:25]), + ActiveBreakers: binary.BigEndian.Uint32(si[25:29]), + } +} + +// SetEmergencyShutdown enables or disables emergency shutdown. +func (cb *CircuitBreaker) SetEmergencyShutdown(d *dao.Simple, enabled bool, blockHeight uint32, triggeredBy util.Uint160) { + state := cb.GetGlobalState(d) + state.EmergencyShutdown = enabled + if enabled { + state.ShutdownBlock = blockHeight + state.ShutdownBy = triggeredBy + } + + key := []byte{circuitPrefixGlobal} + data := make([]byte, 29) + if state.EmergencyShutdown { + data[0] = 1 + } + binary.BigEndian.PutUint32(data[1:5], state.ShutdownBlock) + copy(data[5:25], state.ShutdownBy.BytesBE()) + binary.BigEndian.PutUint32(data[25:29], state.ActiveBreakers) + d.PutStorageItem(cb.contractID, key, data) +} + +// IsEmergencyShutdown returns true if emergency shutdown is active. +func (cb *CircuitBreaker) IsEmergencyShutdown(d *dao.Simple) bool { + state := cb.GetGlobalState(d) + return state.EmergencyShutdown +} + +// Helper methods. +func (cb *CircuitBreaker) makeConfigKey(name string) []byte { + key := make([]byte, 1+len(name)) + key[0] = circuitPrefixConfig + copy(key[1:], name) + return key +} + +func (cb *CircuitBreaker) makeStateKey(name string) []byte { + key := make([]byte, 1+len(name)) + key[0] = circuitPrefixState + copy(key[1:], name) + return key +} + +func (cb *CircuitBreaker) putState(d *dao.Simple, name string, state *CircuitBreakerState) { + key := cb.makeStateKey(name) + data := cb.serializeState(state) + d.PutStorageItem(cb.contractID, key, data) +} + +// Serialization helpers. +func (cb *CircuitBreaker) serializeConfig(cfg *CircuitBreakerConfig) []byte { + nameBytes := []byte(cfg.Name) + data := make([]byte, 4+len(nameBytes)+4+16+1) + offset := 0 + + binary.BigEndian.PutUint32(data[offset:], uint32(len(nameBytes))) + offset += 4 + copy(data[offset:], nameBytes) + offset += len(nameBytes) + + binary.BigEndian.PutUint32(data[offset:], uint32(cfg.ContractID)) + offset += 4 + binary.BigEndian.PutUint32(data[offset:], cfg.FailureThreshold) + offset += 4 + binary.BigEndian.PutUint32(data[offset:], cfg.SuccessThreshold) + offset += 4 + binary.BigEndian.PutUint32(data[offset:], cfg.TimeoutBlocks) + offset += 4 + binary.BigEndian.PutUint32(data[offset:], cfg.CooldownBlocks) + offset += 4 + if cfg.AutoRecover { + data[offset] = 1 + } + + return data +} + +func (cb *CircuitBreaker) deserializeConfig(data []byte) *CircuitBreakerConfig { + if len(data) < 8 { + return nil + } + cfg := &CircuitBreakerConfig{} + offset := 0 + + nameLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(nameLen) > len(data) { + return nil + } + cfg.Name = string(data[offset : offset+int(nameLen)]) + offset += int(nameLen) + + if offset+17 > len(data) { + return nil + } + + cfg.ContractID = int32(binary.BigEndian.Uint32(data[offset:])) + offset += 4 + cfg.FailureThreshold = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + cfg.SuccessThreshold = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + cfg.TimeoutBlocks = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + cfg.CooldownBlocks = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + cfg.AutoRecover = data[offset] == 1 + + return cfg +} + +func (cb *CircuitBreaker) serializeState(state *CircuitBreakerState) []byte { + data := make([]byte, 46) + data[0] = byte(state.State) + binary.BigEndian.PutUint32(data[1:5], state.FailureCount) + binary.BigEndian.PutUint32(data[5:9], state.SuccessCount) + binary.BigEndian.PutUint32(data[9:13], state.LastStateChange) + binary.BigEndian.PutUint32(data[13:17], state.LastFailure) + data[17] = byte(state.TripReason) + copy(data[18:38], state.TrippedBy.BytesBE()) + binary.BigEndian.PutUint64(data[38:46], state.TotalTrips) + return data +} + +func (cb *CircuitBreaker) deserializeState(data []byte) *CircuitBreakerState { + if len(data) < 46 { + return nil + } + state := &CircuitBreakerState{ + State: CircuitState(data[0]), + FailureCount: binary.BigEndian.Uint32(data[1:5]), + SuccessCount: binary.BigEndian.Uint32(data[5:9]), + LastStateChange: binary.BigEndian.Uint32(data[9:13]), + LastFailure: binary.BigEndian.Uint32(data[13:17]), + TripReason: TripReason(data[17]), + TotalTrips: binary.BigEndian.Uint64(data[38:46]), + } + state.TrippedBy, _ = util.Uint160DecodeBytesBE(data[18:38]) + return state +} + +func mustDecodeUint160(data []byte) util.Uint160 { + u, _ := util.Uint160DecodeBytesBE(data) + return u +} + +// StandardCircuitBreakers defines common circuit breaker names. +var StandardCircuitBreakers = struct { + VTSTransfers string + VitaRegistration string + CrossChainBridge string + HealthRecords string + InvestmentOps string + GovernanceVoting string + TributeAssessment string +}{ + VTSTransfers: "vts_transfers", + VitaRegistration: "vita_registration", + CrossChainBridge: "cross_chain_bridge", + HealthRecords: "health_records", + InvestmentOps: "investment_ops", + GovernanceVoting: "governance_voting", + TributeAssessment: "tribute_assessment", +} diff --git a/pkg/core/native/collocatio.go b/pkg/core/native/collocatio.go index 1a6aa0b..68a7795 100644 --- a/pkg/core/native/collocatio.go +++ b/pkg/core/native/collocatio.go @@ -1,2614 +1,2614 @@ -package native - -import ( - "encoding/binary" - "fmt" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Collocatio represents the Investment native contract for democratic investment (PIO/EIO/CIO). -// Latin: "collocatio" = placement, arrangement (investment) -type Collocatio struct { - interop.ContractMD - Tutus ITutus - Vita IVita - RoleRegistry *RoleRegistry - VTS *VTS - Scire *Scire - Eligere *Eligere - Tribute *Tribute -} - -// Storage prefixes for Collocatio contract. -const ( - collocatioPrefixConfig byte = 0x01 // -> CollocatioConfig - collocatioPrefixOpportunity byte = 0x10 // opportunityID -> InvestmentOpportunity - collocatioPrefixOpportunityByType byte = 0x11 // type + opportunityID -> exists - collocatioPrefixOpportunityByStatus byte = 0x12 // status + opportunityID -> exists - collocatioPrefixOppCounter byte = 0x1F // -> next opportunity ID - collocatioPrefixInvestment byte = 0x20 // investmentID -> Investment - collocatioPrefixInvestmentByOpp byte = 0x21 // opportunityID + investmentID -> exists - collocatioPrefixInvestmentByInvestor byte = 0x22 // vitaID + investmentID -> exists - collocatioPrefixInvCounter byte = 0x2F // -> next investment ID - collocatioPrefixEligibility byte = 0x30 // vitaID -> InvestorEligibility - collocatioPrefixEligibilityByOwner byte = 0x31 // owner -> vitaID - collocatioPrefixViolation byte = 0x40 // violationID -> InvestmentViolation - collocatioPrefixViolationByInvestor byte = 0x41 // vitaID + violationID -> exists - collocatioPrefixViolationCounter byte = 0x4F // -> next violation ID - collocatioPrefixEmployment byte = 0x50 // employeeVitaID -> EmploymentVerification - collocatioPrefixEmploymentByEmployer byte = 0x51 // employerVitaID + employeeVitaID -> exists - collocatioPrefixContractor byte = 0x60 // contractorVitaID -> ContractorVerification - collocatioPrefixContractorByPlatform byte = 0x61 // platformHash + contractorVitaID -> exists - collocatioPrefixCommitment byte = 0x70 // commitmentID -> InvestmentCommitment - collocatioPrefixCommitmentByOpp byte = 0x71 // opportunityID + commitmentID -> exists - collocatioPrefixCommitmentByInvestor byte = 0x72 // vitaID + commitmentID -> exists - collocatioPrefixCommitmentCounter byte = 0x7F // -> next commitment ID -) - -// Collocatio events. -const ( - collocatioOpportunityCreatedEvent = "OpportunityCreated" - collocatioOpportunityActivatedEvent = "OpportunityActivated" - collocatioOpportunityClosedEvent = "OpportunityClosed" - collocatioOpportunityFailedEvent = "OpportunityFailed" - collocatioOpportunityCancelledEvent = "OpportunityCancelled" - collocatioInvestmentMadeEvent = "InvestmentMade" - collocatioInvestmentWithdrawnEvent = "InvestmentWithdrawn" - collocatioReturnsDistributedEvent = "ReturnsDistributed" - collocatioEligibilityUpdatedEvent = "EligibilityUpdated" - collocatioEducationCompletedEvent = "EducationCompleted" - collocatioViolationRecordedEvent = "ViolationRecorded" - collocatioViolationResolvedEvent = "ViolationResolved" - collocatioEmploymentVerifiedEvent = "EmploymentVerified" - collocatioEmploymentRevokedEvent = "EmploymentRevoked" - collocatioContractorVerifiedEvent = "ContractorVerified" - collocatioContractorRevokedEvent = "ContractorRevoked" - collocatioCommitmentCreatedEvent = "CommitmentCreated" - collocatioCommitmentRevealedEvent = "CommitmentRevealed" - collocatioCommitmentCanceledEvent = "CommitmentCanceled" -) - -// RoleInvestmentManager is the role ID for investment management. -const RoleInvestmentManager uint64 = 28 - -// Default config values. -const ( - defaultMinPIOParticipants uint64 = 100 - defaultMinEIOParticipants uint64 = 10 - defaultMinCIOParticipants uint64 = 25 - defaultMinInvestment uint64 = 100_00000000 // 100 VTS - defaultMaxIndividualCap uint64 = 1_000_000_00000000 // 1M VTS - defaultWealthConcentration uint64 = 500 // 5% - defaultCreationFee uint64 = 1000_00000000 // 1000 VTS - defaultInvestmentFee uint64 = 50 // 0.5% - defaultWithdrawalPenalty uint64 = 200 // 2% - defaultMinVotingPeriod uint32 = 10000 - defaultMinInvestmentPeriod uint32 = 20000 - defaultMinMaturityPeriod uint32 = 50000 - defaultMaxViolationsBeforeBan uint8 = 3 - defaultCommitRevealDelay uint32 = 10 // Min blocks between commit and reveal - defaultCommitRevealWindow uint32 = 1000 // Max blocks to reveal after delay -) - -var _ interop.Contract = (*Collocatio)(nil) - -func newCollocatio() *Collocatio { - c := &Collocatio{ - ContractMD: *interop.NewContractMD(nativenames.Collocatio, nativeids.Collocatio), - } - defer c.BuildHFSpecificMD(c.ActiveIn()) - - // getConfig - desc := NewDescriptor("getConfig", smartcontract.ArrayType) - md := NewMethodAndPrice(c.getConfig, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // getOpportunityCount - desc = NewDescriptor("getOpportunityCount", smartcontract.IntegerType) - md = NewMethodAndPrice(c.getOpportunityCount, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // getOpportunity - desc = NewDescriptor("getOpportunity", smartcontract.ArrayType, - manifest.NewParameter("opportunityID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.getOpportunity, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // createOpportunity - desc = NewDescriptor("createOpportunity", smartcontract.IntegerType, - manifest.NewParameter("oppType", smartcontract.IntegerType), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("description", smartcontract.StringType), - manifest.NewParameter("termsHash", smartcontract.Hash256Type), - manifest.NewParameter("minParticipants", smartcontract.IntegerType), - manifest.NewParameter("maxParticipants", smartcontract.IntegerType), - manifest.NewParameter("minInvestment", smartcontract.IntegerType), - manifest.NewParameter("maxInvestment", smartcontract.IntegerType), - manifest.NewParameter("targetPool", smartcontract.IntegerType), - manifest.NewParameter("expectedReturns", smartcontract.IntegerType), - manifest.NewParameter("riskLevel", smartcontract.IntegerType), - manifest.NewParameter("maturityBlocks", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.createOpportunity, 1<<17, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // activateOpportunity - desc = NewDescriptor("activateOpportunity", smartcontract.BoolType, - manifest.NewParameter("opportunityID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.activateOpportunity, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // invest - desc = NewDescriptor("invest", smartcontract.IntegerType, - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.invest, 1<<17, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // withdraw - desc = NewDescriptor("withdraw", smartcontract.BoolType, - manifest.NewParameter("investmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.withdraw, 1<<17, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // getEligibility - desc = NewDescriptor("getEligibility", smartcontract.ArrayType, - manifest.NewParameter("investor", smartcontract.Hash160Type)) - md = NewMethodAndPrice(c.getEligibility, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // setEligibility - desc = NewDescriptor("setEligibility", smartcontract.BoolType, - manifest.NewParameter("investor", smartcontract.Hash160Type), - manifest.NewParameter("eligibilityFlags", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.setEligibility, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // isEligible - desc = NewDescriptor("isEligible", smartcontract.BoolType, - manifest.NewParameter("investor", smartcontract.Hash160Type), - manifest.NewParameter("oppType", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.isEligible, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // getInvestment - desc = NewDescriptor("getInvestment", smartcontract.ArrayType, - manifest.NewParameter("investmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.getInvestment, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // getInvestmentCount - desc = NewDescriptor("getInvestmentCount", smartcontract.IntegerType) - md = NewMethodAndPrice(c.getInvestmentCount, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // recordViolation - desc = NewDescriptor("recordViolation", smartcontract.IntegerType, - manifest.NewParameter("violator", smartcontract.Hash160Type), - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("violationType", smartcontract.StringType), - manifest.NewParameter("description", smartcontract.StringType), - manifest.NewParameter("evidenceHash", smartcontract.Hash256Type), - manifest.NewParameter("penalty", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.recordViolation, 1<<17, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // resolveViolation - desc = NewDescriptor("resolveViolation", smartcontract.BoolType, - manifest.NewParameter("violationID", smartcontract.IntegerType), - manifest.NewParameter("resolution", smartcontract.StringType)) - md = NewMethodAndPrice(c.resolveViolation, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // getViolation - desc = NewDescriptor("getViolation", smartcontract.ArrayType, - manifest.NewParameter("violationID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.getViolation, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // verifyEmployment - desc = NewDescriptor("verifyEmployment", smartcontract.BoolType, - manifest.NewParameter("employee", smartcontract.Hash160Type), - manifest.NewParameter("employer", smartcontract.Hash160Type), - manifest.NewParameter("position", smartcontract.StringType), - manifest.NewParameter("startDate", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.verifyEmployment, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // revokeEmployment - desc = NewDescriptor("revokeEmployment", smartcontract.BoolType, - manifest.NewParameter("employee", smartcontract.Hash160Type), - manifest.NewParameter("employer", smartcontract.Hash160Type), - manifest.NewParameter("endDate", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.revokeEmployment, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // getEmploymentStatus - desc = NewDescriptor("getEmploymentStatus", smartcontract.ArrayType, - manifest.NewParameter("employee", smartcontract.Hash160Type)) - md = NewMethodAndPrice(c.getEmploymentStatus, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // verifyContractor - desc = NewDescriptor("verifyContractor", smartcontract.BoolType, - manifest.NewParameter("contractor", smartcontract.Hash160Type), - manifest.NewParameter("platform", smartcontract.Hash160Type), - manifest.NewParameter("platformID", smartcontract.StringType), - manifest.NewParameter("contractorID", smartcontract.StringType), - manifest.NewParameter("startDate", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.verifyContractor, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // revokeContractor - desc = NewDescriptor("revokeContractor", smartcontract.BoolType, - manifest.NewParameter("contractor", smartcontract.Hash160Type), - manifest.NewParameter("platform", smartcontract.Hash160Type), - manifest.NewParameter("endDate", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.revokeContractor, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // getContractorStatus - desc = NewDescriptor("getContractorStatus", smartcontract.ArrayType, - manifest.NewParameter("contractor", smartcontract.Hash160Type)) - md = NewMethodAndPrice(c.getContractorStatus, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // completeInvestmentEducation - desc = NewDescriptor("completeInvestmentEducation", smartcontract.BoolType, - manifest.NewParameter("investor", smartcontract.Hash160Type), - manifest.NewParameter("certificationID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.completeInvestmentEducation, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // distributeReturns - desc = NewDescriptor("distributeReturns", smartcontract.BoolType, - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("actualReturns", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.distributeReturns, 1<<18, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // cancelOpportunity - desc = NewDescriptor("cancelOpportunity", smartcontract.BoolType, - manifest.NewParameter("opportunityID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.cancelOpportunity, 1<<18, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // getInvestmentsByOpportunity - desc = NewDescriptor("getInvestmentsByOpportunity", smartcontract.ArrayType, - manifest.NewParameter("opportunityID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.getInvestmentsByOpportunity, 1<<16, callflag.ReadStates) - c.AddMethod(md, desc) - - // getInvestmentsByInvestor - desc = NewDescriptor("getInvestmentsByInvestor", smartcontract.ArrayType, - manifest.NewParameter("investor", smartcontract.Hash160Type)) - md = NewMethodAndPrice(c.getInvestmentsByInvestor, 1<<16, callflag.ReadStates) - c.AddMethod(md, desc) - - // getOpportunitiesByType - desc = NewDescriptor("getOpportunitiesByType", smartcontract.ArrayType, - manifest.NewParameter("oppType", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.getOpportunitiesByType, 1<<16, callflag.ReadStates) - c.AddMethod(md, desc) - - // getOpportunitiesByStatus - desc = NewDescriptor("getOpportunitiesByStatus", smartcontract.ArrayType, - manifest.NewParameter("status", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.getOpportunitiesByStatus, 1<<16, callflag.ReadStates) - c.AddMethod(md, desc) - - // commitInvestment - Phase 1 of commit-reveal (anti-front-running) - desc = NewDescriptor("commitInvestment", smartcontract.IntegerType, - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("commitment", smartcontract.Hash256Type)) - md = NewMethodAndPrice(c.commitInvestment, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // revealInvestment - Phase 2 of commit-reveal - desc = NewDescriptor("revealInvestment", smartcontract.IntegerType, - manifest.NewParameter("commitmentID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("nonce", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(c.revealInvestment, 1<<17, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // cancelCommitment - Cancel a pending commitment - desc = NewDescriptor("cancelCommitment", smartcontract.BoolType, - manifest.NewParameter("commitmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.cancelCommitment, 1<<16, callflag.States|callflag.AllowNotify) - c.AddMethod(md, desc) - - // getCommitment - Query commitment details - desc = NewDescriptor("getCommitment", smartcontract.ArrayType, - manifest.NewParameter("commitmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(c.getCommitment, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // getCommitmentCount - desc = NewDescriptor("getCommitmentCount", smartcontract.IntegerType) - md = NewMethodAndPrice(c.getCommitmentCount, 1<<15, callflag.ReadStates) - c.AddMethod(md, desc) - - // ===== Events ===== - eDesc := NewEventDescriptor(collocatioOpportunityCreatedEvent, - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("oppType", smartcontract.IntegerType), - manifest.NewParameter("creator", smartcontract.Hash160Type)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioOpportunityActivatedEvent, - manifest.NewParameter("opportunityID", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioInvestmentMadeEvent, - manifest.NewParameter("investmentID", smartcontract.IntegerType), - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("investor", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioInvestmentWithdrawnEvent, - manifest.NewParameter("investmentID", smartcontract.IntegerType), - manifest.NewParameter("returnAmount", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioEligibilityUpdatedEvent, - manifest.NewParameter("investor", smartcontract.Hash160Type), - manifest.NewParameter("eligibility", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioOpportunityClosedEvent, - manifest.NewParameter("opportunityID", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioOpportunityFailedEvent, - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioOpportunityCancelledEvent, - manifest.NewParameter("opportunityID", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioReturnsDistributedEvent, - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("totalReturns", smartcontract.IntegerType), - manifest.NewParameter("investorCount", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioEducationCompletedEvent, - manifest.NewParameter("investor", smartcontract.Hash160Type), - manifest.NewParameter("certificationID", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioViolationRecordedEvent, - manifest.NewParameter("violationID", smartcontract.IntegerType), - manifest.NewParameter("violator", smartcontract.Hash160Type), - manifest.NewParameter("penalty", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioViolationResolvedEvent, - manifest.NewParameter("violationID", smartcontract.IntegerType), - manifest.NewParameter("resolution", smartcontract.StringType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioEmploymentVerifiedEvent, - manifest.NewParameter("employee", smartcontract.Hash160Type), - manifest.NewParameter("employer", smartcontract.Hash160Type)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioEmploymentRevokedEvent, - manifest.NewParameter("employee", smartcontract.Hash160Type), - manifest.NewParameter("employer", smartcontract.Hash160Type)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioContractorVerifiedEvent, - manifest.NewParameter("contractor", smartcontract.Hash160Type), - manifest.NewParameter("platform", smartcontract.Hash160Type)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioContractorRevokedEvent, - manifest.NewParameter("contractor", smartcontract.Hash160Type), - manifest.NewParameter("platform", smartcontract.Hash160Type)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioCommitmentCreatedEvent, - manifest.NewParameter("commitmentID", smartcontract.IntegerType), - manifest.NewParameter("opportunityID", smartcontract.IntegerType), - manifest.NewParameter("investor", smartcontract.Hash160Type)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioCommitmentRevealedEvent, - manifest.NewParameter("commitmentID", smartcontract.IntegerType), - manifest.NewParameter("investmentID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(collocatioCommitmentCanceledEvent, - manifest.NewParameter("commitmentID", smartcontract.IntegerType)) - c.AddEvent(NewEvent(eDesc)) - - return c -} - -// Metadata returns contract metadata. -func (c *Collocatio) Metadata() *interop.ContractMD { - return &c.ContractMD -} - -// Initialize initializes Collocatio contract. -func (c *Collocatio) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != c.ActiveIn() { - return nil - } - - // Initialize default config - cfg := state.CollocatioConfig{ - MinPIOParticipants: defaultMinPIOParticipants, - MinEIOParticipants: defaultMinEIOParticipants, - MinCIOParticipants: defaultMinCIOParticipants, - DefaultMinInvestment: defaultMinInvestment, - MaxIndividualCap: defaultMaxIndividualCap, - WealthConcentration: defaultWealthConcentration, - CreationFee: defaultCreationFee, - InvestmentFee: defaultInvestmentFee, - WithdrawalPenalty: defaultWithdrawalPenalty, - MinVotingPeriod: defaultMinVotingPeriod, - MinInvestmentPeriod: defaultMinInvestmentPeriod, - MinMaturityPeriod: defaultMinMaturityPeriod, - MaxViolationsBeforeBan: defaultMaxViolationsBeforeBan, - ViolationCooldown: 1000000, - CommitRevealDelay: defaultCommitRevealDelay, - CommitRevealWindow: defaultCommitRevealWindow, - } - c.setConfigInternal(ic.DAO, &cfg) - - return nil -} - -// InitializeCache fills native Collocatio cache from DAO. -func (c *Collocatio) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - return nil -} - -// OnPersist implements the Contract interface. -func (c *Collocatio) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -// Handles lifecycle automation for opportunities. -func (c *Collocatio) PostPersist(ic *interop.Context) error { - // Run every 100 blocks for performance - if ic.Block.Index%100 != 0 { - return nil - } - - // Process opportunities that need status updates - c.processActiveOpportunities(ic) - c.processClosedOpportunities(ic) - return nil -} - -// processActiveOpportunities handles Active opportunities past their investment deadline. -func (c *Collocatio) processActiveOpportunities(ic *interop.Context) { - prefix := []byte{collocatioPrefixOpportunityByStatus, byte(state.OpportunityActive)} - cfg := c.getConfigInternal(ic.DAO) - - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 9 { - return true - } - - oppID := binary.BigEndian.Uint64(k[1:9]) - opp := c.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - return true - } - - // Check if investment deadline passed - if ic.Block.Index < opp.InvestmentDeadline { - return true - } - - // Get minimum participants for this opportunity type - var minParticipants uint64 - switch opp.Type { - case state.OpportunityPIO: - minParticipants = cfg.MinPIOParticipants - case state.OpportunityEIO: - minParticipants = cfg.MinEIOParticipants - case state.OpportunityCIO: - minParticipants = cfg.MinCIOParticipants - default: - minParticipants = 1 - } - - // Use opportunity's own min if set - if opp.MinParticipants > minParticipants { - minParticipants = opp.MinParticipants - } - - // Check if opportunity met minimum participants - if opp.CurrentParticipants < minParticipants { - // Failed - didn't meet minimum participants - c.updateOpportunityStatus(ic, opp, state.OpportunityFailed) - ic.AddNotification(c.Hash, collocatioOpportunityFailedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), - stackitem.NewByteArray([]byte("insufficient participants")), - })) - } else { - // Success - close and move to maturity phase - c.updateOpportunityStatus(ic, opp, state.OpportunityClosed) - ic.AddNotification(c.Hash, collocatioOpportunityClosedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), - })) - } - return true - }) -} - -// processClosedOpportunities handles Closed opportunities past their maturity date. -func (c *Collocatio) processClosedOpportunities(ic *interop.Context) { - prefix := []byte{collocatioPrefixOpportunityByStatus, byte(state.OpportunityClosed)} - - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 9 { - return true - } - - oppID := binary.BigEndian.Uint64(k[1:9]) - opp := c.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - return true - } - - // Check if maturity date passed - if ic.Block.Index < opp.MaturityDate { - return true - } - - // Opportunity is mature and ready for returns distribution - // Note: Actual distribution is triggered by distributeReturns call - // This could emit a notification for off-chain systems - // For now, we just log that it's ready (no state change needed) - return true - }) -} - -// updateOpportunityStatus updates an opportunity's status and maintains status index. -func (c *Collocatio) updateOpportunityStatus(ic *interop.Context, opp *state.InvestmentOpportunity, newStatus state.OpportunityStatus) { - oldStatus := opp.Status - - // Remove from old status index - oldStatusKey := makeCollocatioOppByStatusKey(oldStatus, opp.ID) - ic.DAO.DeleteStorageItem(c.ID, oldStatusKey) - - // Update status - opp.Status = newStatus - opp.UpdatedAt = ic.Block.Index - - // Add to new status index - newStatusKey := makeCollocatioOppByStatusKey(newStatus, opp.ID) - ic.DAO.PutStorageItem(c.ID, newStatusKey, []byte{1}) - - // Save opportunity - c.putOpportunity(ic.DAO, opp) -} - -// ActiveIn returns nil (always active from genesis). -func (c *Collocatio) ActiveIn() *config.Hardfork { - return nil -} - -// ============================================================================ -// Storage Key Helpers -// ============================================================================ - -func makeCollocatioConfigKey() []byte { - return []byte{collocatioPrefixConfig} -} - -func makeCollocatioOppKey(oppID uint64) []byte { - key := make([]byte, 9) - key[0] = collocatioPrefixOpportunity - binary.BigEndian.PutUint64(key[1:], oppID) - return key -} - -func makeCollocatioOppByTypeKey(oppType state.OpportunityType, oppID uint64) []byte { - key := make([]byte, 10) - key[0] = collocatioPrefixOpportunityByType - key[1] = byte(oppType) - binary.BigEndian.PutUint64(key[2:], oppID) - return key -} - -func makeCollocatioOppByStatusKey(status state.OpportunityStatus, oppID uint64) []byte { - key := make([]byte, 10) - key[0] = collocatioPrefixOpportunityByStatus - key[1] = byte(status) - binary.BigEndian.PutUint64(key[2:], oppID) - return key -} - -func makeCollocatioOppCounterKey() []byte { - return []byte{collocatioPrefixOppCounter} -} - -func makeCollocatioInvKey(invID uint64) []byte { - key := make([]byte, 9) - key[0] = collocatioPrefixInvestment - binary.BigEndian.PutUint64(key[1:], invID) - return key -} - -func makeCollocatioInvByOppKey(oppID, invID uint64) []byte { - key := make([]byte, 17) - key[0] = collocatioPrefixInvestmentByOpp - binary.BigEndian.PutUint64(key[1:9], oppID) - binary.BigEndian.PutUint64(key[9:], invID) - return key -} - -func makeCollocatioInvByInvestorKey(vitaID, invID uint64) []byte { - key := make([]byte, 17) - key[0] = collocatioPrefixInvestmentByInvestor - binary.BigEndian.PutUint64(key[1:9], vitaID) - binary.BigEndian.PutUint64(key[9:], invID) - return key -} - -func makeCollocatioInvCounterKey() []byte { - return []byte{collocatioPrefixInvCounter} -} - -func makeCollocatioEligKey(vitaID uint64) []byte { - key := make([]byte, 9) - key[0] = collocatioPrefixEligibility - binary.BigEndian.PutUint64(key[1:], vitaID) - return key -} - -func makeCollocatioEligByOwnerKey(owner util.Uint160) []byte { - key := make([]byte, 1+util.Uint160Size) - key[0] = collocatioPrefixEligibilityByOwner - copy(key[1:], owner.BytesBE()) - return key -} - -func makeCollocatioViolationKey(violationID uint64) []byte { - key := make([]byte, 9) - key[0] = collocatioPrefixViolation - binary.BigEndian.PutUint64(key[1:], violationID) - return key -} - -func makeCollocatioViolationByInvestorKey(vitaID, violationID uint64) []byte { - key := make([]byte, 17) - key[0] = collocatioPrefixViolationByInvestor - binary.BigEndian.PutUint64(key[1:9], vitaID) - binary.BigEndian.PutUint64(key[9:], violationID) - return key -} - -func makeCollocatioViolationCounterKey() []byte { - return []byte{collocatioPrefixViolationCounter} -} - -func makeCollocatioEmploymentKey(employeeVitaID uint64) []byte { - key := make([]byte, 9) - key[0] = collocatioPrefixEmployment - binary.BigEndian.PutUint64(key[1:], employeeVitaID) - return key -} - -func makeCollocatioEmploymentByEmployerKey(employerVitaID, employeeVitaID uint64) []byte { - key := make([]byte, 17) - key[0] = collocatioPrefixEmploymentByEmployer - binary.BigEndian.PutUint64(key[1:9], employerVitaID) - binary.BigEndian.PutUint64(key[9:], employeeVitaID) - return key -} - -func makeCollocatioContractorKey(contractorVitaID uint64) []byte { - key := make([]byte, 9) - key[0] = collocatioPrefixContractor - binary.BigEndian.PutUint64(key[1:], contractorVitaID) - return key -} - -func makeCollocatioContractorByPlatformKey(platform util.Uint160, contractorVitaID uint64) []byte { - key := make([]byte, 1+util.Uint160Size+8) - key[0] = collocatioPrefixContractorByPlatform - copy(key[1:1+util.Uint160Size], platform.BytesBE()) - binary.BigEndian.PutUint64(key[1+util.Uint160Size:], contractorVitaID) - return key -} - -func makeCollocatioCommitmentKey(commitmentID uint64) []byte { - key := make([]byte, 9) - key[0] = collocatioPrefixCommitment - binary.BigEndian.PutUint64(key[1:], commitmentID) - return key -} - -func makeCollocatioCommitmentByOppKey(oppID, commitmentID uint64) []byte { - key := make([]byte, 17) - key[0] = collocatioPrefixCommitmentByOpp - binary.BigEndian.PutUint64(key[1:9], oppID) - binary.BigEndian.PutUint64(key[9:], commitmentID) - return key -} - -func makeCollocatioCommitmentByInvestorKey(vitaID, commitmentID uint64) []byte { - key := make([]byte, 17) - key[0] = collocatioPrefixCommitmentByInvestor - binary.BigEndian.PutUint64(key[1:9], vitaID) - binary.BigEndian.PutUint64(key[9:], commitmentID) - return key -} - -func makeCollocatioCommitmentCounterKey() []byte { - return []byte{collocatioPrefixCommitmentCounter} -} - -// ============================================================================ -// Internal Storage Methods -// ============================================================================ - -func (c *Collocatio) getConfigInternal(d *dao.Simple) *state.CollocatioConfig { - si := d.GetStorageItem(c.ID, makeCollocatioConfigKey()) - if si == nil { - return &state.CollocatioConfig{ - MinPIOParticipants: defaultMinPIOParticipants, - MinEIOParticipants: defaultMinEIOParticipants, - MinCIOParticipants: defaultMinCIOParticipants, - DefaultMinInvestment: defaultMinInvestment, - MaxIndividualCap: defaultMaxIndividualCap, - WealthConcentration: defaultWealthConcentration, - CreationFee: defaultCreationFee, - InvestmentFee: defaultInvestmentFee, - WithdrawalPenalty: defaultWithdrawalPenalty, - MinVotingPeriod: defaultMinVotingPeriod, - MinInvestmentPeriod: defaultMinInvestmentPeriod, - MinMaturityPeriod: defaultMinMaturityPeriod, - MaxViolationsBeforeBan: defaultMaxViolationsBeforeBan, - ViolationCooldown: 1000000, - } - } - cfg := new(state.CollocatioConfig) - item, _ := stackitem.Deserialize(si) - cfg.FromStackItem(item) - return cfg -} - -func (c *Collocatio) setConfigInternal(d *dao.Simple, cfg *state.CollocatioConfig) { - item, _ := cfg.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioConfigKey(), data) -} - -func (c *Collocatio) getCounter(d *dao.Simple, key []byte) uint64 { - si := d.GetStorageItem(c.ID, key) - if si == nil || len(si) < 8 { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (c *Collocatio) incrementCounter(d *dao.Simple, key []byte) uint64 { - current := c.getCounter(d, key) - next := current + 1 - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, next) - d.PutStorageItem(c.ID, key, buf) - return next -} - -func (c *Collocatio) getOpportunityInternal(d *dao.Simple, oppID uint64) *state.InvestmentOpportunity { - si := d.GetStorageItem(c.ID, makeCollocatioOppKey(oppID)) - if si == nil { - return nil - } - opp := new(state.InvestmentOpportunity) - item, _ := stackitem.Deserialize(si) - opp.FromStackItem(item) - return opp -} - -func (c *Collocatio) putOpportunity(d *dao.Simple, opp *state.InvestmentOpportunity) { - item, _ := opp.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioOppKey(opp.ID), data) -} - -func (c *Collocatio) getInvestmentInternal(d *dao.Simple, invID uint64) *state.Investment { - si := d.GetStorageItem(c.ID, makeCollocatioInvKey(invID)) - if si == nil { - return nil - } - inv := new(state.Investment) - item, _ := stackitem.Deserialize(si) - inv.FromStackItem(item) - return inv -} - -func (c *Collocatio) putInvestment(d *dao.Simple, inv *state.Investment) { - item, _ := inv.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioInvKey(inv.ID), data) -} - -func (c *Collocatio) getEligibilityInternal(d *dao.Simple, investor util.Uint160) *state.InvestorEligibility { - // First get vitaID from owner mapping - si := d.GetStorageItem(c.ID, makeCollocatioEligByOwnerKey(investor)) - if si == nil || len(si) < 8 { - return nil - } - vitaID := binary.BigEndian.Uint64(si) - - // Then get eligibility - eligSI := d.GetStorageItem(c.ID, makeCollocatioEligKey(vitaID)) - if eligSI == nil { - return nil - } - elig := new(state.InvestorEligibility) - item, _ := stackitem.Deserialize(eligSI) - elig.FromStackItem(item) - return elig -} - -func (c *Collocatio) putEligibility(d *dao.Simple, elig *state.InvestorEligibility) { - item, _ := elig.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioEligKey(elig.VitaID), data) - - // Also store owner -> vitaID mapping - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, elig.VitaID) - d.PutStorageItem(c.ID, makeCollocatioEligByOwnerKey(elig.Investor), buf) -} - -func (c *Collocatio) getCommitmentInternal(d *dao.Simple, commitmentID uint64) *state.InvestmentCommitment { - si := d.GetStorageItem(c.ID, makeCollocatioCommitmentKey(commitmentID)) - if si == nil { - return nil - } - commitment := new(state.InvestmentCommitment) - item, _ := stackitem.Deserialize(si) - commitment.FromStackItem(item) - return commitment -} - -func (c *Collocatio) putCommitment(d *dao.Simple, commitment *state.InvestmentCommitment) { - item, _ := commitment.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioCommitmentKey(commitment.ID), data) -} - -// getInvestorTotalInOpportunity returns the total amount an investor has invested in a specific opportunity. -func (c *Collocatio) getInvestorTotalInOpportunity(d *dao.Simple, vitaID, oppID uint64) uint64 { - prefix := []byte{collocatioPrefixInvestmentByInvestor} - prefix = append(prefix, make([]byte, 8)...) - binary.BigEndian.PutUint64(prefix[1:], vitaID) - - var total uint64 - d.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 16 { - invID := binary.BigEndian.Uint64(k[8:16]) - inv := c.getInvestmentInternal(d, invID) - if inv != nil && inv.OpportunityID == oppID && inv.Status == state.InvestmentActive { - total += inv.Amount - } - } - return true - }) - return total -} - -// ============================================================================ -// Contract Methods -// ============================================================================ - -func (c *Collocatio) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - cfg := c.getConfigInternal(ic.DAO) - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MinPIOParticipants)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MinEIOParticipants)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MinCIOParticipants)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.DefaultMinInvestment)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MaxIndividualCap)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.WealthConcentration)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.CreationFee)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.InvestmentFee)), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.WithdrawalPenalty)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MinVotingPeriod))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MinInvestmentPeriod))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MinMaturityPeriod))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MaxViolationsBeforeBan))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.ViolationCooldown))), - }) -} - -func (c *Collocatio) getOpportunityCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - count := c.getCounter(ic.DAO, makeCollocatioOppCounterKey()) - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} - -func (c *Collocatio) getOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppID := toUint64(args[0]) - opp := c.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - return stackitem.Null{} - } - return opportunityToStackItem(opp) -} - -func (c *Collocatio) createOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { - caller := ic.VM.GetCallingScriptHash() - - oppType := state.OpportunityType(toUint64(args[0])) - name := toString(args[1]) - description := toString(args[2]) - termsHashBytes, err := args[3].TryBytes() - if err != nil { - panic(err) - } - termsHash, err := util.Uint256DecodeBytesBE(termsHashBytes) - if err != nil { - panic(err) - } - minParticipants := toUint64(args[4]) - maxParticipants := toUint64(args[5]) - minInvestment := toUint64(args[6]) - maxInvestment := toUint64(args[7]) - targetPool := toUint64(args[8]) - expectedReturns := toUint64(args[9]) - riskLevel := uint8(toUint64(args[10])) - maturityBlocks := uint32(toUint64(args[11])) - - // Validate caller has Vita - vita, err := c.Vita.GetTokenByOwner(ic.DAO, caller) - if err != nil { - panic("caller must have Vita token") - } - vitaID := vita.TokenID - - // Validate opportunity type - if oppType > state.OpportunityCIO { - panic("invalid opportunity type") - } - - // Validate parameters - cfg := c.getConfigInternal(ic.DAO) - if maturityBlocks < cfg.MinMaturityPeriod { - panic("maturity period too short") - } - if riskLevel < 1 || riskLevel > 10 { - panic("risk level must be 1-10") - } - - // Get minimum participants based on type - var minRequired uint64 - switch oppType { - case state.OpportunityPIO: - minRequired = cfg.MinPIOParticipants - case state.OpportunityEIO: - minRequired = cfg.MinEIOParticipants - case state.OpportunityCIO: - minRequired = cfg.MinCIOParticipants - } - if minParticipants < minRequired { - panic(fmt.Sprintf("minimum participants must be at least %d for this type", minRequired)) - } - - // Charge creation fee to Treasury - if cfg.CreationFee > 0 { - if err := c.VTS.transferUnrestricted(ic, caller, nativehashes.Treasury, new(big.Int).SetUint64(cfg.CreationFee), nil); err != nil { - panic("failed to pay creation fee") - } - } - - // Create opportunity - oppID := c.incrementCounter(ic.DAO, makeCollocatioOppCounterKey()) - currentBlock := ic.Block.Index - - opp := &state.InvestmentOpportunity{ - ID: oppID, - Type: oppType, - Status: state.OpportunityDraft, - Creator: caller, - SponsorVitaID: vitaID, - Name: name, - Description: description, - TermsHash: termsHash, - MinParticipants: minParticipants, - MaxParticipants: maxParticipants, - CurrentParticipants: 0, - MinInvestment: minInvestment, - MaxInvestment: maxInvestment, - TotalPool: 0, - TargetPool: targetPool, - ExpectedReturns: expectedReturns, - RiskLevel: riskLevel, - VotingDeadline: currentBlock + cfg.MinVotingPeriod, - InvestmentDeadline: currentBlock + cfg.MinVotingPeriod + cfg.MinInvestmentPeriod, - MaturityDate: currentBlock + cfg.MinVotingPeriod + cfg.MinInvestmentPeriod + maturityBlocks, - ProposalID: 0, - CreatedAt: currentBlock, - UpdatedAt: currentBlock, - } - - c.putOpportunity(ic.DAO, opp) - - // Store type index - ic.DAO.PutStorageItem(c.ID, makeCollocatioOppByTypeKey(oppType, oppID), []byte{1}) - - // Emit event - ic.AddNotification(c.Hash, collocatioOpportunityCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(oppType))), - stackitem.NewByteArray(caller.BytesBE()), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)) -} - -func (c *Collocatio) activateOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppID := toUint64(args[0]) - - opp := c.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - panic("opportunity not found") - } - - caller := ic.VM.GetCallingScriptHash() - if caller != opp.Creator && !c.Tutus.CheckCommittee(ic) { - panic("only creator or committee can activate") - } - - if opp.Status != state.OpportunityDraft { - panic("opportunity must be in draft status") - } - - opp.Status = state.OpportunityActive - opp.UpdatedAt = ic.Block.Index - c.putOpportunity(ic.DAO, opp) - - ic.AddNotification(c.Hash, collocatioOpportunityActivatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) invest(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppID := toUint64(args[0]) - amount := toUint64(args[1]) - caller := ic.VM.GetCallingScriptHash() - - // Validate caller has Vita - vita, err := c.Vita.GetTokenByOwner(ic.DAO, caller) - if err != nil { - panic("caller must have Vita token") - } - vitaID := vita.TokenID - - // Get opportunity - opp := c.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - panic("opportunity not found") - } - - if opp.Status != state.OpportunityActive { - panic("opportunity is not active") - } - if ic.Block.Index > opp.InvestmentDeadline { - panic("investment deadline has passed") - } - - if opp.MaxParticipants > 0 && opp.CurrentParticipants >= opp.MaxParticipants { - panic("maximum participants reached") - } - - if amount < opp.MinInvestment { - panic("investment below minimum") - } - if amount > opp.MaxInvestment { - panic("investment exceeds maximum") - } - - // Check eligibility - if !c.isEligibleInternal(ic.DAO, caller, opp.Type) { - panic("investor not eligible for this opportunity type") - } - - // Calculate fee - cfg := c.getConfigInternal(ic.DAO) - - // Whale concentration check - prevent any single investor from holding too much of the pool - if cfg.WealthConcentration > 0 { - existingInvestment := c.getInvestorTotalInOpportunity(ic.DAO, vitaID, oppID) - newTotal := existingInvestment + amount - // Calculate what percentage of the pool this investor would hold - // (newTotal / (opp.TotalPool + amount)) * 10000 > WealthConcentration - futurePool := opp.TotalPool + amount - if futurePool > 0 { - concentration := (newTotal * 10000) / futurePool - if concentration > cfg.WealthConcentration { - panic("investment would exceed whale concentration limit") - } - } - } - fee := (amount * cfg.InvestmentFee) / 10000 - netAmount := amount - fee - - // Transfer VTS from investor - if err := c.VTS.transferUnrestricted(ic, caller, c.Hash, new(big.Int).SetUint64(amount), nil); err != nil { - panic("failed to transfer investment amount") - } - - // Send fee to Treasury - if fee > 0 { - if err := c.VTS.transferUnrestricted(ic, c.Hash, nativehashes.Treasury, new(big.Int).SetUint64(fee), nil); err != nil { - panic("failed to transfer fee to treasury") - } - } - - // Create investment record - invID := c.incrementCounter(ic.DAO, makeCollocatioInvCounterKey()) - - inv := &state.Investment{ - ID: invID, - OpportunityID: oppID, - VitaID: vitaID, - Investor: caller, - Amount: netAmount, - Status: state.InvestmentActive, - ReturnAmount: 0, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - - c.putInvestment(ic.DAO, inv) - - // Store indexes - ic.DAO.PutStorageItem(c.ID, makeCollocatioInvByOppKey(oppID, invID), []byte{1}) - ic.DAO.PutStorageItem(c.ID, makeCollocatioInvByInvestorKey(vitaID, invID), []byte{1}) - - // Update opportunity - opp.CurrentParticipants++ - opp.TotalPool += netAmount - opp.UpdatedAt = ic.Block.Index - c.putOpportunity(ic.DAO, opp) - - // Update eligibility stats - c.updateEligibilityOnInvest(ic.DAO, caller, vitaID, netAmount, ic.Block.Index) - - // Emit event - ic.AddNotification(c.Hash, collocatioInvestmentMadeEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), - stackitem.NewByteArray(caller.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(netAmount)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(invID)) -} - -func (c *Collocatio) withdraw(ic *interop.Context, args []stackitem.Item) stackitem.Item { - invID := toUint64(args[0]) - caller := ic.VM.GetCallingScriptHash() - - inv := c.getInvestmentInternal(ic.DAO, invID) - if inv == nil { - panic("investment not found") - } - - if inv.Investor != caller { - panic("only investor can withdraw") - } - - if inv.Status != state.InvestmentActive { - panic("investment is not active") - } - - opp := c.getOpportunityInternal(ic.DAO, inv.OpportunityID) - if opp == nil { - panic("opportunity not found") - } - - // Calculate penalty for early withdrawal - cfg := c.getConfigInternal(ic.DAO) - returnAmount := inv.Amount - if ic.Block.Index < opp.MaturityDate && cfg.WithdrawalPenalty > 0 { - penalty := (inv.Amount * cfg.WithdrawalPenalty) / 10000 - returnAmount = inv.Amount - penalty - if penalty > 0 { - if err := c.VTS.transferUnrestricted(ic, c.Hash, nativehashes.Treasury, new(big.Int).SetUint64(penalty), nil); err != nil { - panic("failed to transfer penalty") - } - } - } - - // Return funds - if err := c.VTS.transferUnrestricted(ic, c.Hash, caller, new(big.Int).SetUint64(returnAmount), nil); err != nil { - panic("failed to return investment") - } - - // Update investment - inv.Status = state.InvestmentWithdrawn - inv.UpdatedAt = ic.Block.Index - c.putInvestment(ic.DAO, inv) - - // Update opportunity - opp.CurrentParticipants-- - opp.TotalPool -= inv.Amount - opp.UpdatedAt = ic.Block.Index - c.putOpportunity(ic.DAO, opp) - - // Update eligibility - c.updateEligibilityOnWithdraw(ic.DAO, caller, inv.Amount, ic.Block.Index) - - // Emit event - ic.AddNotification(c.Hash, collocatioInvestmentWithdrawnEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(returnAmount)), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) getEligibility(ic *interop.Context, args []stackitem.Item) stackitem.Item { - investor := toUint160(args[0]) - elig := c.getEligibilityInternal(ic.DAO, investor) - if elig == nil { - return stackitem.Null{} - } - return eligibilityToStackItem(elig) -} - -func (c *Collocatio) setEligibility(ic *interop.Context, args []stackitem.Item) stackitem.Item { - investor := toUint160(args[0]) - eligFlags := state.EligibilityType(toUint64(args[1])) - - // Only committee or RoleInvestmentManager can set eligibility - if !c.Tutus.CheckCommittee(ic) { - caller := ic.VM.GetCallingScriptHash() - if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { - panic("only committee or investment manager can set eligibility") - } - } - - vita, err := c.Vita.GetTokenByOwner(ic.DAO, investor) - if err != nil { - panic("investor must have Vita token") - } - vitaID := vita.TokenID - - elig := c.getEligibilityInternal(ic.DAO, investor) - if elig == nil { - elig = &state.InvestorEligibility{ - VitaID: vitaID, - Investor: investor, - CreatedAt: ic.Block.Index, - } - } - - elig.Eligibility = eligFlags - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - - ic.AddNotification(c.Hash, collocatioEligibilityUpdatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(investor.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(eligFlags))), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) isEligible(ic *interop.Context, args []stackitem.Item) stackitem.Item { - investor := toUint160(args[0]) - oppType := state.OpportunityType(toUint64(args[1])) - return stackitem.NewBool(c.isEligibleInternal(ic.DAO, investor, oppType)) -} - -func (c *Collocatio) isEligibleInternal(d *dao.Simple, investor util.Uint160, oppType state.OpportunityType) bool { - elig := c.getEligibilityInternal(d, investor) - if elig == nil { - return false - } - - // Must have completed investment education - if !elig.ScireCompleted { - return false - } - - // Check for ban - if elig.HasViolations { - cfg := c.getConfigInternal(d) - if elig.ViolationCount >= cfg.MaxViolationsBeforeBan { - return false - } - } - - switch oppType { - case state.OpportunityPIO: - return elig.Eligibility&state.EligibilityPIO != 0 - case state.OpportunityEIO: - if elig.Eligibility&state.EligibilityEIO == 0 { - return false - } - // Additionally verify active employment - return c.hasActiveEmployment(d, elig.VitaID) - case state.OpportunityCIO: - if elig.Eligibility&state.EligibilityCIO == 0 { - return false - } - // Additionally verify active contractor status - return c.hasActiveContractor(d, elig.VitaID) - default: - return false - } -} - -func (c *Collocatio) getInvestment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - invID := toUint64(args[0]) - inv := c.getInvestmentInternal(ic.DAO, invID) - if inv == nil { - return stackitem.Null{} - } - return investmentToStackItem(inv) -} - -func (c *Collocatio) getInvestmentCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - count := c.getCounter(ic.DAO, makeCollocatioInvCounterKey()) - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} - -// ============================================================================ -// Internal Helpers -// ============================================================================ - -func (c *Collocatio) updateEligibilityOnInvest(d *dao.Simple, investor util.Uint160, vitaID, amount uint64, blockHeight uint32) { - elig := c.getEligibilityInternal(d, investor) - if elig == nil { - elig = &state.InvestorEligibility{ - VitaID: vitaID, - Investor: investor, - CreatedAt: blockHeight, - } - } - elig.TotalInvested += amount - elig.ActiveInvestments++ - elig.LastActivity = blockHeight - elig.UpdatedAt = blockHeight - c.putEligibility(d, elig) -} - -func (c *Collocatio) updateEligibilityOnWithdraw(d *dao.Simple, investor util.Uint160, amount uint64, blockHeight uint32) { - elig := c.getEligibilityInternal(d, investor) - if elig == nil { - return - } - if elig.TotalInvested >= amount { - elig.TotalInvested -= amount - } - if elig.ActiveInvestments > 0 { - elig.ActiveInvestments-- - } - elig.LastActivity = blockHeight - elig.UpdatedAt = blockHeight - c.putEligibility(d, elig) -} - -// ============================================================================ -// Violation System -// ============================================================================ - -func (c *Collocatio) getViolationInternal(d *dao.Simple, violationID uint64) *state.InvestmentViolation { - si := d.GetStorageItem(c.ID, makeCollocatioViolationKey(violationID)) - if si == nil { - return nil - } - v := new(state.InvestmentViolation) - item, _ := stackitem.Deserialize(si) - v.FromStackItem(item) - return v -} - -func (c *Collocatio) putViolation(d *dao.Simple, v *state.InvestmentViolation) { - item, _ := v.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioViolationKey(v.ID), data) -} - -func (c *Collocatio) recordViolation(ic *interop.Context, args []stackitem.Item) stackitem.Item { - violator := toUint160(args[0]) - opportunityID := toUint64(args[1]) - violationType := toString(args[2]) - description := toString(args[3]) - evidenceHashBytes, err := args[4].TryBytes() - if err != nil { - panic(err) - } - evidenceHash, err := util.Uint256DecodeBytesBE(evidenceHashBytes) - if err != nil { - panic(err) - } - penalty := toUint64(args[5]) - - // Authorization: Committee or RoleInvestmentManager - if !c.Tutus.CheckCommittee(ic) { - caller := ic.VM.GetCallingScriptHash() - if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { - panic("only committee or investment manager can record violations") - } - } - - // Verify violator has Vita - vita, err := c.Vita.GetTokenByOwner(ic.DAO, violator) - if err != nil || vita == nil { - panic("violator must have active Vita") - } - vitaID := vita.TokenID - - // Create violation record - violationID := c.incrementCounter(ic.DAO, makeCollocatioViolationCounterKey()) - caller := ic.VM.GetCallingScriptHash() - - v := &state.InvestmentViolation{ - ID: violationID, - VitaID: vitaID, - Violator: violator, - OpportunityID: opportunityID, - ViolationType: violationType, - Description: description, - EvidenceHash: evidenceHash, - Penalty: penalty, - ReportedBy: caller, - ReportedAt: ic.Block.Index, - ResolvedAt: 0, - Resolution: "", - } - - c.putViolation(ic.DAO, v) - - // Store index by investor - ic.DAO.PutStorageItem(c.ID, makeCollocatioViolationByInvestorKey(vitaID, violationID), []byte{1}) - - // Update eligibility - elig := c.getEligibilityInternal(ic.DAO, violator) - if elig == nil { - elig = &state.InvestorEligibility{ - VitaID: vitaID, - Investor: violator, - CreatedAt: ic.Block.Index, - } - } - elig.HasViolations = true - elig.ViolationCount++ - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - - // Apply penalty if specified - if penalty > 0 { - if err := c.VTS.transferUnrestricted(ic, violator, nativehashes.Treasury, new(big.Int).SetUint64(penalty), nil); err != nil { - // Don't panic if transfer fails, just record violation without penalty - } - } - - // Emit event - ic.AddNotification(c.Hash, collocatioViolationRecordedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(violationID)), - stackitem.NewByteArray(violator.BytesBE()), - stackitem.NewByteArray([]byte(violationType)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(violationID)) -} - -func (c *Collocatio) resolveViolation(ic *interop.Context, args []stackitem.Item) stackitem.Item { - violationID := toUint64(args[0]) - resolution := toString(args[1]) - - // Authorization: Committee or RoleInvestmentManager - if !c.Tutus.CheckCommittee(ic) { - caller := ic.VM.GetCallingScriptHash() - if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { - panic("only committee or investment manager can resolve violations") - } - } - - v := c.getViolationInternal(ic.DAO, violationID) - if v == nil { - panic("violation not found") - } - if v.ResolvedAt != 0 { - panic("violation already resolved") - } - - v.ResolvedAt = ic.Block.Index - v.Resolution = resolution - c.putViolation(ic.DAO, v) - - // Emit event - ic.AddNotification(c.Hash, collocatioViolationResolvedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(violationID)), - stackitem.NewByteArray([]byte(resolution)), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) getViolation(ic *interop.Context, args []stackitem.Item) stackitem.Item { - violationID := toUint64(args[0]) - v := c.getViolationInternal(ic.DAO, violationID) - if v == nil { - return stackitem.Null{} - } - return violationToStackItem(v) -} - -// ============================================================================ -// Employment Verification (EIO) -// ============================================================================ - -func (c *Collocatio) getEmploymentInternal(d *dao.Simple, employeeVitaID uint64) *state.EmploymentVerification { - si := d.GetStorageItem(c.ID, makeCollocatioEmploymentKey(employeeVitaID)) - if si == nil { - return nil - } - ev := new(state.EmploymentVerification) - item, _ := stackitem.Deserialize(si) - ev.FromStackItem(item) - return ev -} - -func (c *Collocatio) putEmployment(d *dao.Simple, ev *state.EmploymentVerification) { - item, _ := ev.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioEmploymentKey(ev.VitaID), data) -} - -func (c *Collocatio) verifyEmployment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - employee := toUint160(args[0]) - employer := toUint160(args[1]) - position := toString(args[2]) - startDate := uint32(toUint64(args[3])) - - caller := ic.VM.GetCallingScriptHash() - - // Authorization: Committee, RoleInvestmentManager, or employer - isAuthorized := c.Tutus.CheckCommittee(ic) || - c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || - caller == employer - - if !isAuthorized { - panic("only committee, investment manager, or employer can verify employment") - } - - // Verify both employee and employer have Vita - employeeVita, err := c.Vita.GetTokenByOwner(ic.DAO, employee) - if err != nil { - panic("employee must have Vita token") - } - employerVita, err := c.Vita.GetTokenByOwner(ic.DAO, employer) - if err != nil { - panic("employer must have Vita token") - } - - ev := &state.EmploymentVerification{ - VitaID: employeeVita.TokenID, - Employee: employee, - EmployerVitaID: employerVita.TokenID, - Employer: employer, - Position: position, - StartDate: startDate, - EndDate: 0, - IsActive: true, - VerifiedAt: ic.Block.Index, - VerifiedBy: caller, - } - - c.putEmployment(ic.DAO, ev) - - // Store index by employer - ic.DAO.PutStorageItem(c.ID, makeCollocatioEmploymentByEmployerKey(employerVita.TokenID, employeeVita.TokenID), []byte{1}) - - // Update eligibility - elig := c.getEligibilityInternal(ic.DAO, employee) - if elig == nil { - elig = &state.InvestorEligibility{ - VitaID: employeeVita.TokenID, - Investor: employee, - CreatedAt: ic.Block.Index, - } - } - elig.Eligibility |= state.EligibilityEIO - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - - // Emit event - ic.AddNotification(c.Hash, collocatioEmploymentVerifiedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(employee.BytesBE()), - stackitem.NewByteArray(employer.BytesBE()), - stackitem.NewByteArray([]byte(position)), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) revokeEmployment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - employee := toUint160(args[0]) - employer := toUint160(args[1]) - endDate := uint32(toUint64(args[2])) - - caller := ic.VM.GetCallingScriptHash() - - // Authorization: Committee, RoleInvestmentManager, or employer - isAuthorized := c.Tutus.CheckCommittee(ic) || - c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || - caller == employer - - if !isAuthorized { - panic("only committee, investment manager, or employer can revoke employment") - } - - // Get employee Vita - employeeVita, err := c.Vita.GetTokenByOwner(ic.DAO, employee) - if err != nil { - panic("employee must have Vita token") - } - - ev := c.getEmploymentInternal(ic.DAO, employeeVita.TokenID) - if ev == nil { - panic("employment not found") - } - if !ev.IsActive { - panic("employment already revoked") - } - if ev.Employer != employer { - panic("employer mismatch") - } - - ev.IsActive = false - ev.EndDate = endDate - c.putEmployment(ic.DAO, ev) - - // Remove EIO eligibility - elig := c.getEligibilityInternal(ic.DAO, employee) - if elig != nil { - elig.Eligibility &^= state.EligibilityEIO - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - } - - // Emit event - ic.AddNotification(c.Hash, collocatioEmploymentRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(employee.BytesBE()), - stackitem.NewByteArray(employer.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) getEmploymentStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { - employee := toUint160(args[0]) - - vita, err := c.Vita.GetTokenByOwner(ic.DAO, employee) - if err != nil { - return stackitem.Null{} - } - - ev := c.getEmploymentInternal(ic.DAO, vita.TokenID) - if ev == nil { - return stackitem.Null{} - } - return employmentToStackItem(ev) -} - -func (c *Collocatio) hasActiveEmployment(d *dao.Simple, vitaID uint64) bool { - ev := c.getEmploymentInternal(d, vitaID) - return ev != nil && ev.IsActive -} - -// ============================================================================ -// Contractor Verification (CIO) -// ============================================================================ - -func (c *Collocatio) getContractorInternal(d *dao.Simple, contractorVitaID uint64) *state.ContractorVerification { - si := d.GetStorageItem(c.ID, makeCollocatioContractorKey(contractorVitaID)) - if si == nil { - return nil - } - cv := new(state.ContractorVerification) - item, _ := stackitem.Deserialize(si) - cv.FromStackItem(item) - return cv -} - -func (c *Collocatio) putContractor(d *dao.Simple, cv *state.ContractorVerification) { - item, _ := cv.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(c.ID, makeCollocatioContractorKey(cv.VitaID), data) -} - -func (c *Collocatio) verifyContractor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - contractor := toUint160(args[0]) - platform := toUint160(args[1]) - platformID := toString(args[2]) - contractorID := toString(args[3]) - startDate := uint32(toUint64(args[4])) - - caller := ic.VM.GetCallingScriptHash() - - // Authorization: Committee, RoleInvestmentManager, or platform - isAuthorized := c.Tutus.CheckCommittee(ic) || - c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || - caller == platform - - if !isAuthorized { - panic("only committee, investment manager, or platform can verify contractor") - } - - // Verify contractor has Vita - contractorVita, err := c.Vita.GetTokenByOwner(ic.DAO, contractor) - if err != nil { - panic("contractor must have Vita token") - } - - cv := &state.ContractorVerification{ - VitaID: contractorVita.TokenID, - Contractor: contractor, - PlatformID: platformID, - Platform: platform, - ContractorID: contractorID, - StartDate: startDate, - EndDate: 0, - IsActive: true, - VerifiedAt: ic.Block.Index, - VerifiedBy: caller, - } - - c.putContractor(ic.DAO, cv) - - // Store index by platform - ic.DAO.PutStorageItem(c.ID, makeCollocatioContractorByPlatformKey(platform, contractorVita.TokenID), []byte{1}) - - // Update eligibility - elig := c.getEligibilityInternal(ic.DAO, contractor) - if elig == nil { - elig = &state.InvestorEligibility{ - VitaID: contractorVita.TokenID, - Investor: contractor, - CreatedAt: ic.Block.Index, - } - } - elig.Eligibility |= state.EligibilityCIO - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - - // Emit event - ic.AddNotification(c.Hash, collocatioContractorVerifiedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(contractor.BytesBE()), - stackitem.NewByteArray(platform.BytesBE()), - stackitem.NewByteArray([]byte(platformID)), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) revokeContractor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - contractor := toUint160(args[0]) - platform := toUint160(args[1]) - endDate := uint32(toUint64(args[2])) - - caller := ic.VM.GetCallingScriptHash() - - // Authorization: Committee, RoleInvestmentManager, or platform - isAuthorized := c.Tutus.CheckCommittee(ic) || - c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || - caller == platform - - if !isAuthorized { - panic("only committee, investment manager, or platform can revoke contractor") - } - - // Get contractor Vita - contractorVita, err := c.Vita.GetTokenByOwner(ic.DAO, contractor) - if err != nil { - panic("contractor must have Vita token") - } - - cv := c.getContractorInternal(ic.DAO, contractorVita.TokenID) - if cv == nil { - panic("contractor not found") - } - if !cv.IsActive { - panic("contractor already revoked") - } - if cv.Platform != platform { - panic("platform mismatch") - } - - cv.IsActive = false - cv.EndDate = endDate - c.putContractor(ic.DAO, cv) - - // Remove CIO eligibility - elig := c.getEligibilityInternal(ic.DAO, contractor) - if elig != nil { - elig.Eligibility &^= state.EligibilityCIO - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - } - - // Emit event - ic.AddNotification(c.Hash, collocatioContractorRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(contractor.BytesBE()), - stackitem.NewByteArray(platform.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) getContractorStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { - contractor := toUint160(args[0]) - - vita, err := c.Vita.GetTokenByOwner(ic.DAO, contractor) - if err != nil { - return stackitem.Null{} - } - - cv := c.getContractorInternal(ic.DAO, vita.TokenID) - if cv == nil { - return stackitem.Null{} - } - return contractorToStackItem(cv) -} - -func (c *Collocatio) hasActiveContractor(d *dao.Simple, vitaID uint64) bool { - cv := c.getContractorInternal(d, vitaID) - return cv != nil && cv.IsActive -} - -// ============================================================================ -// Education Completion -// ============================================================================ - -func (c *Collocatio) completeInvestmentEducation(ic *interop.Context, args []stackitem.Item) stackitem.Item { - investor := toUint160(args[0]) - - caller := ic.VM.GetCallingScriptHash() - - // Authorization: Committee or RoleInvestmentManager - if !c.Tutus.CheckCommittee(ic) { - if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { - panic("only committee or investment manager can complete education") - } - } - - // Verify investor has Vita - vita, err := c.Vita.GetTokenByOwner(ic.DAO, investor) - if err != nil { - panic("investor must have Vita token") - } - - // Update eligibility - elig := c.getEligibilityInternal(ic.DAO, investor) - if elig == nil { - elig = &state.InvestorEligibility{ - VitaID: vita.TokenID, - Investor: investor, - CreatedAt: ic.Block.Index, - } - } - elig.ScireCompleted = true - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - - // Emit event - ic.AddNotification(c.Hash, collocatioEducationCompletedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(investor.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -// ============================================================================ -// Returns Distribution & Cancellation -// ============================================================================ - -func (c *Collocatio) distributeReturns(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppID := toUint64(args[0]) - actualReturns := toUint64(args[1]) - - // Authorization: Committee or RoleInvestmentManager - if !c.Tutus.CheckCommittee(ic) { - caller := ic.VM.GetCallingScriptHash() - if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { - panic("only committee or investment manager can distribute returns") - } - } - - opp := c.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - panic("opportunity not found") - } - if opp.Status != state.OpportunityClosed { - panic("opportunity must be in closed status") - } - if ic.Block.Index < opp.MaturityDate { - panic("maturity date not reached") - } - if opp.TotalPool == 0 { - panic("no investments to distribute") - } - - // Collect all investment IDs first (to avoid modifying during iteration) - prefix := []byte{collocatioPrefixInvestmentByOpp} - prefix = append(prefix, make([]byte, 8)...) - binary.BigEndian.PutUint64(prefix[1:], oppID) - - var invIDs []uint64 - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 16 { - invID := binary.BigEndian.Uint64(k[8:16]) - invIDs = append(invIDs, invID) - } - return true - }) - - // Process each investment - for _, invID := range invIDs { - inv := c.getInvestmentInternal(ic.DAO, invID) - if inv == nil || inv.Status != state.InvestmentActive { - continue - } - - // Calculate proportional return - returnAmount := (inv.Amount * actualReturns) / opp.TotalPool - - // Transfer return to investor - if returnAmount > 0 { - if err := c.VTS.transferUnrestricted(ic, c.Hash, inv.Investor, new(big.Int).SetUint64(returnAmount), nil); err != nil { - continue // Skip failed transfers - } - } - - // Also return principal - if inv.Amount > 0 { - if err := c.VTS.transferUnrestricted(ic, c.Hash, inv.Investor, new(big.Int).SetUint64(inv.Amount), nil); err != nil { - continue - } - } - - // Update investment - inv.ReturnAmount = returnAmount - inv.Status = state.InvestmentCompleted - inv.UpdatedAt = ic.Block.Index - c.putInvestment(ic.DAO, inv) - - // Update eligibility - elig := c.getEligibilityInternal(ic.DAO, inv.Investor) - if elig != nil { - elig.TotalReturns += returnAmount - elig.CompletedInvestments++ - if elig.ActiveInvestments > 0 { - elig.ActiveInvestments-- - } - if elig.TotalInvested >= inv.Amount { - elig.TotalInvested -= inv.Amount - } - elig.LastActivity = ic.Block.Index - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - } - - // Emit event for each distribution - ic.AddNotification(c.Hash, collocatioReturnsDistributedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), - stackitem.NewByteArray(inv.Investor.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(returnAmount)), - })) - } - - // Update opportunity status - opp.Status = state.OpportunityCompleted - opp.UpdatedAt = ic.Block.Index - c.putOpportunity(ic.DAO, opp) - - return stackitem.NewBool(true) -} - -func (c *Collocatio) cancelOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppID := toUint64(args[0]) - caller := ic.VM.GetCallingScriptHash() - - opp := c.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - panic("opportunity not found") - } - - // Authorization: Creator (if Draft/Voting) or Committee - isCreator := caller == opp.Creator - isCommittee := c.Tutus.CheckCommittee(ic) - - if opp.Status == state.OpportunityDraft || opp.Status == state.OpportunityVoting { - if !isCreator && !isCommittee { - panic("only creator or committee can cancel draft/voting opportunity") - } - } else if opp.Status == state.OpportunityActive { - if !isCommittee { - panic("only committee can cancel active opportunity") - } - } else { - panic("opportunity cannot be cancelled in current status") - } - - // Collect all investment IDs first (to avoid modifying during iteration) - prefix := []byte{collocatioPrefixInvestmentByOpp} - prefix = append(prefix, make([]byte, 8)...) - binary.BigEndian.PutUint64(prefix[1:], oppID) - - var invIDs []uint64 - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 16 { - invID := binary.BigEndian.Uint64(k[8:16]) - invIDs = append(invIDs, invID) - } - return true - }) - - // Process each investment - for _, invID := range invIDs { - inv := c.getInvestmentInternal(ic.DAO, invID) - if inv == nil || inv.Status != state.InvestmentActive { - continue - } - - // Refund full amount - if inv.Amount > 0 { - if err := c.VTS.transferUnrestricted(ic, c.Hash, inv.Investor, new(big.Int).SetUint64(inv.Amount), nil); err != nil { - continue - } - } - - // Update investment - inv.Status = state.InvestmentRefunded - inv.UpdatedAt = ic.Block.Index - c.putInvestment(ic.DAO, inv) - - // Update eligibility - elig := c.getEligibilityInternal(ic.DAO, inv.Investor) - if elig != nil { - if elig.ActiveInvestments > 0 { - elig.ActiveInvestments-- - } - if elig.TotalInvested >= inv.Amount { - elig.TotalInvested -= inv.Amount - } - elig.LastActivity = ic.Block.Index - elig.UpdatedAt = ic.Block.Index - c.putEligibility(ic.DAO, elig) - } - } - - // Update opportunity status - opp.Status = state.OpportunityCancelled - opp.UpdatedAt = ic.Block.Index - c.putOpportunity(ic.DAO, opp) - - // Emit event - ic.AddNotification(c.Hash, collocatioOpportunityCancelledEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), - })) - - return stackitem.NewBool(true) -} - -// ============================================================================ -// Query Methods -// ============================================================================ - -func (c *Collocatio) getInvestmentsByOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppID := toUint64(args[0]) - - prefix := []byte{collocatioPrefixInvestmentByOpp} - prefix = append(prefix, make([]byte, 8)...) - binary.BigEndian.PutUint64(prefix[1:], oppID) - - var ids []stackitem.Item - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 16 { - invID := binary.BigEndian.Uint64(k[8:16]) - ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(invID))) - } - return true - }) - - return stackitem.NewArray(ids) -} - -func (c *Collocatio) getInvestmentsByInvestor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - investor := toUint160(args[0]) - - vita, err := c.Vita.GetTokenByOwner(ic.DAO, investor) - if err != nil { - return stackitem.NewArray(nil) - } - - prefix := []byte{collocatioPrefixInvestmentByInvestor} - prefix = append(prefix, make([]byte, 8)...) - binary.BigEndian.PutUint64(prefix[1:], vita.TokenID) - - var ids []stackitem.Item - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 16 { - invID := binary.BigEndian.Uint64(k[8:16]) - ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(invID))) - } - return true - }) - - return stackitem.NewArray(ids) -} - -func (c *Collocatio) getOpportunitiesByType(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppType := state.OpportunityType(toUint64(args[0])) - - prefix := []byte{collocatioPrefixOpportunityByType, byte(oppType)} - - var ids []stackitem.Item - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 9 { - oppID := binary.BigEndian.Uint64(k[1:9]) - ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(oppID))) - } - return true - }) - - return stackitem.NewArray(ids) -} - -func (c *Collocatio) getOpportunitiesByStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { - status := state.OpportunityStatus(toUint64(args[0])) - - prefix := []byte{collocatioPrefixOpportunityByStatus, byte(status)} - - var ids []stackitem.Item - ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 9 { - oppID := binary.BigEndian.Uint64(k[1:9]) - ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(oppID))) - } - return true - }) - - return stackitem.NewArray(ids) -} - -// ============================================================================ -// Stack Item Converters -// ============================================================================ - -func opportunityToStackItem(opp *state.InvestmentOpportunity) stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.ID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.Type))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.Status))), - stackitem.NewByteArray(opp.Creator.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.SponsorVitaID)), - stackitem.NewByteArray([]byte(opp.Name)), - stackitem.NewByteArray([]byte(opp.Description)), - stackitem.NewByteArray(opp.TermsHash.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MinParticipants)), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MaxParticipants)), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.CurrentParticipants)), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MinInvestment)), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MaxInvestment)), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.TotalPool)), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.TargetPool)), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.ExpectedReturns)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.RiskLevel))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.VotingDeadline))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.InvestmentDeadline))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.MaturityDate))), - stackitem.NewBigInteger(new(big.Int).SetUint64(opp.ProposalID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.CreatedAt))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.UpdatedAt))), - }) -} - -func investmentToStackItem(inv *state.Investment) stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(inv.ID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(inv.OpportunityID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(inv.VitaID)), - stackitem.NewByteArray(inv.Investor.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(inv.Amount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(inv.Status))), - stackitem.NewBigInteger(new(big.Int).SetUint64(inv.ReturnAmount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(inv.CreatedAt))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(inv.UpdatedAt))), - }) -} - -func eligibilityToStackItem(elig *state.InvestorEligibility) stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(elig.VitaID)), - stackitem.NewByteArray(elig.Investor.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.Eligibility))), - stackitem.NewBool(elig.ScireCompleted), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.RiskScore))), - stackitem.NewBigInteger(new(big.Int).SetUint64(elig.TotalInvested)), - stackitem.NewBigInteger(new(big.Int).SetUint64(elig.TotalReturns)), - stackitem.NewBigInteger(new(big.Int).SetUint64(elig.ActiveInvestments)), - stackitem.NewBigInteger(new(big.Int).SetUint64(elig.CompletedInvestments)), - stackitem.NewBool(elig.HasViolations), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.ViolationCount))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.LastActivity))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.CreatedAt))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.UpdatedAt))), - }) -} - -func violationToStackItem(v *state.InvestmentViolation) stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(v.ID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(v.VitaID)), - stackitem.NewByteArray(v.Violator.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(v.OpportunityID)), - stackitem.NewByteArray([]byte(v.ViolationType)), - stackitem.NewByteArray([]byte(v.Description)), - stackitem.NewByteArray(v.EvidenceHash.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(v.Penalty)), - stackitem.NewByteArray(v.ReportedBy.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(v.ReportedAt))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(v.ResolvedAt))), - stackitem.NewByteArray([]byte(v.Resolution)), - }) -} - -func employmentToStackItem(ev *state.EmploymentVerification) stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(ev.VitaID)), - stackitem.NewByteArray(ev.Employee.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(ev.EmployerVitaID)), - stackitem.NewByteArray(ev.Employer.BytesBE()), - stackitem.NewByteArray([]byte(ev.Position)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ev.StartDate))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ev.EndDate))), - stackitem.NewBool(ev.IsActive), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ev.VerifiedAt))), - stackitem.NewByteArray(ev.VerifiedBy.BytesBE()), - }) -} - -func contractorToStackItem(cv *state.ContractorVerification) stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(cv.VitaID)), - stackitem.NewByteArray(cv.Contractor.BytesBE()), - stackitem.NewByteArray([]byte(cv.PlatformID)), - stackitem.NewByteArray(cv.Platform.BytesBE()), - stackitem.NewByteArray([]byte(cv.ContractorID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cv.StartDate))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cv.EndDate))), - stackitem.NewBool(cv.IsActive), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cv.VerifiedAt))), - stackitem.NewByteArray(cv.VerifiedBy.BytesBE()), - }) -} - -func commitmentToStackItem(c *state.InvestmentCommitment) stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(c.ID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.OpportunityID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.VitaID)), - stackitem.NewByteArray(c.Investor.BytesBE()), - stackitem.NewByteArray(c.Commitment.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.Status))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.CommittedAt))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.RevealDeadline))), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.RevealedAmount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.InvestmentID)), - }) -} - -// ============================================================================ -// Commit-Reveal System (Anti-Front-Running) -// ============================================================================ - -// commitInvestment creates a commitment to invest without revealing the amount. -// The commitment is hash(amount || nonce || investor). -func (col *Collocatio) commitInvestment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - oppID := toUint64(args[0]) - commitmentHashBytes, err := args[1].TryBytes() - if err != nil { - panic(err) - } - commitmentHash, err := util.Uint256DecodeBytesBE(commitmentHashBytes) - if err != nil { - panic("invalid commitment hash") - } - - caller := ic.VM.GetCallingScriptHash() - - // Validate caller has Vita - vita, err := col.Vita.GetTokenByOwner(ic.DAO, caller) - if err != nil { - panic("caller must have Vita token") - } - vitaID := vita.TokenID - - // Get opportunity - opp := col.getOpportunityInternal(ic.DAO, oppID) - if opp == nil { - panic("opportunity not found") - } - - if opp.Status != state.OpportunityActive { - panic("opportunity is not active") - } - if ic.Block.Index > opp.InvestmentDeadline { - panic("investment deadline has passed") - } - - // Check eligibility - if !col.isEligibleInternal(ic.DAO, caller, opp.Type) { - panic("investor not eligible for this opportunity type") - } - - cfg := col.getConfigInternal(ic.DAO) - - // Create commitment - commitmentID := col.incrementCounter(ic.DAO, makeCollocatioCommitmentCounterKey()) - currentBlock := ic.Block.Index - - commitment := &state.InvestmentCommitment{ - ID: commitmentID, - OpportunityID: oppID, - VitaID: vitaID, - Investor: caller, - Commitment: commitmentHash, - Status: state.CommitmentPending, - CommittedAt: currentBlock, - RevealDeadline: currentBlock + cfg.CommitRevealDelay + cfg.CommitRevealWindow, - RevealedAmount: 0, - InvestmentID: 0, - } - - col.putCommitment(ic.DAO, commitment) - - // Store indexes - ic.DAO.PutStorageItem(col.ID, makeCollocatioCommitmentByOppKey(oppID, commitmentID), []byte{1}) - ic.DAO.PutStorageItem(col.ID, makeCollocatioCommitmentByInvestorKey(vitaID, commitmentID), []byte{1}) - - // Emit event - ic.AddNotification(col.Hash, collocatioCommitmentCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), - stackitem.NewByteArray(caller.BytesBE()), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)) -} - -// revealInvestment reveals the investment amount and executes the investment. -// The reveal must happen after CommitRevealDelay blocks but before RevealDeadline. -func (col *Collocatio) revealInvestment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - commitmentID := toUint64(args[0]) - amount := toUint64(args[1]) - nonce, err := args[2].TryBytes() - if err != nil { - panic("invalid nonce") - } - - caller := ic.VM.GetCallingScriptHash() - - // Get commitment - commitment := col.getCommitmentInternal(ic.DAO, commitmentID) - if commitment == nil { - panic("commitment not found") - } - - if commitment.Investor != caller { - panic("only commitment owner can reveal") - } - - if commitment.Status != state.CommitmentPending { - panic("commitment already processed") - } - - cfg := col.getConfigInternal(ic.DAO) - - // Check timing - currentBlock := ic.Block.Index - revealStart := commitment.CommittedAt + cfg.CommitRevealDelay - if currentBlock < revealStart { - panic("reveal period not started yet") - } - if currentBlock > commitment.RevealDeadline { - panic("reveal deadline passed") - } - - // Verify commitment: hash(amount || nonce || investor) - preimage := make([]byte, 8+len(nonce)+util.Uint160Size) - binary.BigEndian.PutUint64(preimage[:8], amount) - copy(preimage[8:8+len(nonce)], nonce) - copy(preimage[8+len(nonce):], caller.BytesBE()) - - // Hash the preimage using SHA256 - computedHash := hash.Sha256(preimage) - if computedHash != commitment.Commitment { - panic("commitment verification failed") - } - - // Get opportunity - opp := col.getOpportunityInternal(ic.DAO, commitment.OpportunityID) - if opp == nil { - panic("opportunity not found") - } - - if opp.Status != state.OpportunityActive { - panic("opportunity is not active") - } - if currentBlock > opp.InvestmentDeadline { - panic("investment deadline has passed") - } - - // Validate amount - if amount < opp.MinInvestment { - panic("investment below minimum") - } - if amount > opp.MaxInvestment { - panic("investment exceeds maximum") - } - - if opp.MaxParticipants > 0 && opp.CurrentParticipants >= opp.MaxParticipants { - panic("maximum participants reached") - } - - // Whale concentration check - if cfg.WealthConcentration > 0 { - existingInvestment := col.getInvestorTotalInOpportunity(ic.DAO, commitment.VitaID, commitment.OpportunityID) - newTotal := existingInvestment + amount - futurePool := opp.TotalPool + amount - if futurePool > 0 { - concentration := (newTotal * 10000) / futurePool - if concentration > cfg.WealthConcentration { - panic("investment would exceed whale concentration limit") - } - } - } - - // Calculate fee - fee := (amount * cfg.InvestmentFee) / 10000 - netAmount := amount - fee - - // Transfer VTS from investor - if err := col.VTS.transferUnrestricted(ic, caller, col.Hash, new(big.Int).SetUint64(amount), nil); err != nil { - panic("failed to transfer investment amount") - } - - // Send fee to Treasury - if fee > 0 { - if err := col.VTS.transferUnrestricted(ic, col.Hash, nativehashes.Treasury, new(big.Int).SetUint64(fee), nil); err != nil { - panic("failed to transfer fee to treasury") - } - } - - // Create investment record - invID := col.incrementCounter(ic.DAO, makeCollocatioInvCounterKey()) - - inv := &state.Investment{ - ID: invID, - OpportunityID: commitment.OpportunityID, - VitaID: commitment.VitaID, - Investor: caller, - Amount: netAmount, - Status: state.InvestmentActive, - ReturnAmount: 0, - CreatedAt: currentBlock, - UpdatedAt: currentBlock, - } - - col.putInvestment(ic.DAO, inv) - - // Store indexes - ic.DAO.PutStorageItem(col.ID, makeCollocatioInvByOppKey(commitment.OpportunityID, invID), []byte{1}) - ic.DAO.PutStorageItem(col.ID, makeCollocatioInvByInvestorKey(commitment.VitaID, invID), []byte{1}) - - // Update opportunity - opp.CurrentParticipants++ - opp.TotalPool += netAmount - opp.UpdatedAt = currentBlock - col.putOpportunity(ic.DAO, opp) - - // Update commitment - commitment.Status = state.CommitmentRevealed - commitment.RevealedAmount = amount - commitment.InvestmentID = invID - col.putCommitment(ic.DAO, commitment) - - // Update eligibility - col.updateEligibilityOnInvest(ic.DAO, caller, commitment.VitaID, netAmount, currentBlock) - - // Emit events - ic.AddNotification(col.Hash, collocatioCommitmentRevealedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(amount)), - })) - - ic.AddNotification(col.Hash, collocatioInvestmentMadeEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(commitment.OpportunityID)), - stackitem.NewByteArray(caller.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(netAmount)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(invID)) -} - -// cancelCommitment cancels a pending commitment. -func (col *Collocatio) cancelCommitment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - commitmentID := toUint64(args[0]) - caller := ic.VM.GetCallingScriptHash() - - commitment := col.getCommitmentInternal(ic.DAO, commitmentID) - if commitment == nil { - panic("commitment not found") - } - - if commitment.Investor != caller { - panic("only commitment owner can cancel") - } - - if commitment.Status != state.CommitmentPending { - panic("commitment already processed") - } - - // Update commitment status - commitment.Status = state.CommitmentCanceled - col.putCommitment(ic.DAO, commitment) - - // Emit event - ic.AddNotification(col.Hash, collocatioCommitmentCanceledEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)), - })) - - return stackitem.NewBool(true) -} - -// getCommitment returns commitment details. -func (col *Collocatio) getCommitment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - commitmentID := toUint64(args[0]) - commitment := col.getCommitmentInternal(ic.DAO, commitmentID) - if commitment == nil { - return stackitem.Null{} - } - return commitmentToStackItem(commitment) -} - -// getCommitmentCount returns the total number of commitments. -func (col *Collocatio) getCommitmentCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - count := col.getCounter(ic.DAO, makeCollocatioCommitmentCounterKey()) - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} +package native + +import ( + "encoding/binary" + "fmt" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Collocatio represents the Investment native contract for democratic investment (PIO/EIO/CIO). +// Latin: "collocatio" = placement, arrangement (investment) +type Collocatio struct { + interop.ContractMD + Tutus ITutus + Vita IVita + RoleRegistry *RoleRegistry + VTS *VTS + Scire *Scire + Eligere *Eligere + Tribute *Tribute +} + +// Storage prefixes for Collocatio contract. +const ( + collocatioPrefixConfig byte = 0x01 // -> CollocatioConfig + collocatioPrefixOpportunity byte = 0x10 // opportunityID -> InvestmentOpportunity + collocatioPrefixOpportunityByType byte = 0x11 // type + opportunityID -> exists + collocatioPrefixOpportunityByStatus byte = 0x12 // status + opportunityID -> exists + collocatioPrefixOppCounter byte = 0x1F // -> next opportunity ID + collocatioPrefixInvestment byte = 0x20 // investmentID -> Investment + collocatioPrefixInvestmentByOpp byte = 0x21 // opportunityID + investmentID -> exists + collocatioPrefixInvestmentByInvestor byte = 0x22 // vitaID + investmentID -> exists + collocatioPrefixInvCounter byte = 0x2F // -> next investment ID + collocatioPrefixEligibility byte = 0x30 // vitaID -> InvestorEligibility + collocatioPrefixEligibilityByOwner byte = 0x31 // owner -> vitaID + collocatioPrefixViolation byte = 0x40 // violationID -> InvestmentViolation + collocatioPrefixViolationByInvestor byte = 0x41 // vitaID + violationID -> exists + collocatioPrefixViolationCounter byte = 0x4F // -> next violation ID + collocatioPrefixEmployment byte = 0x50 // employeeVitaID -> EmploymentVerification + collocatioPrefixEmploymentByEmployer byte = 0x51 // employerVitaID + employeeVitaID -> exists + collocatioPrefixContractor byte = 0x60 // contractorVitaID -> ContractorVerification + collocatioPrefixContractorByPlatform byte = 0x61 // platformHash + contractorVitaID -> exists + collocatioPrefixCommitment byte = 0x70 // commitmentID -> InvestmentCommitment + collocatioPrefixCommitmentByOpp byte = 0x71 // opportunityID + commitmentID -> exists + collocatioPrefixCommitmentByInvestor byte = 0x72 // vitaID + commitmentID -> exists + collocatioPrefixCommitmentCounter byte = 0x7F // -> next commitment ID +) + +// Collocatio events. +const ( + collocatioOpportunityCreatedEvent = "OpportunityCreated" + collocatioOpportunityActivatedEvent = "OpportunityActivated" + collocatioOpportunityClosedEvent = "OpportunityClosed" + collocatioOpportunityFailedEvent = "OpportunityFailed" + collocatioOpportunityCancelledEvent = "OpportunityCancelled" + collocatioInvestmentMadeEvent = "InvestmentMade" + collocatioInvestmentWithdrawnEvent = "InvestmentWithdrawn" + collocatioReturnsDistributedEvent = "ReturnsDistributed" + collocatioEligibilityUpdatedEvent = "EligibilityUpdated" + collocatioEducationCompletedEvent = "EducationCompleted" + collocatioViolationRecordedEvent = "ViolationRecorded" + collocatioViolationResolvedEvent = "ViolationResolved" + collocatioEmploymentVerifiedEvent = "EmploymentVerified" + collocatioEmploymentRevokedEvent = "EmploymentRevoked" + collocatioContractorVerifiedEvent = "ContractorVerified" + collocatioContractorRevokedEvent = "ContractorRevoked" + collocatioCommitmentCreatedEvent = "CommitmentCreated" + collocatioCommitmentRevealedEvent = "CommitmentRevealed" + collocatioCommitmentCanceledEvent = "CommitmentCanceled" +) + +// RoleInvestmentManager is the role ID for investment management. +const RoleInvestmentManager uint64 = 28 + +// Default config values. +const ( + defaultMinPIOParticipants uint64 = 100 + defaultMinEIOParticipants uint64 = 10 + defaultMinCIOParticipants uint64 = 25 + defaultMinInvestment uint64 = 100_00000000 // 100 VTS + defaultMaxIndividualCap uint64 = 1_000_000_00000000 // 1M VTS + defaultWealthConcentration uint64 = 500 // 5% + defaultCreationFee uint64 = 1000_00000000 // 1000 VTS + defaultInvestmentFee uint64 = 50 // 0.5% + defaultWithdrawalPenalty uint64 = 200 // 2% + defaultMinVotingPeriod uint32 = 10000 + defaultMinInvestmentPeriod uint32 = 20000 + defaultMinMaturityPeriod uint32 = 50000 + defaultMaxViolationsBeforeBan uint8 = 3 + defaultCommitRevealDelay uint32 = 10 // Min blocks between commit and reveal + defaultCommitRevealWindow uint32 = 1000 // Max blocks to reveal after delay +) + +var _ interop.Contract = (*Collocatio)(nil) + +func newCollocatio() *Collocatio { + c := &Collocatio{ + ContractMD: *interop.NewContractMD(nativenames.Collocatio, nativeids.Collocatio), + } + defer c.BuildHFSpecificMD(c.ActiveIn()) + + // getConfig + desc := NewDescriptor("getConfig", smartcontract.ArrayType) + md := NewMethodAndPrice(c.getConfig, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // getOpportunityCount + desc = NewDescriptor("getOpportunityCount", smartcontract.IntegerType) + md = NewMethodAndPrice(c.getOpportunityCount, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // getOpportunity + desc = NewDescriptor("getOpportunity", smartcontract.ArrayType, + manifest.NewParameter("opportunityID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.getOpportunity, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // createOpportunity + desc = NewDescriptor("createOpportunity", smartcontract.IntegerType, + manifest.NewParameter("oppType", smartcontract.IntegerType), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("description", smartcontract.StringType), + manifest.NewParameter("termsHash", smartcontract.Hash256Type), + manifest.NewParameter("minParticipants", smartcontract.IntegerType), + manifest.NewParameter("maxParticipants", smartcontract.IntegerType), + manifest.NewParameter("minInvestment", smartcontract.IntegerType), + manifest.NewParameter("maxInvestment", smartcontract.IntegerType), + manifest.NewParameter("targetPool", smartcontract.IntegerType), + manifest.NewParameter("expectedReturns", smartcontract.IntegerType), + manifest.NewParameter("riskLevel", smartcontract.IntegerType), + manifest.NewParameter("maturityBlocks", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.createOpportunity, 1<<17, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // activateOpportunity + desc = NewDescriptor("activateOpportunity", smartcontract.BoolType, + manifest.NewParameter("opportunityID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.activateOpportunity, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // invest + desc = NewDescriptor("invest", smartcontract.IntegerType, + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.invest, 1<<17, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // withdraw + desc = NewDescriptor("withdraw", smartcontract.BoolType, + manifest.NewParameter("investmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.withdraw, 1<<17, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // getEligibility + desc = NewDescriptor("getEligibility", smartcontract.ArrayType, + manifest.NewParameter("investor", smartcontract.Hash160Type)) + md = NewMethodAndPrice(c.getEligibility, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // setEligibility + desc = NewDescriptor("setEligibility", smartcontract.BoolType, + manifest.NewParameter("investor", smartcontract.Hash160Type), + manifest.NewParameter("eligibilityFlags", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.setEligibility, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // isEligible + desc = NewDescriptor("isEligible", smartcontract.BoolType, + manifest.NewParameter("investor", smartcontract.Hash160Type), + manifest.NewParameter("oppType", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.isEligible, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // getInvestment + desc = NewDescriptor("getInvestment", smartcontract.ArrayType, + manifest.NewParameter("investmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.getInvestment, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // getInvestmentCount + desc = NewDescriptor("getInvestmentCount", smartcontract.IntegerType) + md = NewMethodAndPrice(c.getInvestmentCount, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // recordViolation + desc = NewDescriptor("recordViolation", smartcontract.IntegerType, + manifest.NewParameter("violator", smartcontract.Hash160Type), + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("violationType", smartcontract.StringType), + manifest.NewParameter("description", smartcontract.StringType), + manifest.NewParameter("evidenceHash", smartcontract.Hash256Type), + manifest.NewParameter("penalty", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.recordViolation, 1<<17, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // resolveViolation + desc = NewDescriptor("resolveViolation", smartcontract.BoolType, + manifest.NewParameter("violationID", smartcontract.IntegerType), + manifest.NewParameter("resolution", smartcontract.StringType)) + md = NewMethodAndPrice(c.resolveViolation, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // getViolation + desc = NewDescriptor("getViolation", smartcontract.ArrayType, + manifest.NewParameter("violationID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.getViolation, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // verifyEmployment + desc = NewDescriptor("verifyEmployment", smartcontract.BoolType, + manifest.NewParameter("employee", smartcontract.Hash160Type), + manifest.NewParameter("employer", smartcontract.Hash160Type), + manifest.NewParameter("position", smartcontract.StringType), + manifest.NewParameter("startDate", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.verifyEmployment, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // revokeEmployment + desc = NewDescriptor("revokeEmployment", smartcontract.BoolType, + manifest.NewParameter("employee", smartcontract.Hash160Type), + manifest.NewParameter("employer", smartcontract.Hash160Type), + manifest.NewParameter("endDate", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.revokeEmployment, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // getEmploymentStatus + desc = NewDescriptor("getEmploymentStatus", smartcontract.ArrayType, + manifest.NewParameter("employee", smartcontract.Hash160Type)) + md = NewMethodAndPrice(c.getEmploymentStatus, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // verifyContractor + desc = NewDescriptor("verifyContractor", smartcontract.BoolType, + manifest.NewParameter("contractor", smartcontract.Hash160Type), + manifest.NewParameter("platform", smartcontract.Hash160Type), + manifest.NewParameter("platformID", smartcontract.StringType), + manifest.NewParameter("contractorID", smartcontract.StringType), + manifest.NewParameter("startDate", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.verifyContractor, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // revokeContractor + desc = NewDescriptor("revokeContractor", smartcontract.BoolType, + manifest.NewParameter("contractor", smartcontract.Hash160Type), + manifest.NewParameter("platform", smartcontract.Hash160Type), + manifest.NewParameter("endDate", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.revokeContractor, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // getContractorStatus + desc = NewDescriptor("getContractorStatus", smartcontract.ArrayType, + manifest.NewParameter("contractor", smartcontract.Hash160Type)) + md = NewMethodAndPrice(c.getContractorStatus, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // completeInvestmentEducation + desc = NewDescriptor("completeInvestmentEducation", smartcontract.BoolType, + manifest.NewParameter("investor", smartcontract.Hash160Type), + manifest.NewParameter("certificationID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.completeInvestmentEducation, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // distributeReturns + desc = NewDescriptor("distributeReturns", smartcontract.BoolType, + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("actualReturns", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.distributeReturns, 1<<18, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // cancelOpportunity + desc = NewDescriptor("cancelOpportunity", smartcontract.BoolType, + manifest.NewParameter("opportunityID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.cancelOpportunity, 1<<18, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // getInvestmentsByOpportunity + desc = NewDescriptor("getInvestmentsByOpportunity", smartcontract.ArrayType, + manifest.NewParameter("opportunityID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.getInvestmentsByOpportunity, 1<<16, callflag.ReadStates) + c.AddMethod(md, desc) + + // getInvestmentsByInvestor + desc = NewDescriptor("getInvestmentsByInvestor", smartcontract.ArrayType, + manifest.NewParameter("investor", smartcontract.Hash160Type)) + md = NewMethodAndPrice(c.getInvestmentsByInvestor, 1<<16, callflag.ReadStates) + c.AddMethod(md, desc) + + // getOpportunitiesByType + desc = NewDescriptor("getOpportunitiesByType", smartcontract.ArrayType, + manifest.NewParameter("oppType", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.getOpportunitiesByType, 1<<16, callflag.ReadStates) + c.AddMethod(md, desc) + + // getOpportunitiesByStatus + desc = NewDescriptor("getOpportunitiesByStatus", smartcontract.ArrayType, + manifest.NewParameter("status", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.getOpportunitiesByStatus, 1<<16, callflag.ReadStates) + c.AddMethod(md, desc) + + // commitInvestment - Phase 1 of commit-reveal (anti-front-running) + desc = NewDescriptor("commitInvestment", smartcontract.IntegerType, + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("commitment", smartcontract.Hash256Type)) + md = NewMethodAndPrice(c.commitInvestment, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // revealInvestment - Phase 2 of commit-reveal + desc = NewDescriptor("revealInvestment", smartcontract.IntegerType, + manifest.NewParameter("commitmentID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("nonce", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(c.revealInvestment, 1<<17, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // cancelCommitment - Cancel a pending commitment + desc = NewDescriptor("cancelCommitment", smartcontract.BoolType, + manifest.NewParameter("commitmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.cancelCommitment, 1<<16, callflag.States|callflag.AllowNotify) + c.AddMethod(md, desc) + + // getCommitment - Query commitment details + desc = NewDescriptor("getCommitment", smartcontract.ArrayType, + manifest.NewParameter("commitmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(c.getCommitment, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // getCommitmentCount + desc = NewDescriptor("getCommitmentCount", smartcontract.IntegerType) + md = NewMethodAndPrice(c.getCommitmentCount, 1<<15, callflag.ReadStates) + c.AddMethod(md, desc) + + // ===== Events ===== + eDesc := NewEventDescriptor(collocatioOpportunityCreatedEvent, + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("oppType", smartcontract.IntegerType), + manifest.NewParameter("creator", smartcontract.Hash160Type)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioOpportunityActivatedEvent, + manifest.NewParameter("opportunityID", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioInvestmentMadeEvent, + manifest.NewParameter("investmentID", smartcontract.IntegerType), + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("investor", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioInvestmentWithdrawnEvent, + manifest.NewParameter("investmentID", smartcontract.IntegerType), + manifest.NewParameter("returnAmount", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioEligibilityUpdatedEvent, + manifest.NewParameter("investor", smartcontract.Hash160Type), + manifest.NewParameter("eligibility", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioOpportunityClosedEvent, + manifest.NewParameter("opportunityID", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioOpportunityFailedEvent, + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioOpportunityCancelledEvent, + manifest.NewParameter("opportunityID", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioReturnsDistributedEvent, + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("totalReturns", smartcontract.IntegerType), + manifest.NewParameter("investorCount", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioEducationCompletedEvent, + manifest.NewParameter("investor", smartcontract.Hash160Type), + manifest.NewParameter("certificationID", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioViolationRecordedEvent, + manifest.NewParameter("violationID", smartcontract.IntegerType), + manifest.NewParameter("violator", smartcontract.Hash160Type), + manifest.NewParameter("penalty", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioViolationResolvedEvent, + manifest.NewParameter("violationID", smartcontract.IntegerType), + manifest.NewParameter("resolution", smartcontract.StringType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioEmploymentVerifiedEvent, + manifest.NewParameter("employee", smartcontract.Hash160Type), + manifest.NewParameter("employer", smartcontract.Hash160Type)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioEmploymentRevokedEvent, + manifest.NewParameter("employee", smartcontract.Hash160Type), + manifest.NewParameter("employer", smartcontract.Hash160Type)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioContractorVerifiedEvent, + manifest.NewParameter("contractor", smartcontract.Hash160Type), + manifest.NewParameter("platform", smartcontract.Hash160Type)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioContractorRevokedEvent, + manifest.NewParameter("contractor", smartcontract.Hash160Type), + manifest.NewParameter("platform", smartcontract.Hash160Type)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioCommitmentCreatedEvent, + manifest.NewParameter("commitmentID", smartcontract.IntegerType), + manifest.NewParameter("opportunityID", smartcontract.IntegerType), + manifest.NewParameter("investor", smartcontract.Hash160Type)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioCommitmentRevealedEvent, + manifest.NewParameter("commitmentID", smartcontract.IntegerType), + manifest.NewParameter("investmentID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(collocatioCommitmentCanceledEvent, + manifest.NewParameter("commitmentID", smartcontract.IntegerType)) + c.AddEvent(NewEvent(eDesc)) + + return c +} + +// Metadata returns contract metadata. +func (c *Collocatio) Metadata() *interop.ContractMD { + return &c.ContractMD +} + +// Initialize initializes Collocatio contract. +func (c *Collocatio) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != c.ActiveIn() { + return nil + } + + // Initialize default config + cfg := state.CollocatioConfig{ + MinPIOParticipants: defaultMinPIOParticipants, + MinEIOParticipants: defaultMinEIOParticipants, + MinCIOParticipants: defaultMinCIOParticipants, + DefaultMinInvestment: defaultMinInvestment, + MaxIndividualCap: defaultMaxIndividualCap, + WealthConcentration: defaultWealthConcentration, + CreationFee: defaultCreationFee, + InvestmentFee: defaultInvestmentFee, + WithdrawalPenalty: defaultWithdrawalPenalty, + MinVotingPeriod: defaultMinVotingPeriod, + MinInvestmentPeriod: defaultMinInvestmentPeriod, + MinMaturityPeriod: defaultMinMaturityPeriod, + MaxViolationsBeforeBan: defaultMaxViolationsBeforeBan, + ViolationCooldown: 1000000, + CommitRevealDelay: defaultCommitRevealDelay, + CommitRevealWindow: defaultCommitRevealWindow, + } + c.setConfigInternal(ic.DAO, &cfg) + + return nil +} + +// InitializeCache fills native Collocatio cache from DAO. +func (c *Collocatio) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + return nil +} + +// OnPersist implements the Contract interface. +func (c *Collocatio) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +// Handles lifecycle automation for opportunities. +func (c *Collocatio) PostPersist(ic *interop.Context) error { + // Run every 100 blocks for performance + if ic.Block.Index%100 != 0 { + return nil + } + + // Process opportunities that need status updates + c.processActiveOpportunities(ic) + c.processClosedOpportunities(ic) + return nil +} + +// processActiveOpportunities handles Active opportunities past their investment deadline. +func (c *Collocatio) processActiveOpportunities(ic *interop.Context) { + prefix := []byte{collocatioPrefixOpportunityByStatus, byte(state.OpportunityActive)} + cfg := c.getConfigInternal(ic.DAO) + + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 9 { + return true + } + + oppID := binary.BigEndian.Uint64(k[1:9]) + opp := c.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + return true + } + + // Check if investment deadline passed + if ic.Block.Index < opp.InvestmentDeadline { + return true + } + + // Get minimum participants for this opportunity type + var minParticipants uint64 + switch opp.Type { + case state.OpportunityPIO: + minParticipants = cfg.MinPIOParticipants + case state.OpportunityEIO: + minParticipants = cfg.MinEIOParticipants + case state.OpportunityCIO: + minParticipants = cfg.MinCIOParticipants + default: + minParticipants = 1 + } + + // Use opportunity's own min if set + if opp.MinParticipants > minParticipants { + minParticipants = opp.MinParticipants + } + + // Check if opportunity met minimum participants + if opp.CurrentParticipants < minParticipants { + // Failed - didn't meet minimum participants + c.updateOpportunityStatus(ic, opp, state.OpportunityFailed) + ic.AddNotification(c.Hash, collocatioOpportunityFailedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), + stackitem.NewByteArray([]byte("insufficient participants")), + })) + } else { + // Success - close and move to maturity phase + c.updateOpportunityStatus(ic, opp, state.OpportunityClosed) + ic.AddNotification(c.Hash, collocatioOpportunityClosedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), + })) + } + return true + }) +} + +// processClosedOpportunities handles Closed opportunities past their maturity date. +func (c *Collocatio) processClosedOpportunities(ic *interop.Context) { + prefix := []byte{collocatioPrefixOpportunityByStatus, byte(state.OpportunityClosed)} + + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 9 { + return true + } + + oppID := binary.BigEndian.Uint64(k[1:9]) + opp := c.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + return true + } + + // Check if maturity date passed + if ic.Block.Index < opp.MaturityDate { + return true + } + + // Opportunity is mature and ready for returns distribution + // Note: Actual distribution is triggered by distributeReturns call + // This could emit a notification for off-chain systems + // For now, we just log that it's ready (no state change needed) + return true + }) +} + +// updateOpportunityStatus updates an opportunity's status and maintains status index. +func (c *Collocatio) updateOpportunityStatus(ic *interop.Context, opp *state.InvestmentOpportunity, newStatus state.OpportunityStatus) { + oldStatus := opp.Status + + // Remove from old status index + oldStatusKey := makeCollocatioOppByStatusKey(oldStatus, opp.ID) + ic.DAO.DeleteStorageItem(c.ID, oldStatusKey) + + // Update status + opp.Status = newStatus + opp.UpdatedAt = ic.Block.Index + + // Add to new status index + newStatusKey := makeCollocatioOppByStatusKey(newStatus, opp.ID) + ic.DAO.PutStorageItem(c.ID, newStatusKey, []byte{1}) + + // Save opportunity + c.putOpportunity(ic.DAO, opp) +} + +// ActiveIn returns nil (always active from genesis). +func (c *Collocatio) ActiveIn() *config.Hardfork { + return nil +} + +// ============================================================================ +// Storage Key Helpers +// ============================================================================ + +func makeCollocatioConfigKey() []byte { + return []byte{collocatioPrefixConfig} +} + +func makeCollocatioOppKey(oppID uint64) []byte { + key := make([]byte, 9) + key[0] = collocatioPrefixOpportunity + binary.BigEndian.PutUint64(key[1:], oppID) + return key +} + +func makeCollocatioOppByTypeKey(oppType state.OpportunityType, oppID uint64) []byte { + key := make([]byte, 10) + key[0] = collocatioPrefixOpportunityByType + key[1] = byte(oppType) + binary.BigEndian.PutUint64(key[2:], oppID) + return key +} + +func makeCollocatioOppByStatusKey(status state.OpportunityStatus, oppID uint64) []byte { + key := make([]byte, 10) + key[0] = collocatioPrefixOpportunityByStatus + key[1] = byte(status) + binary.BigEndian.PutUint64(key[2:], oppID) + return key +} + +func makeCollocatioOppCounterKey() []byte { + return []byte{collocatioPrefixOppCounter} +} + +func makeCollocatioInvKey(invID uint64) []byte { + key := make([]byte, 9) + key[0] = collocatioPrefixInvestment + binary.BigEndian.PutUint64(key[1:], invID) + return key +} + +func makeCollocatioInvByOppKey(oppID, invID uint64) []byte { + key := make([]byte, 17) + key[0] = collocatioPrefixInvestmentByOpp + binary.BigEndian.PutUint64(key[1:9], oppID) + binary.BigEndian.PutUint64(key[9:], invID) + return key +} + +func makeCollocatioInvByInvestorKey(vitaID, invID uint64) []byte { + key := make([]byte, 17) + key[0] = collocatioPrefixInvestmentByInvestor + binary.BigEndian.PutUint64(key[1:9], vitaID) + binary.BigEndian.PutUint64(key[9:], invID) + return key +} + +func makeCollocatioInvCounterKey() []byte { + return []byte{collocatioPrefixInvCounter} +} + +func makeCollocatioEligKey(vitaID uint64) []byte { + key := make([]byte, 9) + key[0] = collocatioPrefixEligibility + binary.BigEndian.PutUint64(key[1:], vitaID) + return key +} + +func makeCollocatioEligByOwnerKey(owner util.Uint160) []byte { + key := make([]byte, 1+util.Uint160Size) + key[0] = collocatioPrefixEligibilityByOwner + copy(key[1:], owner.BytesBE()) + return key +} + +func makeCollocatioViolationKey(violationID uint64) []byte { + key := make([]byte, 9) + key[0] = collocatioPrefixViolation + binary.BigEndian.PutUint64(key[1:], violationID) + return key +} + +func makeCollocatioViolationByInvestorKey(vitaID, violationID uint64) []byte { + key := make([]byte, 17) + key[0] = collocatioPrefixViolationByInvestor + binary.BigEndian.PutUint64(key[1:9], vitaID) + binary.BigEndian.PutUint64(key[9:], violationID) + return key +} + +func makeCollocatioViolationCounterKey() []byte { + return []byte{collocatioPrefixViolationCounter} +} + +func makeCollocatioEmploymentKey(employeeVitaID uint64) []byte { + key := make([]byte, 9) + key[0] = collocatioPrefixEmployment + binary.BigEndian.PutUint64(key[1:], employeeVitaID) + return key +} + +func makeCollocatioEmploymentByEmployerKey(employerVitaID, employeeVitaID uint64) []byte { + key := make([]byte, 17) + key[0] = collocatioPrefixEmploymentByEmployer + binary.BigEndian.PutUint64(key[1:9], employerVitaID) + binary.BigEndian.PutUint64(key[9:], employeeVitaID) + return key +} + +func makeCollocatioContractorKey(contractorVitaID uint64) []byte { + key := make([]byte, 9) + key[0] = collocatioPrefixContractor + binary.BigEndian.PutUint64(key[1:], contractorVitaID) + return key +} + +func makeCollocatioContractorByPlatformKey(platform util.Uint160, contractorVitaID uint64) []byte { + key := make([]byte, 1+util.Uint160Size+8) + key[0] = collocatioPrefixContractorByPlatform + copy(key[1:1+util.Uint160Size], platform.BytesBE()) + binary.BigEndian.PutUint64(key[1+util.Uint160Size:], contractorVitaID) + return key +} + +func makeCollocatioCommitmentKey(commitmentID uint64) []byte { + key := make([]byte, 9) + key[0] = collocatioPrefixCommitment + binary.BigEndian.PutUint64(key[1:], commitmentID) + return key +} + +func makeCollocatioCommitmentByOppKey(oppID, commitmentID uint64) []byte { + key := make([]byte, 17) + key[0] = collocatioPrefixCommitmentByOpp + binary.BigEndian.PutUint64(key[1:9], oppID) + binary.BigEndian.PutUint64(key[9:], commitmentID) + return key +} + +func makeCollocatioCommitmentByInvestorKey(vitaID, commitmentID uint64) []byte { + key := make([]byte, 17) + key[0] = collocatioPrefixCommitmentByInvestor + binary.BigEndian.PutUint64(key[1:9], vitaID) + binary.BigEndian.PutUint64(key[9:], commitmentID) + return key +} + +func makeCollocatioCommitmentCounterKey() []byte { + return []byte{collocatioPrefixCommitmentCounter} +} + +// ============================================================================ +// Internal Storage Methods +// ============================================================================ + +func (c *Collocatio) getConfigInternal(d *dao.Simple) *state.CollocatioConfig { + si := d.GetStorageItem(c.ID, makeCollocatioConfigKey()) + if si == nil { + return &state.CollocatioConfig{ + MinPIOParticipants: defaultMinPIOParticipants, + MinEIOParticipants: defaultMinEIOParticipants, + MinCIOParticipants: defaultMinCIOParticipants, + DefaultMinInvestment: defaultMinInvestment, + MaxIndividualCap: defaultMaxIndividualCap, + WealthConcentration: defaultWealthConcentration, + CreationFee: defaultCreationFee, + InvestmentFee: defaultInvestmentFee, + WithdrawalPenalty: defaultWithdrawalPenalty, + MinVotingPeriod: defaultMinVotingPeriod, + MinInvestmentPeriod: defaultMinInvestmentPeriod, + MinMaturityPeriod: defaultMinMaturityPeriod, + MaxViolationsBeforeBan: defaultMaxViolationsBeforeBan, + ViolationCooldown: 1000000, + } + } + cfg := new(state.CollocatioConfig) + item, _ := stackitem.Deserialize(si) + cfg.FromStackItem(item) + return cfg +} + +func (c *Collocatio) setConfigInternal(d *dao.Simple, cfg *state.CollocatioConfig) { + item, _ := cfg.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioConfigKey(), data) +} + +func (c *Collocatio) getCounter(d *dao.Simple, key []byte) uint64 { + si := d.GetStorageItem(c.ID, key) + if si == nil || len(si) < 8 { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (c *Collocatio) incrementCounter(d *dao.Simple, key []byte) uint64 { + current := c.getCounter(d, key) + next := current + 1 + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, next) + d.PutStorageItem(c.ID, key, buf) + return next +} + +func (c *Collocatio) getOpportunityInternal(d *dao.Simple, oppID uint64) *state.InvestmentOpportunity { + si := d.GetStorageItem(c.ID, makeCollocatioOppKey(oppID)) + if si == nil { + return nil + } + opp := new(state.InvestmentOpportunity) + item, _ := stackitem.Deserialize(si) + opp.FromStackItem(item) + return opp +} + +func (c *Collocatio) putOpportunity(d *dao.Simple, opp *state.InvestmentOpportunity) { + item, _ := opp.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioOppKey(opp.ID), data) +} + +func (c *Collocatio) getInvestmentInternal(d *dao.Simple, invID uint64) *state.Investment { + si := d.GetStorageItem(c.ID, makeCollocatioInvKey(invID)) + if si == nil { + return nil + } + inv := new(state.Investment) + item, _ := stackitem.Deserialize(si) + inv.FromStackItem(item) + return inv +} + +func (c *Collocatio) putInvestment(d *dao.Simple, inv *state.Investment) { + item, _ := inv.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioInvKey(inv.ID), data) +} + +func (c *Collocatio) getEligibilityInternal(d *dao.Simple, investor util.Uint160) *state.InvestorEligibility { + // First get vitaID from owner mapping + si := d.GetStorageItem(c.ID, makeCollocatioEligByOwnerKey(investor)) + if si == nil || len(si) < 8 { + return nil + } + vitaID := binary.BigEndian.Uint64(si) + + // Then get eligibility + eligSI := d.GetStorageItem(c.ID, makeCollocatioEligKey(vitaID)) + if eligSI == nil { + return nil + } + elig := new(state.InvestorEligibility) + item, _ := stackitem.Deserialize(eligSI) + elig.FromStackItem(item) + return elig +} + +func (c *Collocatio) putEligibility(d *dao.Simple, elig *state.InvestorEligibility) { + item, _ := elig.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioEligKey(elig.VitaID), data) + + // Also store owner -> vitaID mapping + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, elig.VitaID) + d.PutStorageItem(c.ID, makeCollocatioEligByOwnerKey(elig.Investor), buf) +} + +func (c *Collocatio) getCommitmentInternal(d *dao.Simple, commitmentID uint64) *state.InvestmentCommitment { + si := d.GetStorageItem(c.ID, makeCollocatioCommitmentKey(commitmentID)) + if si == nil { + return nil + } + commitment := new(state.InvestmentCommitment) + item, _ := stackitem.Deserialize(si) + commitment.FromStackItem(item) + return commitment +} + +func (c *Collocatio) putCommitment(d *dao.Simple, commitment *state.InvestmentCommitment) { + item, _ := commitment.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioCommitmentKey(commitment.ID), data) +} + +// getInvestorTotalInOpportunity returns the total amount an investor has invested in a specific opportunity. +func (c *Collocatio) getInvestorTotalInOpportunity(d *dao.Simple, vitaID, oppID uint64) uint64 { + prefix := []byte{collocatioPrefixInvestmentByInvestor} + prefix = append(prefix, make([]byte, 8)...) + binary.BigEndian.PutUint64(prefix[1:], vitaID) + + var total uint64 + d.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 16 { + invID := binary.BigEndian.Uint64(k[8:16]) + inv := c.getInvestmentInternal(d, invID) + if inv != nil && inv.OpportunityID == oppID && inv.Status == state.InvestmentActive { + total += inv.Amount + } + } + return true + }) + return total +} + +// ============================================================================ +// Contract Methods +// ============================================================================ + +func (c *Collocatio) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + cfg := c.getConfigInternal(ic.DAO) + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MinPIOParticipants)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MinEIOParticipants)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MinCIOParticipants)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.DefaultMinInvestment)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MaxIndividualCap)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.WealthConcentration)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.CreationFee)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.InvestmentFee)), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.WithdrawalPenalty)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MinVotingPeriod))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MinInvestmentPeriod))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MinMaturityPeriod))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.MaxViolationsBeforeBan))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cfg.ViolationCooldown))), + }) +} + +func (c *Collocatio) getOpportunityCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + count := c.getCounter(ic.DAO, makeCollocatioOppCounterKey()) + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} + +func (c *Collocatio) getOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppID := toUint64(args[0]) + opp := c.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + return stackitem.Null{} + } + return opportunityToStackItem(opp) +} + +func (c *Collocatio) createOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { + caller := ic.VM.GetCallingScriptHash() + + oppType := state.OpportunityType(toUint64(args[0])) + name := toString(args[1]) + description := toString(args[2]) + termsHashBytes, err := args[3].TryBytes() + if err != nil { + panic(err) + } + termsHash, err := util.Uint256DecodeBytesBE(termsHashBytes) + if err != nil { + panic(err) + } + minParticipants := toUint64(args[4]) + maxParticipants := toUint64(args[5]) + minInvestment := toUint64(args[6]) + maxInvestment := toUint64(args[7]) + targetPool := toUint64(args[8]) + expectedReturns := toUint64(args[9]) + riskLevel := uint8(toUint64(args[10])) + maturityBlocks := uint32(toUint64(args[11])) + + // Validate caller has Vita + vita, err := c.Vita.GetTokenByOwner(ic.DAO, caller) + if err != nil { + panic("caller must have Vita token") + } + vitaID := vita.TokenID + + // Validate opportunity type + if oppType > state.OpportunityCIO { + panic("invalid opportunity type") + } + + // Validate parameters + cfg := c.getConfigInternal(ic.DAO) + if maturityBlocks < cfg.MinMaturityPeriod { + panic("maturity period too short") + } + if riskLevel < 1 || riskLevel > 10 { + panic("risk level must be 1-10") + } + + // Get minimum participants based on type + var minRequired uint64 + switch oppType { + case state.OpportunityPIO: + minRequired = cfg.MinPIOParticipants + case state.OpportunityEIO: + minRequired = cfg.MinEIOParticipants + case state.OpportunityCIO: + minRequired = cfg.MinCIOParticipants + } + if minParticipants < minRequired { + panic(fmt.Sprintf("minimum participants must be at least %d for this type", minRequired)) + } + + // Charge creation fee to Treasury + if cfg.CreationFee > 0 { + if err := c.VTS.transferUnrestricted(ic, caller, nativehashes.Treasury, new(big.Int).SetUint64(cfg.CreationFee), nil); err != nil { + panic("failed to pay creation fee") + } + } + + // Create opportunity + oppID := c.incrementCounter(ic.DAO, makeCollocatioOppCounterKey()) + currentBlock := ic.Block.Index + + opp := &state.InvestmentOpportunity{ + ID: oppID, + Type: oppType, + Status: state.OpportunityDraft, + Creator: caller, + SponsorVitaID: vitaID, + Name: name, + Description: description, + TermsHash: termsHash, + MinParticipants: minParticipants, + MaxParticipants: maxParticipants, + CurrentParticipants: 0, + MinInvestment: minInvestment, + MaxInvestment: maxInvestment, + TotalPool: 0, + TargetPool: targetPool, + ExpectedReturns: expectedReturns, + RiskLevel: riskLevel, + VotingDeadline: currentBlock + cfg.MinVotingPeriod, + InvestmentDeadline: currentBlock + cfg.MinVotingPeriod + cfg.MinInvestmentPeriod, + MaturityDate: currentBlock + cfg.MinVotingPeriod + cfg.MinInvestmentPeriod + maturityBlocks, + ProposalID: 0, + CreatedAt: currentBlock, + UpdatedAt: currentBlock, + } + + c.putOpportunity(ic.DAO, opp) + + // Store type index + ic.DAO.PutStorageItem(c.ID, makeCollocatioOppByTypeKey(oppType, oppID), []byte{1}) + + // Emit event + ic.AddNotification(c.Hash, collocatioOpportunityCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(oppType))), + stackitem.NewByteArray(caller.BytesBE()), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)) +} + +func (c *Collocatio) activateOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppID := toUint64(args[0]) + + opp := c.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + panic("opportunity not found") + } + + caller := ic.VM.GetCallingScriptHash() + if caller != opp.Creator && !c.Tutus.CheckCommittee(ic) { + panic("only creator or committee can activate") + } + + if opp.Status != state.OpportunityDraft { + panic("opportunity must be in draft status") + } + + opp.Status = state.OpportunityActive + opp.UpdatedAt = ic.Block.Index + c.putOpportunity(ic.DAO, opp) + + ic.AddNotification(c.Hash, collocatioOpportunityActivatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) invest(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppID := toUint64(args[0]) + amount := toUint64(args[1]) + caller := ic.VM.GetCallingScriptHash() + + // Validate caller has Vita + vita, err := c.Vita.GetTokenByOwner(ic.DAO, caller) + if err != nil { + panic("caller must have Vita token") + } + vitaID := vita.TokenID + + // Get opportunity + opp := c.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + panic("opportunity not found") + } + + if opp.Status != state.OpportunityActive { + panic("opportunity is not active") + } + if ic.Block.Index > opp.InvestmentDeadline { + panic("investment deadline has passed") + } + + if opp.MaxParticipants > 0 && opp.CurrentParticipants >= opp.MaxParticipants { + panic("maximum participants reached") + } + + if amount < opp.MinInvestment { + panic("investment below minimum") + } + if amount > opp.MaxInvestment { + panic("investment exceeds maximum") + } + + // Check eligibility + if !c.isEligibleInternal(ic.DAO, caller, opp.Type) { + panic("investor not eligible for this opportunity type") + } + + // Calculate fee + cfg := c.getConfigInternal(ic.DAO) + + // Whale concentration check - prevent any single investor from holding too much of the pool + if cfg.WealthConcentration > 0 { + existingInvestment := c.getInvestorTotalInOpportunity(ic.DAO, vitaID, oppID) + newTotal := existingInvestment + amount + // Calculate what percentage of the pool this investor would hold + // (newTotal / (opp.TotalPool + amount)) * 10000 > WealthConcentration + futurePool := opp.TotalPool + amount + if futurePool > 0 { + concentration := (newTotal * 10000) / futurePool + if concentration > cfg.WealthConcentration { + panic("investment would exceed whale concentration limit") + } + } + } + fee := (amount * cfg.InvestmentFee) / 10000 + netAmount := amount - fee + + // Transfer VTS from investor + if err := c.VTS.transferUnrestricted(ic, caller, c.Hash, new(big.Int).SetUint64(amount), nil); err != nil { + panic("failed to transfer investment amount") + } + + // Send fee to Treasury + if fee > 0 { + if err := c.VTS.transferUnrestricted(ic, c.Hash, nativehashes.Treasury, new(big.Int).SetUint64(fee), nil); err != nil { + panic("failed to transfer fee to treasury") + } + } + + // Create investment record + invID := c.incrementCounter(ic.DAO, makeCollocatioInvCounterKey()) + + inv := &state.Investment{ + ID: invID, + OpportunityID: oppID, + VitaID: vitaID, + Investor: caller, + Amount: netAmount, + Status: state.InvestmentActive, + ReturnAmount: 0, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + + c.putInvestment(ic.DAO, inv) + + // Store indexes + ic.DAO.PutStorageItem(c.ID, makeCollocatioInvByOppKey(oppID, invID), []byte{1}) + ic.DAO.PutStorageItem(c.ID, makeCollocatioInvByInvestorKey(vitaID, invID), []byte{1}) + + // Update opportunity + opp.CurrentParticipants++ + opp.TotalPool += netAmount + opp.UpdatedAt = ic.Block.Index + c.putOpportunity(ic.DAO, opp) + + // Update eligibility stats + c.updateEligibilityOnInvest(ic.DAO, caller, vitaID, netAmount, ic.Block.Index) + + // Emit event + ic.AddNotification(c.Hash, collocatioInvestmentMadeEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), + stackitem.NewByteArray(caller.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(netAmount)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(invID)) +} + +func (c *Collocatio) withdraw(ic *interop.Context, args []stackitem.Item) stackitem.Item { + invID := toUint64(args[0]) + caller := ic.VM.GetCallingScriptHash() + + inv := c.getInvestmentInternal(ic.DAO, invID) + if inv == nil { + panic("investment not found") + } + + if inv.Investor != caller { + panic("only investor can withdraw") + } + + if inv.Status != state.InvestmentActive { + panic("investment is not active") + } + + opp := c.getOpportunityInternal(ic.DAO, inv.OpportunityID) + if opp == nil { + panic("opportunity not found") + } + + // Calculate penalty for early withdrawal + cfg := c.getConfigInternal(ic.DAO) + returnAmount := inv.Amount + if ic.Block.Index < opp.MaturityDate && cfg.WithdrawalPenalty > 0 { + penalty := (inv.Amount * cfg.WithdrawalPenalty) / 10000 + returnAmount = inv.Amount - penalty + if penalty > 0 { + if err := c.VTS.transferUnrestricted(ic, c.Hash, nativehashes.Treasury, new(big.Int).SetUint64(penalty), nil); err != nil { + panic("failed to transfer penalty") + } + } + } + + // Return funds + if err := c.VTS.transferUnrestricted(ic, c.Hash, caller, new(big.Int).SetUint64(returnAmount), nil); err != nil { + panic("failed to return investment") + } + + // Update investment + inv.Status = state.InvestmentWithdrawn + inv.UpdatedAt = ic.Block.Index + c.putInvestment(ic.DAO, inv) + + // Update opportunity + opp.CurrentParticipants-- + opp.TotalPool -= inv.Amount + opp.UpdatedAt = ic.Block.Index + c.putOpportunity(ic.DAO, opp) + + // Update eligibility + c.updateEligibilityOnWithdraw(ic.DAO, caller, inv.Amount, ic.Block.Index) + + // Emit event + ic.AddNotification(c.Hash, collocatioInvestmentWithdrawnEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(returnAmount)), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) getEligibility(ic *interop.Context, args []stackitem.Item) stackitem.Item { + investor := toUint160(args[0]) + elig := c.getEligibilityInternal(ic.DAO, investor) + if elig == nil { + return stackitem.Null{} + } + return eligibilityToStackItem(elig) +} + +func (c *Collocatio) setEligibility(ic *interop.Context, args []stackitem.Item) stackitem.Item { + investor := toUint160(args[0]) + eligFlags := state.EligibilityType(toUint64(args[1])) + + // Only committee or RoleInvestmentManager can set eligibility + if !c.Tutus.CheckCommittee(ic) { + caller := ic.VM.GetCallingScriptHash() + if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { + panic("only committee or investment manager can set eligibility") + } + } + + vita, err := c.Vita.GetTokenByOwner(ic.DAO, investor) + if err != nil { + panic("investor must have Vita token") + } + vitaID := vita.TokenID + + elig := c.getEligibilityInternal(ic.DAO, investor) + if elig == nil { + elig = &state.InvestorEligibility{ + VitaID: vitaID, + Investor: investor, + CreatedAt: ic.Block.Index, + } + } + + elig.Eligibility = eligFlags + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + + ic.AddNotification(c.Hash, collocatioEligibilityUpdatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(investor.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(eligFlags))), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) isEligible(ic *interop.Context, args []stackitem.Item) stackitem.Item { + investor := toUint160(args[0]) + oppType := state.OpportunityType(toUint64(args[1])) + return stackitem.NewBool(c.isEligibleInternal(ic.DAO, investor, oppType)) +} + +func (c *Collocatio) isEligibleInternal(d *dao.Simple, investor util.Uint160, oppType state.OpportunityType) bool { + elig := c.getEligibilityInternal(d, investor) + if elig == nil { + return false + } + + // Must have completed investment education + if !elig.ScireCompleted { + return false + } + + // Check for ban + if elig.HasViolations { + cfg := c.getConfigInternal(d) + if elig.ViolationCount >= cfg.MaxViolationsBeforeBan { + return false + } + } + + switch oppType { + case state.OpportunityPIO: + return elig.Eligibility&state.EligibilityPIO != 0 + case state.OpportunityEIO: + if elig.Eligibility&state.EligibilityEIO == 0 { + return false + } + // Additionally verify active employment + return c.hasActiveEmployment(d, elig.VitaID) + case state.OpportunityCIO: + if elig.Eligibility&state.EligibilityCIO == 0 { + return false + } + // Additionally verify active contractor status + return c.hasActiveContractor(d, elig.VitaID) + default: + return false + } +} + +func (c *Collocatio) getInvestment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + invID := toUint64(args[0]) + inv := c.getInvestmentInternal(ic.DAO, invID) + if inv == nil { + return stackitem.Null{} + } + return investmentToStackItem(inv) +} + +func (c *Collocatio) getInvestmentCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + count := c.getCounter(ic.DAO, makeCollocatioInvCounterKey()) + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} + +// ============================================================================ +// Internal Helpers +// ============================================================================ + +func (c *Collocatio) updateEligibilityOnInvest(d *dao.Simple, investor util.Uint160, vitaID, amount uint64, blockHeight uint32) { + elig := c.getEligibilityInternal(d, investor) + if elig == nil { + elig = &state.InvestorEligibility{ + VitaID: vitaID, + Investor: investor, + CreatedAt: blockHeight, + } + } + elig.TotalInvested += amount + elig.ActiveInvestments++ + elig.LastActivity = blockHeight + elig.UpdatedAt = blockHeight + c.putEligibility(d, elig) +} + +func (c *Collocatio) updateEligibilityOnWithdraw(d *dao.Simple, investor util.Uint160, amount uint64, blockHeight uint32) { + elig := c.getEligibilityInternal(d, investor) + if elig == nil { + return + } + if elig.TotalInvested >= amount { + elig.TotalInvested -= amount + } + if elig.ActiveInvestments > 0 { + elig.ActiveInvestments-- + } + elig.LastActivity = blockHeight + elig.UpdatedAt = blockHeight + c.putEligibility(d, elig) +} + +// ============================================================================ +// Violation System +// ============================================================================ + +func (c *Collocatio) getViolationInternal(d *dao.Simple, violationID uint64) *state.InvestmentViolation { + si := d.GetStorageItem(c.ID, makeCollocatioViolationKey(violationID)) + if si == nil { + return nil + } + v := new(state.InvestmentViolation) + item, _ := stackitem.Deserialize(si) + v.FromStackItem(item) + return v +} + +func (c *Collocatio) putViolation(d *dao.Simple, v *state.InvestmentViolation) { + item, _ := v.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioViolationKey(v.ID), data) +} + +func (c *Collocatio) recordViolation(ic *interop.Context, args []stackitem.Item) stackitem.Item { + violator := toUint160(args[0]) + opportunityID := toUint64(args[1]) + violationType := toString(args[2]) + description := toString(args[3]) + evidenceHashBytes, err := args[4].TryBytes() + if err != nil { + panic(err) + } + evidenceHash, err := util.Uint256DecodeBytesBE(evidenceHashBytes) + if err != nil { + panic(err) + } + penalty := toUint64(args[5]) + + // Authorization: Committee or RoleInvestmentManager + if !c.Tutus.CheckCommittee(ic) { + caller := ic.VM.GetCallingScriptHash() + if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { + panic("only committee or investment manager can record violations") + } + } + + // Verify violator has Vita + vita, err := c.Vita.GetTokenByOwner(ic.DAO, violator) + if err != nil || vita == nil { + panic("violator must have active Vita") + } + vitaID := vita.TokenID + + // Create violation record + violationID := c.incrementCounter(ic.DAO, makeCollocatioViolationCounterKey()) + caller := ic.VM.GetCallingScriptHash() + + v := &state.InvestmentViolation{ + ID: violationID, + VitaID: vitaID, + Violator: violator, + OpportunityID: opportunityID, + ViolationType: violationType, + Description: description, + EvidenceHash: evidenceHash, + Penalty: penalty, + ReportedBy: caller, + ReportedAt: ic.Block.Index, + ResolvedAt: 0, + Resolution: "", + } + + c.putViolation(ic.DAO, v) + + // Store index by investor + ic.DAO.PutStorageItem(c.ID, makeCollocatioViolationByInvestorKey(vitaID, violationID), []byte{1}) + + // Update eligibility + elig := c.getEligibilityInternal(ic.DAO, violator) + if elig == nil { + elig = &state.InvestorEligibility{ + VitaID: vitaID, + Investor: violator, + CreatedAt: ic.Block.Index, + } + } + elig.HasViolations = true + elig.ViolationCount++ + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + + // Apply penalty if specified + if penalty > 0 { + if err := c.VTS.transferUnrestricted(ic, violator, nativehashes.Treasury, new(big.Int).SetUint64(penalty), nil); err != nil { + // Don't panic if transfer fails, just record violation without penalty + } + } + + // Emit event + ic.AddNotification(c.Hash, collocatioViolationRecordedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(violationID)), + stackitem.NewByteArray(violator.BytesBE()), + stackitem.NewByteArray([]byte(violationType)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(violationID)) +} + +func (c *Collocatio) resolveViolation(ic *interop.Context, args []stackitem.Item) stackitem.Item { + violationID := toUint64(args[0]) + resolution := toString(args[1]) + + // Authorization: Committee or RoleInvestmentManager + if !c.Tutus.CheckCommittee(ic) { + caller := ic.VM.GetCallingScriptHash() + if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { + panic("only committee or investment manager can resolve violations") + } + } + + v := c.getViolationInternal(ic.DAO, violationID) + if v == nil { + panic("violation not found") + } + if v.ResolvedAt != 0 { + panic("violation already resolved") + } + + v.ResolvedAt = ic.Block.Index + v.Resolution = resolution + c.putViolation(ic.DAO, v) + + // Emit event + ic.AddNotification(c.Hash, collocatioViolationResolvedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(violationID)), + stackitem.NewByteArray([]byte(resolution)), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) getViolation(ic *interop.Context, args []stackitem.Item) stackitem.Item { + violationID := toUint64(args[0]) + v := c.getViolationInternal(ic.DAO, violationID) + if v == nil { + return stackitem.Null{} + } + return violationToStackItem(v) +} + +// ============================================================================ +// Employment Verification (EIO) +// ============================================================================ + +func (c *Collocatio) getEmploymentInternal(d *dao.Simple, employeeVitaID uint64) *state.EmploymentVerification { + si := d.GetStorageItem(c.ID, makeCollocatioEmploymentKey(employeeVitaID)) + if si == nil { + return nil + } + ev := new(state.EmploymentVerification) + item, _ := stackitem.Deserialize(si) + ev.FromStackItem(item) + return ev +} + +func (c *Collocatio) putEmployment(d *dao.Simple, ev *state.EmploymentVerification) { + item, _ := ev.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioEmploymentKey(ev.VitaID), data) +} + +func (c *Collocatio) verifyEmployment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + employee := toUint160(args[0]) + employer := toUint160(args[1]) + position := toString(args[2]) + startDate := uint32(toUint64(args[3])) + + caller := ic.VM.GetCallingScriptHash() + + // Authorization: Committee, RoleInvestmentManager, or employer + isAuthorized := c.Tutus.CheckCommittee(ic) || + c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || + caller == employer + + if !isAuthorized { + panic("only committee, investment manager, or employer can verify employment") + } + + // Verify both employee and employer have Vita + employeeVita, err := c.Vita.GetTokenByOwner(ic.DAO, employee) + if err != nil { + panic("employee must have Vita token") + } + employerVita, err := c.Vita.GetTokenByOwner(ic.DAO, employer) + if err != nil { + panic("employer must have Vita token") + } + + ev := &state.EmploymentVerification{ + VitaID: employeeVita.TokenID, + Employee: employee, + EmployerVitaID: employerVita.TokenID, + Employer: employer, + Position: position, + StartDate: startDate, + EndDate: 0, + IsActive: true, + VerifiedAt: ic.Block.Index, + VerifiedBy: caller, + } + + c.putEmployment(ic.DAO, ev) + + // Store index by employer + ic.DAO.PutStorageItem(c.ID, makeCollocatioEmploymentByEmployerKey(employerVita.TokenID, employeeVita.TokenID), []byte{1}) + + // Update eligibility + elig := c.getEligibilityInternal(ic.DAO, employee) + if elig == nil { + elig = &state.InvestorEligibility{ + VitaID: employeeVita.TokenID, + Investor: employee, + CreatedAt: ic.Block.Index, + } + } + elig.Eligibility |= state.EligibilityEIO + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + + // Emit event + ic.AddNotification(c.Hash, collocatioEmploymentVerifiedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(employee.BytesBE()), + stackitem.NewByteArray(employer.BytesBE()), + stackitem.NewByteArray([]byte(position)), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) revokeEmployment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + employee := toUint160(args[0]) + employer := toUint160(args[1]) + endDate := uint32(toUint64(args[2])) + + caller := ic.VM.GetCallingScriptHash() + + // Authorization: Committee, RoleInvestmentManager, or employer + isAuthorized := c.Tutus.CheckCommittee(ic) || + c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || + caller == employer + + if !isAuthorized { + panic("only committee, investment manager, or employer can revoke employment") + } + + // Get employee Vita + employeeVita, err := c.Vita.GetTokenByOwner(ic.DAO, employee) + if err != nil { + panic("employee must have Vita token") + } + + ev := c.getEmploymentInternal(ic.DAO, employeeVita.TokenID) + if ev == nil { + panic("employment not found") + } + if !ev.IsActive { + panic("employment already revoked") + } + if ev.Employer != employer { + panic("employer mismatch") + } + + ev.IsActive = false + ev.EndDate = endDate + c.putEmployment(ic.DAO, ev) + + // Remove EIO eligibility + elig := c.getEligibilityInternal(ic.DAO, employee) + if elig != nil { + elig.Eligibility &^= state.EligibilityEIO + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + } + + // Emit event + ic.AddNotification(c.Hash, collocatioEmploymentRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(employee.BytesBE()), + stackitem.NewByteArray(employer.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) getEmploymentStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { + employee := toUint160(args[0]) + + vita, err := c.Vita.GetTokenByOwner(ic.DAO, employee) + if err != nil { + return stackitem.Null{} + } + + ev := c.getEmploymentInternal(ic.DAO, vita.TokenID) + if ev == nil { + return stackitem.Null{} + } + return employmentToStackItem(ev) +} + +func (c *Collocatio) hasActiveEmployment(d *dao.Simple, vitaID uint64) bool { + ev := c.getEmploymentInternal(d, vitaID) + return ev != nil && ev.IsActive +} + +// ============================================================================ +// Contractor Verification (CIO) +// ============================================================================ + +func (c *Collocatio) getContractorInternal(d *dao.Simple, contractorVitaID uint64) *state.ContractorVerification { + si := d.GetStorageItem(c.ID, makeCollocatioContractorKey(contractorVitaID)) + if si == nil { + return nil + } + cv := new(state.ContractorVerification) + item, _ := stackitem.Deserialize(si) + cv.FromStackItem(item) + return cv +} + +func (c *Collocatio) putContractor(d *dao.Simple, cv *state.ContractorVerification) { + item, _ := cv.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(c.ID, makeCollocatioContractorKey(cv.VitaID), data) +} + +func (c *Collocatio) verifyContractor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + contractor := toUint160(args[0]) + platform := toUint160(args[1]) + platformID := toString(args[2]) + contractorID := toString(args[3]) + startDate := uint32(toUint64(args[4])) + + caller := ic.VM.GetCallingScriptHash() + + // Authorization: Committee, RoleInvestmentManager, or platform + isAuthorized := c.Tutus.CheckCommittee(ic) || + c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || + caller == platform + + if !isAuthorized { + panic("only committee, investment manager, or platform can verify contractor") + } + + // Verify contractor has Vita + contractorVita, err := c.Vita.GetTokenByOwner(ic.DAO, contractor) + if err != nil { + panic("contractor must have Vita token") + } + + cv := &state.ContractorVerification{ + VitaID: contractorVita.TokenID, + Contractor: contractor, + PlatformID: platformID, + Platform: platform, + ContractorID: contractorID, + StartDate: startDate, + EndDate: 0, + IsActive: true, + VerifiedAt: ic.Block.Index, + VerifiedBy: caller, + } + + c.putContractor(ic.DAO, cv) + + // Store index by platform + ic.DAO.PutStorageItem(c.ID, makeCollocatioContractorByPlatformKey(platform, contractorVita.TokenID), []byte{1}) + + // Update eligibility + elig := c.getEligibilityInternal(ic.DAO, contractor) + if elig == nil { + elig = &state.InvestorEligibility{ + VitaID: contractorVita.TokenID, + Investor: contractor, + CreatedAt: ic.Block.Index, + } + } + elig.Eligibility |= state.EligibilityCIO + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + + // Emit event + ic.AddNotification(c.Hash, collocatioContractorVerifiedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(contractor.BytesBE()), + stackitem.NewByteArray(platform.BytesBE()), + stackitem.NewByteArray([]byte(platformID)), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) revokeContractor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + contractor := toUint160(args[0]) + platform := toUint160(args[1]) + endDate := uint32(toUint64(args[2])) + + caller := ic.VM.GetCallingScriptHash() + + // Authorization: Committee, RoleInvestmentManager, or platform + isAuthorized := c.Tutus.CheckCommittee(ic) || + c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) || + caller == platform + + if !isAuthorized { + panic("only committee, investment manager, or platform can revoke contractor") + } + + // Get contractor Vita + contractorVita, err := c.Vita.GetTokenByOwner(ic.DAO, contractor) + if err != nil { + panic("contractor must have Vita token") + } + + cv := c.getContractorInternal(ic.DAO, contractorVita.TokenID) + if cv == nil { + panic("contractor not found") + } + if !cv.IsActive { + panic("contractor already revoked") + } + if cv.Platform != platform { + panic("platform mismatch") + } + + cv.IsActive = false + cv.EndDate = endDate + c.putContractor(ic.DAO, cv) + + // Remove CIO eligibility + elig := c.getEligibilityInternal(ic.DAO, contractor) + if elig != nil { + elig.Eligibility &^= state.EligibilityCIO + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + } + + // Emit event + ic.AddNotification(c.Hash, collocatioContractorRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(contractor.BytesBE()), + stackitem.NewByteArray(platform.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) getContractorStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { + contractor := toUint160(args[0]) + + vita, err := c.Vita.GetTokenByOwner(ic.DAO, contractor) + if err != nil { + return stackitem.Null{} + } + + cv := c.getContractorInternal(ic.DAO, vita.TokenID) + if cv == nil { + return stackitem.Null{} + } + return contractorToStackItem(cv) +} + +func (c *Collocatio) hasActiveContractor(d *dao.Simple, vitaID uint64) bool { + cv := c.getContractorInternal(d, vitaID) + return cv != nil && cv.IsActive +} + +// ============================================================================ +// Education Completion +// ============================================================================ + +func (c *Collocatio) completeInvestmentEducation(ic *interop.Context, args []stackitem.Item) stackitem.Item { + investor := toUint160(args[0]) + + caller := ic.VM.GetCallingScriptHash() + + // Authorization: Committee or RoleInvestmentManager + if !c.Tutus.CheckCommittee(ic) { + if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { + panic("only committee or investment manager can complete education") + } + } + + // Verify investor has Vita + vita, err := c.Vita.GetTokenByOwner(ic.DAO, investor) + if err != nil { + panic("investor must have Vita token") + } + + // Update eligibility + elig := c.getEligibilityInternal(ic.DAO, investor) + if elig == nil { + elig = &state.InvestorEligibility{ + VitaID: vita.TokenID, + Investor: investor, + CreatedAt: ic.Block.Index, + } + } + elig.ScireCompleted = true + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + + // Emit event + ic.AddNotification(c.Hash, collocatioEducationCompletedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(investor.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +// ============================================================================ +// Returns Distribution & Cancellation +// ============================================================================ + +func (c *Collocatio) distributeReturns(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppID := toUint64(args[0]) + actualReturns := toUint64(args[1]) + + // Authorization: Committee or RoleInvestmentManager + if !c.Tutus.CheckCommittee(ic) { + caller := ic.VM.GetCallingScriptHash() + if !c.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleInvestmentManager, ic.Block.Index) { + panic("only committee or investment manager can distribute returns") + } + } + + opp := c.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + panic("opportunity not found") + } + if opp.Status != state.OpportunityClosed { + panic("opportunity must be in closed status") + } + if ic.Block.Index < opp.MaturityDate { + panic("maturity date not reached") + } + if opp.TotalPool == 0 { + panic("no investments to distribute") + } + + // Collect all investment IDs first (to avoid modifying during iteration) + prefix := []byte{collocatioPrefixInvestmentByOpp} + prefix = append(prefix, make([]byte, 8)...) + binary.BigEndian.PutUint64(prefix[1:], oppID) + + var invIDs []uint64 + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 16 { + invID := binary.BigEndian.Uint64(k[8:16]) + invIDs = append(invIDs, invID) + } + return true + }) + + // Process each investment + for _, invID := range invIDs { + inv := c.getInvestmentInternal(ic.DAO, invID) + if inv == nil || inv.Status != state.InvestmentActive { + continue + } + + // Calculate proportional return + returnAmount := (inv.Amount * actualReturns) / opp.TotalPool + + // Transfer return to investor + if returnAmount > 0 { + if err := c.VTS.transferUnrestricted(ic, c.Hash, inv.Investor, new(big.Int).SetUint64(returnAmount), nil); err != nil { + continue // Skip failed transfers + } + } + + // Also return principal + if inv.Amount > 0 { + if err := c.VTS.transferUnrestricted(ic, c.Hash, inv.Investor, new(big.Int).SetUint64(inv.Amount), nil); err != nil { + continue + } + } + + // Update investment + inv.ReturnAmount = returnAmount + inv.Status = state.InvestmentCompleted + inv.UpdatedAt = ic.Block.Index + c.putInvestment(ic.DAO, inv) + + // Update eligibility + elig := c.getEligibilityInternal(ic.DAO, inv.Investor) + if elig != nil { + elig.TotalReturns += returnAmount + elig.CompletedInvestments++ + if elig.ActiveInvestments > 0 { + elig.ActiveInvestments-- + } + if elig.TotalInvested >= inv.Amount { + elig.TotalInvested -= inv.Amount + } + elig.LastActivity = ic.Block.Index + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + } + + // Emit event for each distribution + ic.AddNotification(c.Hash, collocatioReturnsDistributedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), + stackitem.NewByteArray(inv.Investor.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(returnAmount)), + })) + } + + // Update opportunity status + opp.Status = state.OpportunityCompleted + opp.UpdatedAt = ic.Block.Index + c.putOpportunity(ic.DAO, opp) + + return stackitem.NewBool(true) +} + +func (c *Collocatio) cancelOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppID := toUint64(args[0]) + caller := ic.VM.GetCallingScriptHash() + + opp := c.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + panic("opportunity not found") + } + + // Authorization: Creator (if Draft/Voting) or Committee + isCreator := caller == opp.Creator + isCommittee := c.Tutus.CheckCommittee(ic) + + if opp.Status == state.OpportunityDraft || opp.Status == state.OpportunityVoting { + if !isCreator && !isCommittee { + panic("only creator or committee can cancel draft/voting opportunity") + } + } else if opp.Status == state.OpportunityActive { + if !isCommittee { + panic("only committee can cancel active opportunity") + } + } else { + panic("opportunity cannot be cancelled in current status") + } + + // Collect all investment IDs first (to avoid modifying during iteration) + prefix := []byte{collocatioPrefixInvestmentByOpp} + prefix = append(prefix, make([]byte, 8)...) + binary.BigEndian.PutUint64(prefix[1:], oppID) + + var invIDs []uint64 + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 16 { + invID := binary.BigEndian.Uint64(k[8:16]) + invIDs = append(invIDs, invID) + } + return true + }) + + // Process each investment + for _, invID := range invIDs { + inv := c.getInvestmentInternal(ic.DAO, invID) + if inv == nil || inv.Status != state.InvestmentActive { + continue + } + + // Refund full amount + if inv.Amount > 0 { + if err := c.VTS.transferUnrestricted(ic, c.Hash, inv.Investor, new(big.Int).SetUint64(inv.Amount), nil); err != nil { + continue + } + } + + // Update investment + inv.Status = state.InvestmentRefunded + inv.UpdatedAt = ic.Block.Index + c.putInvestment(ic.DAO, inv) + + // Update eligibility + elig := c.getEligibilityInternal(ic.DAO, inv.Investor) + if elig != nil { + if elig.ActiveInvestments > 0 { + elig.ActiveInvestments-- + } + if elig.TotalInvested >= inv.Amount { + elig.TotalInvested -= inv.Amount + } + elig.LastActivity = ic.Block.Index + elig.UpdatedAt = ic.Block.Index + c.putEligibility(ic.DAO, elig) + } + } + + // Update opportunity status + opp.Status = state.OpportunityCancelled + opp.UpdatedAt = ic.Block.Index + c.putOpportunity(ic.DAO, opp) + + // Emit event + ic.AddNotification(c.Hash, collocatioOpportunityCancelledEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), + })) + + return stackitem.NewBool(true) +} + +// ============================================================================ +// Query Methods +// ============================================================================ + +func (c *Collocatio) getInvestmentsByOpportunity(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppID := toUint64(args[0]) + + prefix := []byte{collocatioPrefixInvestmentByOpp} + prefix = append(prefix, make([]byte, 8)...) + binary.BigEndian.PutUint64(prefix[1:], oppID) + + var ids []stackitem.Item + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 16 { + invID := binary.BigEndian.Uint64(k[8:16]) + ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(invID))) + } + return true + }) + + return stackitem.NewArray(ids) +} + +func (c *Collocatio) getInvestmentsByInvestor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + investor := toUint160(args[0]) + + vita, err := c.Vita.GetTokenByOwner(ic.DAO, investor) + if err != nil { + return stackitem.NewArray(nil) + } + + prefix := []byte{collocatioPrefixInvestmentByInvestor} + prefix = append(prefix, make([]byte, 8)...) + binary.BigEndian.PutUint64(prefix[1:], vita.TokenID) + + var ids []stackitem.Item + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 16 { + invID := binary.BigEndian.Uint64(k[8:16]) + ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(invID))) + } + return true + }) + + return stackitem.NewArray(ids) +} + +func (c *Collocatio) getOpportunitiesByType(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppType := state.OpportunityType(toUint64(args[0])) + + prefix := []byte{collocatioPrefixOpportunityByType, byte(oppType)} + + var ids []stackitem.Item + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 9 { + oppID := binary.BigEndian.Uint64(k[1:9]) + ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(oppID))) + } + return true + }) + + return stackitem.NewArray(ids) +} + +func (c *Collocatio) getOpportunitiesByStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { + status := state.OpportunityStatus(toUint64(args[0])) + + prefix := []byte{collocatioPrefixOpportunityByStatus, byte(status)} + + var ids []stackitem.Item + ic.DAO.Seek(c.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 9 { + oppID := binary.BigEndian.Uint64(k[1:9]) + ids = append(ids, stackitem.NewBigInteger(new(big.Int).SetUint64(oppID))) + } + return true + }) + + return stackitem.NewArray(ids) +} + +// ============================================================================ +// Stack Item Converters +// ============================================================================ + +func opportunityToStackItem(opp *state.InvestmentOpportunity) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.ID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.Type))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.Status))), + stackitem.NewByteArray(opp.Creator.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.SponsorVitaID)), + stackitem.NewByteArray([]byte(opp.Name)), + stackitem.NewByteArray([]byte(opp.Description)), + stackitem.NewByteArray(opp.TermsHash.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MinParticipants)), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MaxParticipants)), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.CurrentParticipants)), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MinInvestment)), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.MaxInvestment)), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.TotalPool)), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.TargetPool)), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.ExpectedReturns)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.RiskLevel))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.VotingDeadline))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.InvestmentDeadline))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.MaturityDate))), + stackitem.NewBigInteger(new(big.Int).SetUint64(opp.ProposalID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.CreatedAt))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(opp.UpdatedAt))), + }) +} + +func investmentToStackItem(inv *state.Investment) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(inv.ID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(inv.OpportunityID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(inv.VitaID)), + stackitem.NewByteArray(inv.Investor.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(inv.Amount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(inv.Status))), + stackitem.NewBigInteger(new(big.Int).SetUint64(inv.ReturnAmount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(inv.CreatedAt))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(inv.UpdatedAt))), + }) +} + +func eligibilityToStackItem(elig *state.InvestorEligibility) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(elig.VitaID)), + stackitem.NewByteArray(elig.Investor.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.Eligibility))), + stackitem.NewBool(elig.ScireCompleted), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.RiskScore))), + stackitem.NewBigInteger(new(big.Int).SetUint64(elig.TotalInvested)), + stackitem.NewBigInteger(new(big.Int).SetUint64(elig.TotalReturns)), + stackitem.NewBigInteger(new(big.Int).SetUint64(elig.ActiveInvestments)), + stackitem.NewBigInteger(new(big.Int).SetUint64(elig.CompletedInvestments)), + stackitem.NewBool(elig.HasViolations), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.ViolationCount))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.LastActivity))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.CreatedAt))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(elig.UpdatedAt))), + }) +} + +func violationToStackItem(v *state.InvestmentViolation) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(v.ID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(v.VitaID)), + stackitem.NewByteArray(v.Violator.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(v.OpportunityID)), + stackitem.NewByteArray([]byte(v.ViolationType)), + stackitem.NewByteArray([]byte(v.Description)), + stackitem.NewByteArray(v.EvidenceHash.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(v.Penalty)), + stackitem.NewByteArray(v.ReportedBy.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(v.ReportedAt))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(v.ResolvedAt))), + stackitem.NewByteArray([]byte(v.Resolution)), + }) +} + +func employmentToStackItem(ev *state.EmploymentVerification) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(ev.VitaID)), + stackitem.NewByteArray(ev.Employee.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(ev.EmployerVitaID)), + stackitem.NewByteArray(ev.Employer.BytesBE()), + stackitem.NewByteArray([]byte(ev.Position)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ev.StartDate))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ev.EndDate))), + stackitem.NewBool(ev.IsActive), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ev.VerifiedAt))), + stackitem.NewByteArray(ev.VerifiedBy.BytesBE()), + }) +} + +func contractorToStackItem(cv *state.ContractorVerification) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(cv.VitaID)), + stackitem.NewByteArray(cv.Contractor.BytesBE()), + stackitem.NewByteArray([]byte(cv.PlatformID)), + stackitem.NewByteArray(cv.Platform.BytesBE()), + stackitem.NewByteArray([]byte(cv.ContractorID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cv.StartDate))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cv.EndDate))), + stackitem.NewBool(cv.IsActive), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(cv.VerifiedAt))), + stackitem.NewByteArray(cv.VerifiedBy.BytesBE()), + }) +} + +func commitmentToStackItem(c *state.InvestmentCommitment) stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(c.ID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.OpportunityID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.VitaID)), + stackitem.NewByteArray(c.Investor.BytesBE()), + stackitem.NewByteArray(c.Commitment.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.Status))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.CommittedAt))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.RevealDeadline))), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.RevealedAmount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.InvestmentID)), + }) +} + +// ============================================================================ +// Commit-Reveal System (Anti-Front-Running) +// ============================================================================ + +// commitInvestment creates a commitment to invest without revealing the amount. +// The commitment is hash(amount || nonce || investor). +func (col *Collocatio) commitInvestment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + oppID := toUint64(args[0]) + commitmentHashBytes, err := args[1].TryBytes() + if err != nil { + panic(err) + } + commitmentHash, err := util.Uint256DecodeBytesBE(commitmentHashBytes) + if err != nil { + panic("invalid commitment hash") + } + + caller := ic.VM.GetCallingScriptHash() + + // Validate caller has Vita + vita, err := col.Vita.GetTokenByOwner(ic.DAO, caller) + if err != nil { + panic("caller must have Vita token") + } + vitaID := vita.TokenID + + // Get opportunity + opp := col.getOpportunityInternal(ic.DAO, oppID) + if opp == nil { + panic("opportunity not found") + } + + if opp.Status != state.OpportunityActive { + panic("opportunity is not active") + } + if ic.Block.Index > opp.InvestmentDeadline { + panic("investment deadline has passed") + } + + // Check eligibility + if !col.isEligibleInternal(ic.DAO, caller, opp.Type) { + panic("investor not eligible for this opportunity type") + } + + cfg := col.getConfigInternal(ic.DAO) + + // Create commitment + commitmentID := col.incrementCounter(ic.DAO, makeCollocatioCommitmentCounterKey()) + currentBlock := ic.Block.Index + + commitment := &state.InvestmentCommitment{ + ID: commitmentID, + OpportunityID: oppID, + VitaID: vitaID, + Investor: caller, + Commitment: commitmentHash, + Status: state.CommitmentPending, + CommittedAt: currentBlock, + RevealDeadline: currentBlock + cfg.CommitRevealDelay + cfg.CommitRevealWindow, + RevealedAmount: 0, + InvestmentID: 0, + } + + col.putCommitment(ic.DAO, commitment) + + // Store indexes + ic.DAO.PutStorageItem(col.ID, makeCollocatioCommitmentByOppKey(oppID, commitmentID), []byte{1}) + ic.DAO.PutStorageItem(col.ID, makeCollocatioCommitmentByInvestorKey(vitaID, commitmentID), []byte{1}) + + // Emit event + ic.AddNotification(col.Hash, collocatioCommitmentCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(oppID)), + stackitem.NewByteArray(caller.BytesBE()), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)) +} + +// revealInvestment reveals the investment amount and executes the investment. +// The reveal must happen after CommitRevealDelay blocks but before RevealDeadline. +func (col *Collocatio) revealInvestment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + commitmentID := toUint64(args[0]) + amount := toUint64(args[1]) + nonce, err := args[2].TryBytes() + if err != nil { + panic("invalid nonce") + } + + caller := ic.VM.GetCallingScriptHash() + + // Get commitment + commitment := col.getCommitmentInternal(ic.DAO, commitmentID) + if commitment == nil { + panic("commitment not found") + } + + if commitment.Investor != caller { + panic("only commitment owner can reveal") + } + + if commitment.Status != state.CommitmentPending { + panic("commitment already processed") + } + + cfg := col.getConfigInternal(ic.DAO) + + // Check timing + currentBlock := ic.Block.Index + revealStart := commitment.CommittedAt + cfg.CommitRevealDelay + if currentBlock < revealStart { + panic("reveal period not started yet") + } + if currentBlock > commitment.RevealDeadline { + panic("reveal deadline passed") + } + + // Verify commitment: hash(amount || nonce || investor) + preimage := make([]byte, 8+len(nonce)+util.Uint160Size) + binary.BigEndian.PutUint64(preimage[:8], amount) + copy(preimage[8:8+len(nonce)], nonce) + copy(preimage[8+len(nonce):], caller.BytesBE()) + + // Hash the preimage using SHA256 + computedHash := hash.Sha256(preimage) + if computedHash != commitment.Commitment { + panic("commitment verification failed") + } + + // Get opportunity + opp := col.getOpportunityInternal(ic.DAO, commitment.OpportunityID) + if opp == nil { + panic("opportunity not found") + } + + if opp.Status != state.OpportunityActive { + panic("opportunity is not active") + } + if currentBlock > opp.InvestmentDeadline { + panic("investment deadline has passed") + } + + // Validate amount + if amount < opp.MinInvestment { + panic("investment below minimum") + } + if amount > opp.MaxInvestment { + panic("investment exceeds maximum") + } + + if opp.MaxParticipants > 0 && opp.CurrentParticipants >= opp.MaxParticipants { + panic("maximum participants reached") + } + + // Whale concentration check + if cfg.WealthConcentration > 0 { + existingInvestment := col.getInvestorTotalInOpportunity(ic.DAO, commitment.VitaID, commitment.OpportunityID) + newTotal := existingInvestment + amount + futurePool := opp.TotalPool + amount + if futurePool > 0 { + concentration := (newTotal * 10000) / futurePool + if concentration > cfg.WealthConcentration { + panic("investment would exceed whale concentration limit") + } + } + } + + // Calculate fee + fee := (amount * cfg.InvestmentFee) / 10000 + netAmount := amount - fee + + // Transfer VTS from investor + if err := col.VTS.transferUnrestricted(ic, caller, col.Hash, new(big.Int).SetUint64(amount), nil); err != nil { + panic("failed to transfer investment amount") + } + + // Send fee to Treasury + if fee > 0 { + if err := col.VTS.transferUnrestricted(ic, col.Hash, nativehashes.Treasury, new(big.Int).SetUint64(fee), nil); err != nil { + panic("failed to transfer fee to treasury") + } + } + + // Create investment record + invID := col.incrementCounter(ic.DAO, makeCollocatioInvCounterKey()) + + inv := &state.Investment{ + ID: invID, + OpportunityID: commitment.OpportunityID, + VitaID: commitment.VitaID, + Investor: caller, + Amount: netAmount, + Status: state.InvestmentActive, + ReturnAmount: 0, + CreatedAt: currentBlock, + UpdatedAt: currentBlock, + } + + col.putInvestment(ic.DAO, inv) + + // Store indexes + ic.DAO.PutStorageItem(col.ID, makeCollocatioInvByOppKey(commitment.OpportunityID, invID), []byte{1}) + ic.DAO.PutStorageItem(col.ID, makeCollocatioInvByInvestorKey(commitment.VitaID, invID), []byte{1}) + + // Update opportunity + opp.CurrentParticipants++ + opp.TotalPool += netAmount + opp.UpdatedAt = currentBlock + col.putOpportunity(ic.DAO, opp) + + // Update commitment + commitment.Status = state.CommitmentRevealed + commitment.RevealedAmount = amount + commitment.InvestmentID = invID + col.putCommitment(ic.DAO, commitment) + + // Update eligibility + col.updateEligibilityOnInvest(ic.DAO, caller, commitment.VitaID, netAmount, currentBlock) + + // Emit events + ic.AddNotification(col.Hash, collocatioCommitmentRevealedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(amount)), + })) + + ic.AddNotification(col.Hash, collocatioInvestmentMadeEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(invID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(commitment.OpportunityID)), + stackitem.NewByteArray(caller.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(netAmount)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(invID)) +} + +// cancelCommitment cancels a pending commitment. +func (col *Collocatio) cancelCommitment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + commitmentID := toUint64(args[0]) + caller := ic.VM.GetCallingScriptHash() + + commitment := col.getCommitmentInternal(ic.DAO, commitmentID) + if commitment == nil { + panic("commitment not found") + } + + if commitment.Investor != caller { + panic("only commitment owner can cancel") + } + + if commitment.Status != state.CommitmentPending { + panic("commitment already processed") + } + + // Update commitment status + commitment.Status = state.CommitmentCanceled + col.putCommitment(ic.DAO, commitment) + + // Emit event + ic.AddNotification(col.Hash, collocatioCommitmentCanceledEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(commitmentID)), + })) + + return stackitem.NewBool(true) +} + +// getCommitment returns commitment details. +func (col *Collocatio) getCommitment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + commitmentID := toUint64(args[0]) + commitment := col.getCommitmentInternal(ic.DAO, commitmentID) + if commitment == nil { + return stackitem.Null{} + } + return commitmentToStackItem(commitment) +} + +// getCommitmentCount returns the total number of commitments. +func (col *Collocatio) getCommitmentCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + count := col.getCounter(ic.DAO, makeCollocatioCommitmentCounterKey()) + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} diff --git a/pkg/core/native/compatibility_test.go b/pkg/core/native/compatibility_test.go index 91ccfc2..90f670e 100644 --- a/pkg/core/native/compatibility_test.go +++ b/pkg/core/native/compatibility_test.go @@ -4,7 +4,7 @@ import ( "testing" "unicode" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/config_registry.go b/pkg/core/native/config_registry.go index 39cdedc..f7ea719 100644 --- a/pkg/core/native/config_registry.go +++ b/pkg/core/native/config_registry.go @@ -1,504 +1,504 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// LOW-003: Configuration Governance -// Centralized configuration registry for managing system-wide parameters. -// All hardcoded values should be moved here for transparent governance. - -// ConfigCategory represents a category of configuration values. -type ConfigCategory uint8 - -const ( - // ConfigCategorySystem covers core system parameters - ConfigCategorySystem ConfigCategory = iota - // ConfigCategoryIdentity covers Vita/identity parameters - ConfigCategoryIdentity - // ConfigCategoryEconomic covers VTS/Tribute/economic parameters - ConfigCategoryEconomic - // ConfigCategoryGovernance covers voting/committee parameters - ConfigCategoryGovernance - // ConfigCategoryHealth covers Salus/healthcare parameters - ConfigCategoryHealth - // ConfigCategoryEducation covers Scire/education parameters - ConfigCategoryEducation - // ConfigCategoryLegal covers Lex/legal parameters - ConfigCategoryLegal - // ConfigCategorySecurity covers security-related parameters - ConfigCategorySecurity -) - -// ConfigEntry represents a single configuration value. -type ConfigEntry struct { - Key string // Unique key (category:name format) - Category ConfigCategory // Category for grouping - ValueType ConfigValueType - CurrentValue []byte // Current value (serialized) - DefaultValue []byte // Default value (for reference) - MinValue []byte // Minimum allowed value (if applicable) - MaxValue []byte // Maximum allowed value (if applicable) - Description string // Human-readable description - RequiresVote bool // Whether changes require governance vote - LastModified uint32 // Block height of last modification - ModifiedBy util.Uint160 // Who made the last modification -} - -// ConfigValueType represents the type of a configuration value. -type ConfigValueType uint8 - -const ( - ConfigTypeUint64 ConfigValueType = iota - ConfigTypeInt64 - ConfigTypeBool - ConfigTypeString - ConfigTypeHash160 - ConfigTypeHash256 - ConfigTypeBytes -) - -// ConfigChangeProposal represents a pending configuration change. -type ConfigChangeProposal struct { - ProposalID uint64 - Key string - NewValue []byte - Proposer util.Uint160 - ProposedAt uint32 - ExpiresAt uint32 - Approvals []util.Uint160 - RequiredVotes uint32 - Status ConfigProposalStatus -} - -// ConfigProposalStatus represents the status of a config change proposal. -type ConfigProposalStatus uint8 - -const ( - ConfigProposalPending ConfigProposalStatus = iota - ConfigProposalApproved - ConfigProposalRejected - ConfigProposalExpired - ConfigProposalExecuted -) - -// Storage prefixes for configuration registry. -const ( - configRegPrefixEntry byte = 0xC0 // key -> ConfigEntry - configRegPrefixByCategory byte = 0xC1 // category + key -> exists - configRegPrefixProposal byte = 0xC2 // proposalID -> ConfigChangeProposal - configRegPrefixProposalCtr byte = 0xCF // -> next proposalID -) - -// Configuration errors. -var ( - ErrConfigNotFound = errors.New("configuration key not found") - ErrConfigInvalidValue = errors.New("invalid configuration value") - ErrConfigOutOfRange = errors.New("configuration value out of allowed range") - ErrConfigRequiresVote = errors.New("configuration change requires governance vote") - ErrConfigProposalExists = errors.New("pending proposal already exists for this key") -) - -// ConfigRegistry manages system-wide configuration. -type ConfigRegistry struct { - contractID int32 -} - -// NewConfigRegistry creates a new configuration registry. -func NewConfigRegistry(contractID int32) *ConfigRegistry { - return &ConfigRegistry{contractID: contractID} -} - -// GetEntry retrieves a configuration entry by key. -func (cr *ConfigRegistry) GetEntry(d *dao.Simple, key string) *ConfigEntry { - storageKey := cr.makeEntryKey(key) - si := d.GetStorageItem(cr.contractID, storageKey) - if si == nil { - return nil - } - return cr.deserializeEntry(si) -} - -// SetEntry stores a configuration entry. -func (cr *ConfigRegistry) SetEntry(d *dao.Simple, entry *ConfigEntry) { - storageKey := cr.makeEntryKey(entry.Key) - data := cr.serializeEntry(entry) - d.PutStorageItem(cr.contractID, storageKey, data) - - // Also index by category - catKey := cr.makeCategoryKey(entry.Category, entry.Key) - d.PutStorageItem(cr.contractID, catKey, []byte{1}) -} - -// GetUint64 retrieves a uint64 configuration value. -func (cr *ConfigRegistry) GetUint64(d *dao.Simple, key string) (uint64, error) { - entry := cr.GetEntry(d, key) - if entry == nil { - return 0, ErrConfigNotFound - } - if entry.ValueType != ConfigTypeUint64 || len(entry.CurrentValue) < 8 { - return 0, ErrConfigInvalidValue - } - return binary.BigEndian.Uint64(entry.CurrentValue), nil -} - -// GetUint64OrDefault retrieves a uint64 value or returns default if not found. -func (cr *ConfigRegistry) GetUint64OrDefault(d *dao.Simple, key string, defaultVal uint64) uint64 { - val, err := cr.GetUint64(d, key) - if err != nil { - return defaultVal - } - return val -} - -// SetUint64 sets a uint64 configuration value. -func (cr *ConfigRegistry) SetUint64(d *dao.Simple, key string, value uint64, modifier util.Uint160, blockHeight uint32) error { - entry := cr.GetEntry(d, key) - if entry == nil { - return ErrConfigNotFound - } - if entry.ValueType != ConfigTypeUint64 { - return ErrConfigInvalidValue - } - - // Validate range - if len(entry.MinValue) >= 8 && len(entry.MaxValue) >= 8 { - minVal := binary.BigEndian.Uint64(entry.MinValue) - maxVal := binary.BigEndian.Uint64(entry.MaxValue) - if value < minVal || value > maxVal { - return ErrConfigOutOfRange - } - } - - entry.CurrentValue = make([]byte, 8) - binary.BigEndian.PutUint64(entry.CurrentValue, value) - entry.LastModified = blockHeight - entry.ModifiedBy = modifier - cr.SetEntry(d, entry) - return nil -} - -// GetBool retrieves a boolean configuration value. -func (cr *ConfigRegistry) GetBool(d *dao.Simple, key string) (bool, error) { - entry := cr.GetEntry(d, key) - if entry == nil { - return false, ErrConfigNotFound - } - if entry.ValueType != ConfigTypeBool || len(entry.CurrentValue) < 1 { - return false, ErrConfigInvalidValue - } - return entry.CurrentValue[0] == 1, nil -} - -// GetBoolOrDefault retrieves a bool value or returns default if not found. -func (cr *ConfigRegistry) GetBoolOrDefault(d *dao.Simple, key string, defaultVal bool) bool { - val, err := cr.GetBool(d, key) - if err != nil { - return defaultVal - } - return val -} - -// GetEntriesByCategory retrieves all entries in a category. -func (cr *ConfigRegistry) GetEntriesByCategory(d *dao.Simple, category ConfigCategory) []*ConfigEntry { - var entries []*ConfigEntry - prefix := []byte{configRegPrefixByCategory, byte(category)} - - d.Seek(cr.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) > 1 { - key := string(k[1:]) // Skip category byte - entry := cr.GetEntry(d, key) - if entry != nil { - entries = append(entries, entry) - } - } - return true - }) - - return entries -} - -// RegisterConfig registers a new configuration entry with defaults. -func (cr *ConfigRegistry) RegisterConfig(d *dao.Simple, key string, category ConfigCategory, valueType ConfigValueType, - defaultValue []byte, minValue, maxValue []byte, description string, requiresVote bool) { - - entry := &ConfigEntry{ - Key: key, - Category: category, - ValueType: valueType, - CurrentValue: defaultValue, - DefaultValue: defaultValue, - MinValue: minValue, - MaxValue: maxValue, - Description: description, - RequiresVote: requiresVote, - } - cr.SetEntry(d, entry) -} - -// ToStackItem converts ConfigEntry to stack item for RPC. -func (e *ConfigEntry) ToStackItem() stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray([]byte(e.Key)), - stackitem.NewBigInteger(big.NewInt(int64(e.Category))), - stackitem.NewBigInteger(big.NewInt(int64(e.ValueType))), - stackitem.NewByteArray(e.CurrentValue), - stackitem.NewByteArray(e.DefaultValue), - stackitem.NewByteArray([]byte(e.Description)), - stackitem.NewBool(e.RequiresVote), - stackitem.NewBigInteger(big.NewInt(int64(e.LastModified))), - }) -} - -// Helper methods for storage keys. -func (cr *ConfigRegistry) makeEntryKey(key string) []byte { - result := make([]byte, 1+len(key)) - result[0] = configRegPrefixEntry - copy(result[1:], key) - return result -} - -func (cr *ConfigRegistry) makeCategoryKey(category ConfigCategory, key string) []byte { - result := make([]byte, 2+len(key)) - result[0] = configRegPrefixByCategory - result[1] = byte(category) - copy(result[2:], key) - return result -} - -// Serialization helpers. -func (cr *ConfigRegistry) serializeEntry(e *ConfigEntry) []byte { - keyBytes := []byte(e.Key) - descBytes := []byte(e.Description) - - size := 4 + len(keyBytes) + // key length + key - 1 + // category - 1 + // value type - 4 + len(e.CurrentValue) + // current value length + value - 4 + len(e.DefaultValue) + // default value length + value - 4 + len(e.MinValue) + // min value length + value - 4 + len(e.MaxValue) + // max value length + value - 4 + len(descBytes) + // description length + desc - 1 + // requires vote - 4 + // last modified - 20 // modified by - - data := make([]byte, size) - offset := 0 - - // Key - binary.BigEndian.PutUint32(data[offset:], uint32(len(keyBytes))) - offset += 4 - copy(data[offset:], keyBytes) - offset += len(keyBytes) - - // Category - data[offset] = byte(e.Category) - offset++ - - // Value type - data[offset] = byte(e.ValueType) - offset++ - - // Current value - binary.BigEndian.PutUint32(data[offset:], uint32(len(e.CurrentValue))) - offset += 4 - copy(data[offset:], e.CurrentValue) - offset += len(e.CurrentValue) - - // Default value - binary.BigEndian.PutUint32(data[offset:], uint32(len(e.DefaultValue))) - offset += 4 - copy(data[offset:], e.DefaultValue) - offset += len(e.DefaultValue) - - // Min value - binary.BigEndian.PutUint32(data[offset:], uint32(len(e.MinValue))) - offset += 4 - copy(data[offset:], e.MinValue) - offset += len(e.MinValue) - - // Max value - binary.BigEndian.PutUint32(data[offset:], uint32(len(e.MaxValue))) - offset += 4 - copy(data[offset:], e.MaxValue) - offset += len(e.MaxValue) - - // Description - binary.BigEndian.PutUint32(data[offset:], uint32(len(descBytes))) - offset += 4 - copy(data[offset:], descBytes) - offset += len(descBytes) - - // Requires vote - if e.RequiresVote { - data[offset] = 1 - } - offset++ - - // Last modified - binary.BigEndian.PutUint32(data[offset:], e.LastModified) - offset += 4 - - // Modified by - copy(data[offset:], e.ModifiedBy.BytesBE()) - - return data -} - -func (cr *ConfigRegistry) deserializeEntry(data []byte) *ConfigEntry { - if len(data) < 10 { - return nil - } - - e := &ConfigEntry{} - offset := 0 - - // Key - keyLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(keyLen) > len(data) { - return nil - } - e.Key = string(data[offset : offset+int(keyLen)]) - offset += int(keyLen) - - // Category - e.Category = ConfigCategory(data[offset]) - offset++ - - // Value type - e.ValueType = ConfigValueType(data[offset]) - offset++ - - // Current value - if offset+4 > len(data) { - return nil - } - valLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(valLen) > len(data) { - return nil - } - e.CurrentValue = make([]byte, valLen) - copy(e.CurrentValue, data[offset:offset+int(valLen)]) - offset += int(valLen) - - // Default value - if offset+4 > len(data) { - return nil - } - defLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(defLen) > len(data) { - return nil - } - e.DefaultValue = make([]byte, defLen) - copy(e.DefaultValue, data[offset:offset+int(defLen)]) - offset += int(defLen) - - // Min value - if offset+4 > len(data) { - return nil - } - minLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(minLen) > len(data) { - return nil - } - e.MinValue = make([]byte, minLen) - copy(e.MinValue, data[offset:offset+int(minLen)]) - offset += int(minLen) - - // Max value - if offset+4 > len(data) { - return nil - } - maxLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(maxLen) > len(data) { - return nil - } - e.MaxValue = make([]byte, maxLen) - copy(e.MaxValue, data[offset:offset+int(maxLen)]) - offset += int(maxLen) - - // Description - if offset+4 > len(data) { - return nil - } - descLen := binary.BigEndian.Uint32(data[offset:]) - offset += 4 - if offset+int(descLen) > len(data) { - return nil - } - e.Description = string(data[offset : offset+int(descLen)]) - offset += int(descLen) - - // Requires vote - if offset >= len(data) { - return nil - } - e.RequiresVote = data[offset] == 1 - offset++ - - // Last modified - if offset+4 > len(data) { - return nil - } - e.LastModified = binary.BigEndian.Uint32(data[offset:]) - offset += 4 - - // Modified by - if offset+20 > len(data) { - return nil - } - e.ModifiedBy, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) - - return e -} - -// StandardConfigKeys defines standard configuration keys used across contracts. -var StandardConfigKeys = struct { - // System - MaxQueryLimit string - DefaultPageSize string - BlockInterval string - - // Security - RecoveryDelay string - RequiredApprovals string - RateLimitBlocks string - ProofExpiryBlocks string - - // Economic - TributeRateMild string - TributeRateSevere string - AsylumQuotaPerYear string - - // Governance - VotingAge string - ProposalQuorum string - ProposalThreshold string -}{ - MaxQueryLimit: "system:max_query_limit", - DefaultPageSize: "system:default_page_size", - BlockInterval: "system:block_interval", - RecoveryDelay: "security:recovery_delay", - RequiredApprovals: "security:required_approvals", - RateLimitBlocks: "security:rate_limit_blocks", - ProofExpiryBlocks: "security:proof_expiry_blocks", - TributeRateMild: "economic:tribute_rate_mild", - TributeRateSevere: "economic:tribute_rate_severe", - AsylumQuotaPerYear: "economic:asylum_quota_per_year", - VotingAge: "governance:voting_age", - ProposalQuorum: "governance:proposal_quorum", - ProposalThreshold: "governance:proposal_threshold", -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// LOW-003: Configuration Governance +// Centralized configuration registry for managing system-wide parameters. +// All hardcoded values should be moved here for transparent governance. + +// ConfigCategory represents a category of configuration values. +type ConfigCategory uint8 + +const ( + // ConfigCategorySystem covers core system parameters + ConfigCategorySystem ConfigCategory = iota + // ConfigCategoryIdentity covers Vita/identity parameters + ConfigCategoryIdentity + // ConfigCategoryEconomic covers VTS/Tribute/economic parameters + ConfigCategoryEconomic + // ConfigCategoryGovernance covers voting/committee parameters + ConfigCategoryGovernance + // ConfigCategoryHealth covers Salus/healthcare parameters + ConfigCategoryHealth + // ConfigCategoryEducation covers Scire/education parameters + ConfigCategoryEducation + // ConfigCategoryLegal covers Lex/legal parameters + ConfigCategoryLegal + // ConfigCategorySecurity covers security-related parameters + ConfigCategorySecurity +) + +// ConfigEntry represents a single configuration value. +type ConfigEntry struct { + Key string // Unique key (category:name format) + Category ConfigCategory // Category for grouping + ValueType ConfigValueType + CurrentValue []byte // Current value (serialized) + DefaultValue []byte // Default value (for reference) + MinValue []byte // Minimum allowed value (if applicable) + MaxValue []byte // Maximum allowed value (if applicable) + Description string // Human-readable description + RequiresVote bool // Whether changes require governance vote + LastModified uint32 // Block height of last modification + ModifiedBy util.Uint160 // Who made the last modification +} + +// ConfigValueType represents the type of a configuration value. +type ConfigValueType uint8 + +const ( + ConfigTypeUint64 ConfigValueType = iota + ConfigTypeInt64 + ConfigTypeBool + ConfigTypeString + ConfigTypeHash160 + ConfigTypeHash256 + ConfigTypeBytes +) + +// ConfigChangeProposal represents a pending configuration change. +type ConfigChangeProposal struct { + ProposalID uint64 + Key string + NewValue []byte + Proposer util.Uint160 + ProposedAt uint32 + ExpiresAt uint32 + Approvals []util.Uint160 + RequiredVotes uint32 + Status ConfigProposalStatus +} + +// ConfigProposalStatus represents the status of a config change proposal. +type ConfigProposalStatus uint8 + +const ( + ConfigProposalPending ConfigProposalStatus = iota + ConfigProposalApproved + ConfigProposalRejected + ConfigProposalExpired + ConfigProposalExecuted +) + +// Storage prefixes for configuration registry. +const ( + configRegPrefixEntry byte = 0xC0 // key -> ConfigEntry + configRegPrefixByCategory byte = 0xC1 // category + key -> exists + configRegPrefixProposal byte = 0xC2 // proposalID -> ConfigChangeProposal + configRegPrefixProposalCtr byte = 0xCF // -> next proposalID +) + +// Configuration errors. +var ( + ErrConfigNotFound = errors.New("configuration key not found") + ErrConfigInvalidValue = errors.New("invalid configuration value") + ErrConfigOutOfRange = errors.New("configuration value out of allowed range") + ErrConfigRequiresVote = errors.New("configuration change requires governance vote") + ErrConfigProposalExists = errors.New("pending proposal already exists for this key") +) + +// ConfigRegistry manages system-wide configuration. +type ConfigRegistry struct { + contractID int32 +} + +// NewConfigRegistry creates a new configuration registry. +func NewConfigRegistry(contractID int32) *ConfigRegistry { + return &ConfigRegistry{contractID: contractID} +} + +// GetEntry retrieves a configuration entry by key. +func (cr *ConfigRegistry) GetEntry(d *dao.Simple, key string) *ConfigEntry { + storageKey := cr.makeEntryKey(key) + si := d.GetStorageItem(cr.contractID, storageKey) + if si == nil { + return nil + } + return cr.deserializeEntry(si) +} + +// SetEntry stores a configuration entry. +func (cr *ConfigRegistry) SetEntry(d *dao.Simple, entry *ConfigEntry) { + storageKey := cr.makeEntryKey(entry.Key) + data := cr.serializeEntry(entry) + d.PutStorageItem(cr.contractID, storageKey, data) + + // Also index by category + catKey := cr.makeCategoryKey(entry.Category, entry.Key) + d.PutStorageItem(cr.contractID, catKey, []byte{1}) +} + +// GetUint64 retrieves a uint64 configuration value. +func (cr *ConfigRegistry) GetUint64(d *dao.Simple, key string) (uint64, error) { + entry := cr.GetEntry(d, key) + if entry == nil { + return 0, ErrConfigNotFound + } + if entry.ValueType != ConfigTypeUint64 || len(entry.CurrentValue) < 8 { + return 0, ErrConfigInvalidValue + } + return binary.BigEndian.Uint64(entry.CurrentValue), nil +} + +// GetUint64OrDefault retrieves a uint64 value or returns default if not found. +func (cr *ConfigRegistry) GetUint64OrDefault(d *dao.Simple, key string, defaultVal uint64) uint64 { + val, err := cr.GetUint64(d, key) + if err != nil { + return defaultVal + } + return val +} + +// SetUint64 sets a uint64 configuration value. +func (cr *ConfigRegistry) SetUint64(d *dao.Simple, key string, value uint64, modifier util.Uint160, blockHeight uint32) error { + entry := cr.GetEntry(d, key) + if entry == nil { + return ErrConfigNotFound + } + if entry.ValueType != ConfigTypeUint64 { + return ErrConfigInvalidValue + } + + // Validate range + if len(entry.MinValue) >= 8 && len(entry.MaxValue) >= 8 { + minVal := binary.BigEndian.Uint64(entry.MinValue) + maxVal := binary.BigEndian.Uint64(entry.MaxValue) + if value < minVal || value > maxVal { + return ErrConfigOutOfRange + } + } + + entry.CurrentValue = make([]byte, 8) + binary.BigEndian.PutUint64(entry.CurrentValue, value) + entry.LastModified = blockHeight + entry.ModifiedBy = modifier + cr.SetEntry(d, entry) + return nil +} + +// GetBool retrieves a boolean configuration value. +func (cr *ConfigRegistry) GetBool(d *dao.Simple, key string) (bool, error) { + entry := cr.GetEntry(d, key) + if entry == nil { + return false, ErrConfigNotFound + } + if entry.ValueType != ConfigTypeBool || len(entry.CurrentValue) < 1 { + return false, ErrConfigInvalidValue + } + return entry.CurrentValue[0] == 1, nil +} + +// GetBoolOrDefault retrieves a bool value or returns default if not found. +func (cr *ConfigRegistry) GetBoolOrDefault(d *dao.Simple, key string, defaultVal bool) bool { + val, err := cr.GetBool(d, key) + if err != nil { + return defaultVal + } + return val +} + +// GetEntriesByCategory retrieves all entries in a category. +func (cr *ConfigRegistry) GetEntriesByCategory(d *dao.Simple, category ConfigCategory) []*ConfigEntry { + var entries []*ConfigEntry + prefix := []byte{configRegPrefixByCategory, byte(category)} + + d.Seek(cr.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) > 1 { + key := string(k[1:]) // Skip category byte + entry := cr.GetEntry(d, key) + if entry != nil { + entries = append(entries, entry) + } + } + return true + }) + + return entries +} + +// RegisterConfig registers a new configuration entry with defaults. +func (cr *ConfigRegistry) RegisterConfig(d *dao.Simple, key string, category ConfigCategory, valueType ConfigValueType, + defaultValue []byte, minValue, maxValue []byte, description string, requiresVote bool) { + + entry := &ConfigEntry{ + Key: key, + Category: category, + ValueType: valueType, + CurrentValue: defaultValue, + DefaultValue: defaultValue, + MinValue: minValue, + MaxValue: maxValue, + Description: description, + RequiresVote: requiresVote, + } + cr.SetEntry(d, entry) +} + +// ToStackItem converts ConfigEntry to stack item for RPC. +func (e *ConfigEntry) ToStackItem() stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray([]byte(e.Key)), + stackitem.NewBigInteger(big.NewInt(int64(e.Category))), + stackitem.NewBigInteger(big.NewInt(int64(e.ValueType))), + stackitem.NewByteArray(e.CurrentValue), + stackitem.NewByteArray(e.DefaultValue), + stackitem.NewByteArray([]byte(e.Description)), + stackitem.NewBool(e.RequiresVote), + stackitem.NewBigInteger(big.NewInt(int64(e.LastModified))), + }) +} + +// Helper methods for storage keys. +func (cr *ConfigRegistry) makeEntryKey(key string) []byte { + result := make([]byte, 1+len(key)) + result[0] = configRegPrefixEntry + copy(result[1:], key) + return result +} + +func (cr *ConfigRegistry) makeCategoryKey(category ConfigCategory, key string) []byte { + result := make([]byte, 2+len(key)) + result[0] = configRegPrefixByCategory + result[1] = byte(category) + copy(result[2:], key) + return result +} + +// Serialization helpers. +func (cr *ConfigRegistry) serializeEntry(e *ConfigEntry) []byte { + keyBytes := []byte(e.Key) + descBytes := []byte(e.Description) + + size := 4 + len(keyBytes) + // key length + key + 1 + // category + 1 + // value type + 4 + len(e.CurrentValue) + // current value length + value + 4 + len(e.DefaultValue) + // default value length + value + 4 + len(e.MinValue) + // min value length + value + 4 + len(e.MaxValue) + // max value length + value + 4 + len(descBytes) + // description length + desc + 1 + // requires vote + 4 + // last modified + 20 // modified by + + data := make([]byte, size) + offset := 0 + + // Key + binary.BigEndian.PutUint32(data[offset:], uint32(len(keyBytes))) + offset += 4 + copy(data[offset:], keyBytes) + offset += len(keyBytes) + + // Category + data[offset] = byte(e.Category) + offset++ + + // Value type + data[offset] = byte(e.ValueType) + offset++ + + // Current value + binary.BigEndian.PutUint32(data[offset:], uint32(len(e.CurrentValue))) + offset += 4 + copy(data[offset:], e.CurrentValue) + offset += len(e.CurrentValue) + + // Default value + binary.BigEndian.PutUint32(data[offset:], uint32(len(e.DefaultValue))) + offset += 4 + copy(data[offset:], e.DefaultValue) + offset += len(e.DefaultValue) + + // Min value + binary.BigEndian.PutUint32(data[offset:], uint32(len(e.MinValue))) + offset += 4 + copy(data[offset:], e.MinValue) + offset += len(e.MinValue) + + // Max value + binary.BigEndian.PutUint32(data[offset:], uint32(len(e.MaxValue))) + offset += 4 + copy(data[offset:], e.MaxValue) + offset += len(e.MaxValue) + + // Description + binary.BigEndian.PutUint32(data[offset:], uint32(len(descBytes))) + offset += 4 + copy(data[offset:], descBytes) + offset += len(descBytes) + + // Requires vote + if e.RequiresVote { + data[offset] = 1 + } + offset++ + + // Last modified + binary.BigEndian.PutUint32(data[offset:], e.LastModified) + offset += 4 + + // Modified by + copy(data[offset:], e.ModifiedBy.BytesBE()) + + return data +} + +func (cr *ConfigRegistry) deserializeEntry(data []byte) *ConfigEntry { + if len(data) < 10 { + return nil + } + + e := &ConfigEntry{} + offset := 0 + + // Key + keyLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(keyLen) > len(data) { + return nil + } + e.Key = string(data[offset : offset+int(keyLen)]) + offset += int(keyLen) + + // Category + e.Category = ConfigCategory(data[offset]) + offset++ + + // Value type + e.ValueType = ConfigValueType(data[offset]) + offset++ + + // Current value + if offset+4 > len(data) { + return nil + } + valLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(valLen) > len(data) { + return nil + } + e.CurrentValue = make([]byte, valLen) + copy(e.CurrentValue, data[offset:offset+int(valLen)]) + offset += int(valLen) + + // Default value + if offset+4 > len(data) { + return nil + } + defLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(defLen) > len(data) { + return nil + } + e.DefaultValue = make([]byte, defLen) + copy(e.DefaultValue, data[offset:offset+int(defLen)]) + offset += int(defLen) + + // Min value + if offset+4 > len(data) { + return nil + } + minLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(minLen) > len(data) { + return nil + } + e.MinValue = make([]byte, minLen) + copy(e.MinValue, data[offset:offset+int(minLen)]) + offset += int(minLen) + + // Max value + if offset+4 > len(data) { + return nil + } + maxLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(maxLen) > len(data) { + return nil + } + e.MaxValue = make([]byte, maxLen) + copy(e.MaxValue, data[offset:offset+int(maxLen)]) + offset += int(maxLen) + + // Description + if offset+4 > len(data) { + return nil + } + descLen := binary.BigEndian.Uint32(data[offset:]) + offset += 4 + if offset+int(descLen) > len(data) { + return nil + } + e.Description = string(data[offset : offset+int(descLen)]) + offset += int(descLen) + + // Requires vote + if offset >= len(data) { + return nil + } + e.RequiresVote = data[offset] == 1 + offset++ + + // Last modified + if offset+4 > len(data) { + return nil + } + e.LastModified = binary.BigEndian.Uint32(data[offset:]) + offset += 4 + + // Modified by + if offset+20 > len(data) { + return nil + } + e.ModifiedBy, _ = util.Uint160DecodeBytesBE(data[offset : offset+20]) + + return e +} + +// StandardConfigKeys defines standard configuration keys used across contracts. +var StandardConfigKeys = struct { + // System + MaxQueryLimit string + DefaultPageSize string + BlockInterval string + + // Security + RecoveryDelay string + RequiredApprovals string + RateLimitBlocks string + ProofExpiryBlocks string + + // Economic + TributeRateMild string + TributeRateSevere string + AsylumQuotaPerYear string + + // Governance + VotingAge string + ProposalQuorum string + ProposalThreshold string +}{ + MaxQueryLimit: "system:max_query_limit", + DefaultPageSize: "system:default_page_size", + BlockInterval: "system:block_interval", + RecoveryDelay: "security:recovery_delay", + RequiredApprovals: "security:required_approvals", + RateLimitBlocks: "security:rate_limit_blocks", + ProofExpiryBlocks: "security:proof_expiry_blocks", + TributeRateMild: "economic:tribute_rate_mild", + TributeRateSevere: "economic:tribute_rate_severe", + AsylumQuotaPerYear: "economic:asylum_quota_per_year", + VotingAge: "governance:voting_age", + ProposalQuorum: "governance:proposal_quorum", + ProposalThreshold: "governance:proposal_threshold", +} diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index 22a99fb..4ba6e53 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -4,18 +4,18 @@ import ( "math/big" "strings" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" ) // Native contract interfaces sufficient for Blockchain functioning and diff --git a/pkg/core/native/contract_test.go b/pkg/core/native/contract_test.go index f5e5604..f3d8201 100644 --- a/pkg/core/native/contract_test.go +++ b/pkg/core/native/contract_test.go @@ -3,7 +3,7 @@ package native import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/crypto.go b/pkg/core/native/crypto.go index 015cf08..b482f12 100644 --- a/pkg/core/native/crypto.go +++ b/pkg/core/native/crypto.go @@ -11,18 +11,18 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/twmb/murmur3" "golang.org/x/crypto/sha3" ) @@ -369,7 +369,7 @@ func scalarFromBytes(bytes []byte, neg bool) (*fr.Element, error) { } // The input bytes are in the LE form, so we can't use fr.Element.SetBytesCanonical as far // as it accepts BE. Confirmed by https://github.com/neo-project/neo/issues/2647#issuecomment-1129849870 - // and by https://github.com/tutus-one/tutus-chain/pull/3043#issuecomment-1733424840. + // and by https://git.marketally.com/tutus-one/tutus-chain/pull/3043#issuecomment-1733424840. v, err := fr.LittleEndian.Element((*[fr.Bytes]byte)(bytes)) if err != nil { return nil, fmt.Errorf("invalid multiplier: failed to decode scalar: %w", err) diff --git a/pkg/core/native/crypto_blspoints.go b/pkg/core/native/crypto_blspoints.go index e3f52ff..d977f22 100644 --- a/pkg/core/native/crypto_blspoints.go +++ b/pkg/core/native/crypto_blspoints.go @@ -6,7 +6,7 @@ import ( "math/big" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // blsPoint is a wrapper around bls12381 point types that must be used as diff --git a/pkg/core/native/crypto_test.go b/pkg/core/native/crypto_test.go index 348119b..fa5ebdc 100644 --- a/pkg/core/native/crypto_test.go +++ b/pkg/core/native/crypto_test.go @@ -10,11 +10,11 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index 8fcbc01..2acb714 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -9,22 +9,22 @@ import ( "slices" "sync/atomic" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Designate represents a designation contract. diff --git a/pkg/core/native/eligere.go b/pkg/core/native/eligere.go index 42d2e15..19609ba 100644 --- a/pkg/core/native/eligere.go +++ b/pkg/core/native/eligere.go @@ -1,950 +1,950 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Storage key prefixes for Eligere. -const ( - eligerePrefixProposal byte = 0x01 // proposalID -> Proposal - eligerePrefixProposalTitle byte = 0x02 // titleHash -> proposalID - eligerePrefixVote byte = 0x03 // proposalID + vitaID -> Vote - eligerePrefixVoterHistory byte = 0x04 // vitaID + proposalID -> exists - eligerePrefixProposalStatus byte = 0x05 // status + proposalID -> exists - eligerePrefixCategory byte = 0x06 // category + proposalID -> exists - eligerePrefixActiveProposals byte = 0x07 // -> serialized []proposalID - eligerePrefixProposalCounter byte = 0xF0 // -> next proposalID - eligerePrefixConfig byte = 0xF1 // -> EligereConfig -) - -// Event names for Eligere. -const ( - ProposalCreatedEvent = "ProposalCreated" - ProposalActivatedEvent = "ProposalActivated" - VoteCastEvent = "VoteCast" - ProposalPassedEvent = "ProposalPassed" - ProposalRejectedEvent = "ProposalRejected" - ProposalExecutedEvent = "ProposalExecuted" - ProposalCancelledEvent = "ProposalCancelled" - ProposalExpiredEvent = "ProposalExpired" -) - -// Errors for Eligere. -var ( - ErrProposalNotFound = errors.New("proposal not found") - ErrProposalNotActive = errors.New("proposal is not active for voting") - ErrAlreadyVoted = errors.New("already voted on this proposal") - ErrVotingNotStarted = errors.New("voting period has not started") - ErrVotingEnded = errors.New("voting period has ended") - ErrNotProposer = errors.New("caller is not the proposer") - ErrProposalTitleExists = errors.New("proposal title already exists") - ErrInvalidCategory = errors.New("invalid proposal category") - ErrQuorumNotMet = errors.New("quorum not met") - ErrThresholdNotMet = errors.New("threshold not met") - ErrProposalNotPassed = errors.New("proposal has not passed") - ErrExecutionDelayNotPassed = errors.New("execution delay has not passed") - ErrProposalAlreadyExecuted = errors.New("proposal already executed") - ErrNoVitaToken = errors.New("caller must have active Vita") - ErrVotingRightRestricted = errors.New("voting right is restricted") - ErrUnderVotingAge = errors.New("voter is under voting age") - ErrInvalidVotingPeriod = errors.New("invalid voting period") - ErrTitleTooLong = errors.New("title too long (max 128 chars)") - ErrInvalidVoteChoice = errors.New("invalid vote choice") - ErrVotingPeriodNotEnded = errors.New("voting period has not ended") - ErrProposalAlreadyFinalized = errors.New("proposal already finalized") - ErrNotCommitteeOrProposer = errors.New("only proposer or committee can cancel") - ErrCannotCancelFinalized = errors.New("cannot cancel finalized proposal") -) - -// Eligere represents the democratic voting native contract. -type Eligere struct { - interop.ContractMD - - Tutus ITutus - Vita IVita - RoleRegistry IRoleRegistry - Lex ILex - Annos IAnnos -} - -// EligereCache contains cached data for performance. -type EligereCache struct { - proposalCount uint64 -} - -// Copy implements dao.NativeContractCache. -func (c *EligereCache) Copy() dao.NativeContractCache { - return &EligereCache{ - proposalCount: c.proposalCount, - } -} - -// newEligere creates a new Eligere native contract. -func newEligere() *Eligere { - e := &Eligere{} - - e.ContractMD = *interop.NewContractMD(nativenames.Eligere, nativeids.Eligere) - defer e.BuildHFSpecificMD(e.ActiveIn()) - - // ============ Proposal Management Methods ============ - - desc := NewDescriptor("createProposal", smartcontract.IntegerType, - manifest.NewParameter("title", smartcontract.StringType), - manifest.NewParameter("contentHash", smartcontract.ByteArrayType), - manifest.NewParameter("category", smartcontract.IntegerType), - manifest.NewParameter("votingStartsAt", smartcontract.IntegerType), - manifest.NewParameter("votingEndsAt", smartcontract.IntegerType), - manifest.NewParameter("targetContract", smartcontract.Hash160Type), - manifest.NewParameter("targetMethod", smartcontract.StringType), - manifest.NewParameter("targetParams", smartcontract.ByteArrayType)) - md := NewMethodAndPrice(e.createProposal, 1<<15, callflag.States|callflag.AllowNotify) - e.AddMethod(md, desc) - - desc = NewDescriptor("cancelProposal", smartcontract.BoolType, - manifest.NewParameter("proposalID", smartcontract.IntegerType)) - md = NewMethodAndPrice(e.cancelProposal, 1<<15, callflag.States|callflag.AllowNotify) - e.AddMethod(md, desc) - - // ============ Voting Methods ============ - - desc = NewDescriptor("vote", smartcontract.BoolType, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("choice", smartcontract.IntegerType)) - md = NewMethodAndPrice(e.vote, 1<<15, callflag.States|callflag.AllowNotify) - e.AddMethod(md, desc) - - // ============ Tallying & Execution Methods ============ - - desc = NewDescriptor("tallyVotes", smartcontract.BoolType, - manifest.NewParameter("proposalID", smartcontract.IntegerType)) - md = NewMethodAndPrice(e.tallyVotes, 1<<15, callflag.States|callflag.AllowNotify) - e.AddMethod(md, desc) - - desc = NewDescriptor("executeProposal", smartcontract.BoolType, - manifest.NewParameter("proposalID", smartcontract.IntegerType)) - md = NewMethodAndPrice(e.executeProposal, 1<<17, callflag.All) - e.AddMethod(md, desc) - - // ============ Query Methods ============ - - desc = NewDescriptor("getProposal", smartcontract.ArrayType, - manifest.NewParameter("proposalID", smartcontract.IntegerType)) - md = NewMethodAndPrice(e.getProposal, 1<<15, callflag.ReadStates) - e.AddMethod(md, desc) - - desc = NewDescriptor("hasVoted", smartcontract.BoolType, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("voter", smartcontract.Hash160Type)) - md = NewMethodAndPrice(e.hasVoted, 1<<15, callflag.ReadStates) - e.AddMethod(md, desc) - - desc = NewDescriptor("getVote", smartcontract.ArrayType, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("voter", smartcontract.Hash160Type)) - md = NewMethodAndPrice(e.getVote, 1<<15, callflag.ReadStates) - e.AddMethod(md, desc) - - desc = NewDescriptor("getProposalCount", smartcontract.IntegerType) - md = NewMethodAndPrice(e.getProposalCount, 1<<15, callflag.ReadStates) - e.AddMethod(md, desc) - - desc = NewDescriptor("getConfig", smartcontract.ArrayType) - md = NewMethodAndPrice(e.getConfig, 1<<15, callflag.ReadStates) - e.AddMethod(md, desc) - - // ============ Events ============ - - eDesc := NewEventDescriptor(ProposalCreatedEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("title", smartcontract.StringType), - manifest.NewParameter("category", smartcontract.IntegerType), - manifest.NewParameter("proposer", smartcontract.Hash160Type), - manifest.NewParameter("votingEndsAt", smartcontract.IntegerType)) - e.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(ProposalActivatedEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType)) - e.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VoteCastEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("voterVitaID", smartcontract.IntegerType), - manifest.NewParameter("choice", smartcontract.IntegerType)) - e.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(ProposalPassedEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("supportPercent", smartcontract.IntegerType), - manifest.NewParameter("totalVotes", smartcontract.IntegerType)) - e.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(ProposalRejectedEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("supportPercent", smartcontract.IntegerType)) - e.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(ProposalExecutedEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("executor", smartcontract.Hash160Type), - manifest.NewParameter("executedAt", smartcontract.IntegerType)) - e.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(ProposalCancelledEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("cancelledBy", smartcontract.Hash160Type)) - e.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(ProposalExpiredEvent, - manifest.NewParameter("proposalID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - e.AddEvent(NewEvent(eDesc)) - - return e -} - -// Initialize implements the Contract interface. -func (e *Eligere) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != e.ActiveIn() { - return nil - } - - // Initialize cache - cache := &EligereCache{proposalCount: 0} - ic.DAO.SetCache(e.ID, cache) - - // Initialize config with defaults - cfg := state.DefaultEligereConfig() - if err := e.putConfig(ic.DAO, cfg); err != nil { - return err - } - - // Initialize proposal counter - setIntWithKey(e.ID, ic.DAO, []byte{eligerePrefixProposalCounter}, 0) - - return nil -} - -// InitializeCache implements the Contract interface. -func (e *Eligere) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cache := &EligereCache{ - proposalCount: e.getProposalCounter(d), - } - d.SetCache(e.ID, cache) - return nil -} - -// OnPersist implements the Contract interface. -func (e *Eligere) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (e *Eligere) PostPersist(ic *interop.Context) error { - return nil -} - -// Metadata returns contract metadata. -func (e *Eligere) Metadata() *interop.ContractMD { - return &e.ContractMD -} - -// Address returns the contract's script hash. -func (e *Eligere) Address() util.Uint160 { - return e.Hash -} - -// GetProposalInternal returns a proposal by ID (for cross-native access). -func (e *Eligere) GetProposalInternal(d *dao.Simple, proposalID uint64) *state.Proposal { - return e.getProposalInternal(d, proposalID) -} - -// ActiveIn implements the Contract interface. -func (e *Eligere) ActiveIn() *config.Hardfork { - return nil // Active from genesis -} - -// ============ Storage Helpers ============ - -func (e *Eligere) makeProposalKey(proposalID uint64) []byte { - key := make([]byte, 1+8) - key[0] = eligerePrefixProposal - binary.BigEndian.PutUint64(key[1:], proposalID) - return key -} - -func (e *Eligere) makeTitleKey(title string) []byte { - h := hash.Hash160([]byte(title)) - key := make([]byte, 1+20) - key[0] = eligerePrefixProposalTitle - copy(key[1:], h.BytesBE()) - return key -} - -func (e *Eligere) makeVoteKey(proposalID, vitaID uint64) []byte { - key := make([]byte, 1+8+8) - key[0] = eligerePrefixVote - binary.BigEndian.PutUint64(key[1:9], proposalID) - binary.BigEndian.PutUint64(key[9:], vitaID) - return key -} - -func (e *Eligere) makeVoterHistoryKey(vitaID, proposalID uint64) []byte { - key := make([]byte, 1+8+8) - key[0] = eligerePrefixVoterHistory - binary.BigEndian.PutUint64(key[1:9], vitaID) - binary.BigEndian.PutUint64(key[9:], proposalID) - return key -} - -func (e *Eligere) makeStatusIndexKey(status state.ProposalStatus, proposalID uint64) []byte { - key := make([]byte, 1+1+8) - key[0] = eligerePrefixProposalStatus - key[1] = byte(status) - binary.BigEndian.PutUint64(key[2:], proposalID) - return key -} - -func (e *Eligere) makeCategoryIndexKey(category state.ProposalCategory, proposalID uint64) []byte { - key := make([]byte, 1+1+8) - key[0] = eligerePrefixCategory - key[1] = byte(category) - binary.BigEndian.PutUint64(key[2:], proposalID) - return key -} - -// ============ Proposal Storage ============ - -func (e *Eligere) getProposalInternal(d *dao.Simple, proposalID uint64) *state.Proposal { - key := e.makeProposalKey(proposalID) - proposal := &state.Proposal{} - err := getConvertibleFromDAO(e.ID, d, key, proposal) - if err != nil { - return nil - } - return proposal -} - -func (e *Eligere) putProposal(d *dao.Simple, proposal *state.Proposal) error { - key := e.makeProposalKey(proposal.ID) - return putConvertibleToDAO(e.ID, d, key, proposal) -} - -func (e *Eligere) titleExists(d *dao.Simple, title string) bool { - key := e.makeTitleKey(title) - si := d.GetStorageItem(e.ID, key) - return si != nil -} - -func (e *Eligere) putTitleIndex(d *dao.Simple, title string, proposalID uint64) { - key := e.makeTitleKey(title) - val := make([]byte, 8) - binary.BigEndian.PutUint64(val, proposalID) - d.PutStorageItem(e.ID, key, val) -} - -// ============ Vote Storage ============ - -func (e *Eligere) getVoteInternal(d *dao.Simple, proposalID, vitaID uint64) *state.Vote { - key := e.makeVoteKey(proposalID, vitaID) - vote := &state.Vote{} - err := getConvertibleFromDAO(e.ID, d, key, vote) - if err != nil { - return nil - } - return vote -} - -func (e *Eligere) putVote(d *dao.Simple, vote *state.Vote) error { - key := e.makeVoteKey(vote.ProposalID, vote.VoterVitaID) - return putConvertibleToDAO(e.ID, d, key, vote) -} - -func (e *Eligere) hasVotedInternal(d *dao.Simple, proposalID, vitaID uint64) bool { - key := e.makeVoteKey(proposalID, vitaID) - si := d.GetStorageItem(e.ID, key) - return si != nil -} - -func (e *Eligere) putVoterHistory(d *dao.Simple, vitaID, proposalID uint64) { - key := e.makeVoterHistoryKey(vitaID, proposalID) - d.PutStorageItem(e.ID, key, []byte{1}) -} - -// ============ Config Storage ============ - -func (e *Eligere) getConfigInternal(d *dao.Simple) *state.EligereConfig { - key := []byte{eligerePrefixConfig} - cfg := &state.EligereConfig{} - err := getConvertibleFromDAO(e.ID, d, key, cfg) - if err != nil { - return state.DefaultEligereConfig() - } - return cfg -} - -func (e *Eligere) putConfig(d *dao.Simple, cfg *state.EligereConfig) error { - key := []byte{eligerePrefixConfig} - return putConvertibleToDAO(e.ID, d, key, cfg) -} - -// ============ Counter ============ - -func (e *Eligere) getProposalCounter(d *dao.Simple) uint64 { - key := []byte{eligerePrefixProposalCounter} - return uint64(getIntWithKey(e.ID, d, key)) -} - -func (e *Eligere) getAndIncrementProposalCounter(d *dao.Simple) uint64 { - key := []byte{eligerePrefixProposalCounter} - current := getIntWithKey(e.ID, d, key) - setIntWithKey(e.ID, d, key, current+1) - return uint64(current + 1) -} - -// ============ Status Index ============ - -func (e *Eligere) updateStatusIndex(d *dao.Simple, proposalID uint64, oldStatus, newStatus state.ProposalStatus) { - // Remove from old status index - if oldStatus != newStatus { - oldKey := e.makeStatusIndexKey(oldStatus, proposalID) - d.DeleteStorageItem(e.ID, oldKey) - } - // Add to new status index - newKey := e.makeStatusIndexKey(newStatus, proposalID) - d.PutStorageItem(e.ID, newKey, []byte{1}) -} - -// ============ Authorization Helpers ============ - -func (e *Eligere) checkCommittee(ic *interop.Context) bool { - if e.Tutus == nil { - return false - } - return e.Tutus.CheckCommittee(ic) -} - -func (e *Eligere) hasLegislatorRole(d *dao.Simple, addr util.Uint160, blockHeight uint32) bool { - if e.RoleRegistry == nil { - return false - } - return e.RoleRegistry.HasRoleInternal(d, addr, RoleLegislator, blockHeight) -} - -// ============ Contract Methods ============ - -// createProposal creates a new democratic proposal. -func (e *Eligere) createProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { - title := toString(args[0]) - contentHashBytes := toBytes(args[1]) - category := state.ProposalCategory(toBigInt(args[2]).Uint64()) - votingStartsAt := uint32(toBigInt(args[3]).Uint64()) - votingEndsAt := uint32(toBigInt(args[4]).Uint64()) - targetContract := toUint160(args[5]) - targetMethod := toString(args[6]) - targetParams := toBytes(args[7]) - - caller := ic.VM.GetCallingScriptHash() - - // Validate caller has active Vita - token, err := e.Vita.GetTokenByOwner(ic.DAO, caller) - if err != nil || token == nil || token.Status != state.TokenStatusActive { - panic(ErrNoVitaToken) - } - - // Check voting right is not restricted (via Lex) - if e.Lex != nil && !e.Lex.HasRightInternal(ic.DAO, caller, state.RightVote, ic.Block.Index) { - panic(ErrVotingRightRestricted) - } - - // For law/constitutional amendments, require legislator role or committee - if category == state.ProposalCategoryLawAmendment || category == state.ProposalCategoryConstitutional { - if !e.hasLegislatorRole(ic.DAO, caller, ic.Block.Index) && !e.checkCommittee(ic) { - panic("legislative authority required for law amendments") - } - } - - // Validate inputs - if len(title) > 128 { - panic(ErrTitleTooLong) - } - if len(title) == 0 { - panic("title cannot be empty") - } - if category < state.ProposalCategoryLawAmendment || category > state.ProposalCategoryReferendum { - panic(ErrInvalidCategory) - } - - // Check title uniqueness - if e.titleExists(ic.DAO, title) { - panic(ErrProposalTitleExists) - } - - // Validate voting period - cfg := e.getConfigInternal(ic.DAO) - if votingStartsAt < ic.Block.Index { - votingStartsAt = ic.Block.Index // Can't start in past - } - duration := votingEndsAt - votingStartsAt - if duration < cfg.MinVotingPeriod || duration > cfg.MaxVotingPeriod { - panic(ErrInvalidVotingPeriod) - } - - // Determine threshold based on category - threshold := cfg.StandardThreshold - if category == state.ProposalCategoryConstitutional { - threshold = cfg.ConstitutionalThreshold - } - - // Get next proposal ID - proposalID := e.getAndIncrementProposalCounter(ic.DAO) - - // Create proposal - var contentHash util.Uint256 - if len(contentHashBytes) == 32 { - copy(contentHash[:], contentHashBytes) - } - - proposal := &state.Proposal{ - ID: proposalID, - Title: title, - ContentHash: contentHash, - Category: category, - Proposer: caller, - ProposerVitaID: token.TokenID, - CreatedAt: ic.Block.Index, - VotingStartsAt: votingStartsAt, - VotingEndsAt: votingEndsAt, - ExecutionDelay: cfg.DefaultExecutionDelay, - QuorumPercent: cfg.DefaultQuorum, - ThresholdPercent: threshold, - Status: state.ProposalStatusDraft, - TargetContract: targetContract, - TargetMethod: targetMethod, - TargetParams: targetParams, - } - - // If voting starts now, activate immediately - if votingStartsAt <= ic.Block.Index { - proposal.Status = state.ProposalStatusActive - } - - // Store proposal - if err := e.putProposal(ic.DAO, proposal); err != nil { - panic(err) - } - - // Store indexes - e.putTitleIndex(ic.DAO, title, proposalID) - e.updateStatusIndex(ic.DAO, proposalID, state.ProposalStatusDraft, proposal.Status) - - // Store category index - catKey := e.makeCategoryIndexKey(category, proposalID) - ic.DAO.PutStorageItem(e.ID, catKey, []byte{1}) - - // Emit event - ic.AddNotification(e.Hash, ProposalCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(proposalID))), - stackitem.NewByteArray([]byte(title)), - stackitem.NewBigInteger(big.NewInt(int64(category))), - stackitem.NewByteArray(caller.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(votingEndsAt))), - })) - - return stackitem.NewBigInteger(big.NewInt(int64(proposalID))) -} - -// cancelProposal cancels a proposal (proposer or committee only). -func (e *Eligere) cancelProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { - proposalID := toBigInt(args[0]).Uint64() - - caller := ic.VM.GetCallingScriptHash() - - proposal := e.getProposalInternal(ic.DAO, proposalID) - if proposal == nil { - panic(ErrProposalNotFound) - } - - // Check authorization: must be proposer or committee - isProposer := proposal.Proposer.Equals(caller) - isCommittee := e.checkCommittee(ic) - if !isProposer && !isCommittee { - panic(ErrNotCommitteeOrProposer) - } - - // Can only cancel draft or active proposals - if proposal.Status != state.ProposalStatusDraft && proposal.Status != state.ProposalStatusActive { - panic(ErrCannotCancelFinalized) - } - - oldStatus := proposal.Status - proposal.Status = state.ProposalStatusCancelled - - // Update proposal - if err := e.putProposal(ic.DAO, proposal); err != nil { - panic(err) - } - - // Update status index - e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) - - // Emit event - ic.AddNotification(e.Hash, ProposalCancelledEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(proposalID))), - stackitem.NewByteArray(caller.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -// vote casts a vote on a proposal. -func (e *Eligere) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item { - proposalID := toBigInt(args[0]).Uint64() - choice := state.VoteChoice(toBigInt(args[1]).Uint64()) - - caller := ic.VM.GetCallingScriptHash() - - // Validate vote choice - if choice > state.VoteChoiceNo { - panic(ErrInvalidVoteChoice) - } - - // Validate caller has active Vita - token, err := e.Vita.GetTokenByOwner(ic.DAO, caller) - if err != nil || token == nil || token.Status != state.TokenStatusActive { - panic(ErrNoVitaToken) - } - - // Check voting right is not restricted - if e.Lex != nil && !e.Lex.HasRightInternal(ic.DAO, caller, state.RightVote, ic.Block.Index) { - panic(ErrVotingRightRestricted) - } - - // Check voter is of voting age (18+) - if e.Annos != nil && !e.Annos.IsVotingAgeInternal(ic.DAO, caller, ic.Block.Timestamp) { - panic(ErrUnderVotingAge) - } - - // Get proposal - proposal := e.getProposalInternal(ic.DAO, proposalID) - if proposal == nil { - panic(ErrProposalNotFound) - } - - // Check proposal is active - if proposal.Status != state.ProposalStatusActive { - // Auto-activate if draft and voting period has started - if proposal.Status == state.ProposalStatusDraft && ic.Block.Index >= proposal.VotingStartsAt { - proposal.Status = state.ProposalStatusActive - e.updateStatusIndex(ic.DAO, proposalID, state.ProposalStatusDraft, state.ProposalStatusActive) - } else { - panic(ErrProposalNotActive) - } - } - - // Check voting period - if ic.Block.Index < proposal.VotingStartsAt { - panic(ErrVotingNotStarted) - } - if ic.Block.Index > proposal.VotingEndsAt { - panic(ErrVotingEnded) - } - - // Check not already voted (using Vita ID for one-person-one-vote) - if e.hasVotedInternal(ic.DAO, proposalID, token.TokenID) { - panic(ErrAlreadyVoted) - } - - // Create vote record - voteRecord := &state.Vote{ - ProposalID: proposalID, - VoterVitaID: token.TokenID, - Voter: caller, - Choice: choice, - VotedAt: ic.Block.Index, - Weight: 1, // Equal voting weight - } - - // Store vote - if err := e.putVote(ic.DAO, voteRecord); err != nil { - panic(err) - } - - // Store voter history - e.putVoterHistory(ic.DAO, token.TokenID, proposalID) - - // Update vote counts (incremental tallying) - proposal.TotalVotes++ - switch choice { - case state.VoteChoiceYes: - proposal.YesVotes++ - case state.VoteChoiceNo: - proposal.NoVotes++ - case state.VoteChoiceAbstain: - proposal.AbstainVotes++ - } - - // Update proposal - if err := e.putProposal(ic.DAO, proposal); err != nil { - panic(err) - } - - // Emit event - ic.AddNotification(e.Hash, VoteCastEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(proposalID))), - stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))), - stackitem.NewBigInteger(big.NewInt(int64(choice))), - })) - - return stackitem.NewBool(true) -} - -// tallyVotes finalizes voting after the deadline. -func (e *Eligere) tallyVotes(ic *interop.Context, args []stackitem.Item) stackitem.Item { - proposalID := toBigInt(args[0]).Uint64() - - proposal := e.getProposalInternal(ic.DAO, proposalID) - if proposal == nil { - panic(ErrProposalNotFound) - } - - // Must be after voting period - if ic.Block.Index <= proposal.VotingEndsAt { - panic(ErrVotingPeriodNotEnded) - } - - // Must still be active - if proposal.Status != state.ProposalStatusActive { - panic(ErrProposalAlreadyFinalized) - } - - // Get total eligible voters for quorum calculation - // Use Vita token count as the voter base - totalVoters := e.Vita.GetTotalTokenCount(ic.DAO) - if totalVoters == 0 { - totalVoters = 1 // Avoid division by zero - } - - oldStatus := proposal.Status - - // Check quorum (total votes including abstentions) - quorumRequired := (totalVoters * uint64(proposal.QuorumPercent)) / 100 - if proposal.TotalVotes < quorumRequired { - proposal.Status = state.ProposalStatusExpired - - if err := e.putProposal(ic.DAO, proposal); err != nil { - panic(err) - } - e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) - - ic.AddNotification(e.Hash, ProposalExpiredEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(proposalID))), - stackitem.NewByteArray([]byte("quorum not met")), - })) - return stackitem.NewBool(false) - } - - // Check threshold (yes votes vs yes+no, abstentions don't count toward threshold) - totalDecisiveVotes := proposal.YesVotes + proposal.NoVotes - var supportPercent uint64 = 0 - if totalDecisiveVotes > 0 { - supportPercent = (proposal.YesVotes * 100) / totalDecisiveVotes - } - - if totalDecisiveVotes == 0 || supportPercent < uint64(proposal.ThresholdPercent) { - proposal.Status = state.ProposalStatusRejected - - if err := e.putProposal(ic.DAO, proposal); err != nil { - panic(err) - } - e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) - - ic.AddNotification(e.Hash, ProposalRejectedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(proposalID))), - stackitem.NewBigInteger(big.NewInt(int64(supportPercent))), - })) - return stackitem.NewBool(false) - } - - // Proposal passed - proposal.Status = state.ProposalStatusPassed - - if err := e.putProposal(ic.DAO, proposal); err != nil { - panic(err) - } - e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) - - ic.AddNotification(e.Hash, ProposalPassedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(proposalID))), - stackitem.NewBigInteger(big.NewInt(int64(supportPercent))), - stackitem.NewBigInteger(big.NewInt(int64(proposal.TotalVotes))), - })) - - return stackitem.NewBool(true) -} - -// executeProposal executes a passed proposal after the execution delay. -func (e *Eligere) executeProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { - proposalID := toBigInt(args[0]).Uint64() - - caller := ic.VM.GetCallingScriptHash() - - proposal := e.getProposalInternal(ic.DAO, proposalID) - if proposal == nil { - panic(ErrProposalNotFound) - } - - // Must have passed - if proposal.Status != state.ProposalStatusPassed { - panic(ErrProposalNotPassed) - } - - // Check execution delay has passed - executionBlock := proposal.VotingEndsAt + proposal.ExecutionDelay - if ic.Block.Index < executionBlock { - panic(ErrExecutionDelayNotPassed) - } - - // Already executed check - if proposal.ExecutedAt > 0 { - panic(ErrProposalAlreadyExecuted) - } - - oldStatus := proposal.Status - - // Execute based on category - switch proposal.Category { - case state.ProposalCategoryLawAmendment: - // Call Lex.ratifyAmendment if Lex is available - if e.Lex != nil { - e.executeLawAmendment(ic, proposal) - } - case state.ProposalCategoryConstitutional: - // Constitutional amendments also go through Lex - if e.Lex != nil { - e.executeLawAmendment(ic, proposal) - } - case state.ProposalCategoryGovernanceAction: - // Governance actions may call target contract - // Implementation depends on specific action - case state.ProposalCategoryInvestment: - // Investment proposals - future integration - case state.ProposalCategoryReferendum: - // Referendums are advisory, no automatic execution - } - - // Update proposal status - proposal.Status = state.ProposalStatusExecuted - proposal.ExecutedAt = ic.Block.Index - proposal.ExecutedBy = caller - - if err := e.putProposal(ic.DAO, proposal); err != nil { - panic(err) - } - e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) - - // Emit event - ic.AddNotification(e.Hash, ProposalExecutedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(proposalID))), - stackitem.NewByteArray(caller.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))), - })) - - return stackitem.NewBool(true) -} - -// executeLawAmendment calls Lex to ratify a law amendment. -func (e *Eligere) executeLawAmendment(ic *interop.Context, proposal *state.Proposal) { - // The Lex contract's ratifyAmendment method will be called - // This creates or updates a law based on the proposal - if e.Lex != nil { - // All amendments create Federal-level laws - // Constitutional rights (RightLife, RightLiberty, etc.) are immutable in code - // Constitutional proposals require 67% supermajority but still create Federal laws - lawCategory := state.LawCategoryFederal - - // Call Lex to ratify the amendment - e.Lex.RatifyAmendmentInternal(ic, proposal.ID, proposal.ContentHash, lawCategory, 0) - } -} - -// ============ Query Methods ============ - -func (e *Eligere) getProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { - proposalID := toBigInt(args[0]).Uint64() - - proposal := e.getProposalInternal(ic.DAO, proposalID) - if proposal == nil { - return stackitem.Null{} - } - - item, err := proposal.ToStackItem() - if err != nil { - return stackitem.Null{} - } - return item -} - -func (e *Eligere) hasVoted(ic *interop.Context, args []stackitem.Item) stackitem.Item { - proposalID := toBigInt(args[0]).Uint64() - voter := toUint160(args[1]) - - // Get voter's Vita token - token, err := e.Vita.GetTokenByOwner(ic.DAO, voter) - if err != nil || token == nil { - return stackitem.NewBool(false) - } - - return stackitem.NewBool(e.hasVotedInternal(ic.DAO, proposalID, token.TokenID)) -} - -func (e *Eligere) getVote(ic *interop.Context, args []stackitem.Item) stackitem.Item { - proposalID := toBigInt(args[0]).Uint64() - voter := toUint160(args[1]) - - // Get voter's Vita token - token, err := e.Vita.GetTokenByOwner(ic.DAO, voter) - if err != nil || token == nil { - return stackitem.Null{} - } - - vote := e.getVoteInternal(ic.DAO, proposalID, token.TokenID) - if vote == nil { - return stackitem.Null{} - } - - item, err := vote.ToStackItem() - if err != nil { - return stackitem.Null{} - } - return item -} - -func (e *Eligere) getProposalCount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - key := []byte{eligerePrefixProposalCounter} - count := getIntWithKey(e.ID, ic.DAO, key) - return stackitem.NewBigInteger(big.NewInt(count)) -} - -func (e *Eligere) getConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cfg := e.getConfigInternal(ic.DAO) - item, err := cfg.ToStackItem() - if err != nil { - return stackitem.Null{} - } - return item -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Storage key prefixes for Eligere. +const ( + eligerePrefixProposal byte = 0x01 // proposalID -> Proposal + eligerePrefixProposalTitle byte = 0x02 // titleHash -> proposalID + eligerePrefixVote byte = 0x03 // proposalID + vitaID -> Vote + eligerePrefixVoterHistory byte = 0x04 // vitaID + proposalID -> exists + eligerePrefixProposalStatus byte = 0x05 // status + proposalID -> exists + eligerePrefixCategory byte = 0x06 // category + proposalID -> exists + eligerePrefixActiveProposals byte = 0x07 // -> serialized []proposalID + eligerePrefixProposalCounter byte = 0xF0 // -> next proposalID + eligerePrefixConfig byte = 0xF1 // -> EligereConfig +) + +// Event names for Eligere. +const ( + ProposalCreatedEvent = "ProposalCreated" + ProposalActivatedEvent = "ProposalActivated" + VoteCastEvent = "VoteCast" + ProposalPassedEvent = "ProposalPassed" + ProposalRejectedEvent = "ProposalRejected" + ProposalExecutedEvent = "ProposalExecuted" + ProposalCancelledEvent = "ProposalCancelled" + ProposalExpiredEvent = "ProposalExpired" +) + +// Errors for Eligere. +var ( + ErrProposalNotFound = errors.New("proposal not found") + ErrProposalNotActive = errors.New("proposal is not active for voting") + ErrAlreadyVoted = errors.New("already voted on this proposal") + ErrVotingNotStarted = errors.New("voting period has not started") + ErrVotingEnded = errors.New("voting period has ended") + ErrNotProposer = errors.New("caller is not the proposer") + ErrProposalTitleExists = errors.New("proposal title already exists") + ErrInvalidCategory = errors.New("invalid proposal category") + ErrQuorumNotMet = errors.New("quorum not met") + ErrThresholdNotMet = errors.New("threshold not met") + ErrProposalNotPassed = errors.New("proposal has not passed") + ErrExecutionDelayNotPassed = errors.New("execution delay has not passed") + ErrProposalAlreadyExecuted = errors.New("proposal already executed") + ErrNoVitaToken = errors.New("caller must have active Vita") + ErrVotingRightRestricted = errors.New("voting right is restricted") + ErrUnderVotingAge = errors.New("voter is under voting age") + ErrInvalidVotingPeriod = errors.New("invalid voting period") + ErrTitleTooLong = errors.New("title too long (max 128 chars)") + ErrInvalidVoteChoice = errors.New("invalid vote choice") + ErrVotingPeriodNotEnded = errors.New("voting period has not ended") + ErrProposalAlreadyFinalized = errors.New("proposal already finalized") + ErrNotCommitteeOrProposer = errors.New("only proposer or committee can cancel") + ErrCannotCancelFinalized = errors.New("cannot cancel finalized proposal") +) + +// Eligere represents the democratic voting native contract. +type Eligere struct { + interop.ContractMD + + Tutus ITutus + Vita IVita + RoleRegistry IRoleRegistry + Lex ILex + Annos IAnnos +} + +// EligereCache contains cached data for performance. +type EligereCache struct { + proposalCount uint64 +} + +// Copy implements dao.NativeContractCache. +func (c *EligereCache) Copy() dao.NativeContractCache { + return &EligereCache{ + proposalCount: c.proposalCount, + } +} + +// newEligere creates a new Eligere native contract. +func newEligere() *Eligere { + e := &Eligere{} + + e.ContractMD = *interop.NewContractMD(nativenames.Eligere, nativeids.Eligere) + defer e.BuildHFSpecificMD(e.ActiveIn()) + + // ============ Proposal Management Methods ============ + + desc := NewDescriptor("createProposal", smartcontract.IntegerType, + manifest.NewParameter("title", smartcontract.StringType), + manifest.NewParameter("contentHash", smartcontract.ByteArrayType), + manifest.NewParameter("category", smartcontract.IntegerType), + manifest.NewParameter("votingStartsAt", smartcontract.IntegerType), + manifest.NewParameter("votingEndsAt", smartcontract.IntegerType), + manifest.NewParameter("targetContract", smartcontract.Hash160Type), + manifest.NewParameter("targetMethod", smartcontract.StringType), + manifest.NewParameter("targetParams", smartcontract.ByteArrayType)) + md := NewMethodAndPrice(e.createProposal, 1<<15, callflag.States|callflag.AllowNotify) + e.AddMethod(md, desc) + + desc = NewDescriptor("cancelProposal", smartcontract.BoolType, + manifest.NewParameter("proposalID", smartcontract.IntegerType)) + md = NewMethodAndPrice(e.cancelProposal, 1<<15, callflag.States|callflag.AllowNotify) + e.AddMethod(md, desc) + + // ============ Voting Methods ============ + + desc = NewDescriptor("vote", smartcontract.BoolType, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("choice", smartcontract.IntegerType)) + md = NewMethodAndPrice(e.vote, 1<<15, callflag.States|callflag.AllowNotify) + e.AddMethod(md, desc) + + // ============ Tallying & Execution Methods ============ + + desc = NewDescriptor("tallyVotes", smartcontract.BoolType, + manifest.NewParameter("proposalID", smartcontract.IntegerType)) + md = NewMethodAndPrice(e.tallyVotes, 1<<15, callflag.States|callflag.AllowNotify) + e.AddMethod(md, desc) + + desc = NewDescriptor("executeProposal", smartcontract.BoolType, + manifest.NewParameter("proposalID", smartcontract.IntegerType)) + md = NewMethodAndPrice(e.executeProposal, 1<<17, callflag.All) + e.AddMethod(md, desc) + + // ============ Query Methods ============ + + desc = NewDescriptor("getProposal", smartcontract.ArrayType, + manifest.NewParameter("proposalID", smartcontract.IntegerType)) + md = NewMethodAndPrice(e.getProposal, 1<<15, callflag.ReadStates) + e.AddMethod(md, desc) + + desc = NewDescriptor("hasVoted", smartcontract.BoolType, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("voter", smartcontract.Hash160Type)) + md = NewMethodAndPrice(e.hasVoted, 1<<15, callflag.ReadStates) + e.AddMethod(md, desc) + + desc = NewDescriptor("getVote", smartcontract.ArrayType, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("voter", smartcontract.Hash160Type)) + md = NewMethodAndPrice(e.getVote, 1<<15, callflag.ReadStates) + e.AddMethod(md, desc) + + desc = NewDescriptor("getProposalCount", smartcontract.IntegerType) + md = NewMethodAndPrice(e.getProposalCount, 1<<15, callflag.ReadStates) + e.AddMethod(md, desc) + + desc = NewDescriptor("getConfig", smartcontract.ArrayType) + md = NewMethodAndPrice(e.getConfig, 1<<15, callflag.ReadStates) + e.AddMethod(md, desc) + + // ============ Events ============ + + eDesc := NewEventDescriptor(ProposalCreatedEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("title", smartcontract.StringType), + manifest.NewParameter("category", smartcontract.IntegerType), + manifest.NewParameter("proposer", smartcontract.Hash160Type), + manifest.NewParameter("votingEndsAt", smartcontract.IntegerType)) + e.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(ProposalActivatedEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType)) + e.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VoteCastEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("voterVitaID", smartcontract.IntegerType), + manifest.NewParameter("choice", smartcontract.IntegerType)) + e.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(ProposalPassedEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("supportPercent", smartcontract.IntegerType), + manifest.NewParameter("totalVotes", smartcontract.IntegerType)) + e.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(ProposalRejectedEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("supportPercent", smartcontract.IntegerType)) + e.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(ProposalExecutedEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("executor", smartcontract.Hash160Type), + manifest.NewParameter("executedAt", smartcontract.IntegerType)) + e.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(ProposalCancelledEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("cancelledBy", smartcontract.Hash160Type)) + e.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(ProposalExpiredEvent, + manifest.NewParameter("proposalID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + e.AddEvent(NewEvent(eDesc)) + + return e +} + +// Initialize implements the Contract interface. +func (e *Eligere) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != e.ActiveIn() { + return nil + } + + // Initialize cache + cache := &EligereCache{proposalCount: 0} + ic.DAO.SetCache(e.ID, cache) + + // Initialize config with defaults + cfg := state.DefaultEligereConfig() + if err := e.putConfig(ic.DAO, cfg); err != nil { + return err + } + + // Initialize proposal counter + setIntWithKey(e.ID, ic.DAO, []byte{eligerePrefixProposalCounter}, 0) + + return nil +} + +// InitializeCache implements the Contract interface. +func (e *Eligere) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cache := &EligereCache{ + proposalCount: e.getProposalCounter(d), + } + d.SetCache(e.ID, cache) + return nil +} + +// OnPersist implements the Contract interface. +func (e *Eligere) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (e *Eligere) PostPersist(ic *interop.Context) error { + return nil +} + +// Metadata returns contract metadata. +func (e *Eligere) Metadata() *interop.ContractMD { + return &e.ContractMD +} + +// Address returns the contract's script hash. +func (e *Eligere) Address() util.Uint160 { + return e.Hash +} + +// GetProposalInternal returns a proposal by ID (for cross-native access). +func (e *Eligere) GetProposalInternal(d *dao.Simple, proposalID uint64) *state.Proposal { + return e.getProposalInternal(d, proposalID) +} + +// ActiveIn implements the Contract interface. +func (e *Eligere) ActiveIn() *config.Hardfork { + return nil // Active from genesis +} + +// ============ Storage Helpers ============ + +func (e *Eligere) makeProposalKey(proposalID uint64) []byte { + key := make([]byte, 1+8) + key[0] = eligerePrefixProposal + binary.BigEndian.PutUint64(key[1:], proposalID) + return key +} + +func (e *Eligere) makeTitleKey(title string) []byte { + h := hash.Hash160([]byte(title)) + key := make([]byte, 1+20) + key[0] = eligerePrefixProposalTitle + copy(key[1:], h.BytesBE()) + return key +} + +func (e *Eligere) makeVoteKey(proposalID, vitaID uint64) []byte { + key := make([]byte, 1+8+8) + key[0] = eligerePrefixVote + binary.BigEndian.PutUint64(key[1:9], proposalID) + binary.BigEndian.PutUint64(key[9:], vitaID) + return key +} + +func (e *Eligere) makeVoterHistoryKey(vitaID, proposalID uint64) []byte { + key := make([]byte, 1+8+8) + key[0] = eligerePrefixVoterHistory + binary.BigEndian.PutUint64(key[1:9], vitaID) + binary.BigEndian.PutUint64(key[9:], proposalID) + return key +} + +func (e *Eligere) makeStatusIndexKey(status state.ProposalStatus, proposalID uint64) []byte { + key := make([]byte, 1+1+8) + key[0] = eligerePrefixProposalStatus + key[1] = byte(status) + binary.BigEndian.PutUint64(key[2:], proposalID) + return key +} + +func (e *Eligere) makeCategoryIndexKey(category state.ProposalCategory, proposalID uint64) []byte { + key := make([]byte, 1+1+8) + key[0] = eligerePrefixCategory + key[1] = byte(category) + binary.BigEndian.PutUint64(key[2:], proposalID) + return key +} + +// ============ Proposal Storage ============ + +func (e *Eligere) getProposalInternal(d *dao.Simple, proposalID uint64) *state.Proposal { + key := e.makeProposalKey(proposalID) + proposal := &state.Proposal{} + err := getConvertibleFromDAO(e.ID, d, key, proposal) + if err != nil { + return nil + } + return proposal +} + +func (e *Eligere) putProposal(d *dao.Simple, proposal *state.Proposal) error { + key := e.makeProposalKey(proposal.ID) + return putConvertibleToDAO(e.ID, d, key, proposal) +} + +func (e *Eligere) titleExists(d *dao.Simple, title string) bool { + key := e.makeTitleKey(title) + si := d.GetStorageItem(e.ID, key) + return si != nil +} + +func (e *Eligere) putTitleIndex(d *dao.Simple, title string, proposalID uint64) { + key := e.makeTitleKey(title) + val := make([]byte, 8) + binary.BigEndian.PutUint64(val, proposalID) + d.PutStorageItem(e.ID, key, val) +} + +// ============ Vote Storage ============ + +func (e *Eligere) getVoteInternal(d *dao.Simple, proposalID, vitaID uint64) *state.Vote { + key := e.makeVoteKey(proposalID, vitaID) + vote := &state.Vote{} + err := getConvertibleFromDAO(e.ID, d, key, vote) + if err != nil { + return nil + } + return vote +} + +func (e *Eligere) putVote(d *dao.Simple, vote *state.Vote) error { + key := e.makeVoteKey(vote.ProposalID, vote.VoterVitaID) + return putConvertibleToDAO(e.ID, d, key, vote) +} + +func (e *Eligere) hasVotedInternal(d *dao.Simple, proposalID, vitaID uint64) bool { + key := e.makeVoteKey(proposalID, vitaID) + si := d.GetStorageItem(e.ID, key) + return si != nil +} + +func (e *Eligere) putVoterHistory(d *dao.Simple, vitaID, proposalID uint64) { + key := e.makeVoterHistoryKey(vitaID, proposalID) + d.PutStorageItem(e.ID, key, []byte{1}) +} + +// ============ Config Storage ============ + +func (e *Eligere) getConfigInternal(d *dao.Simple) *state.EligereConfig { + key := []byte{eligerePrefixConfig} + cfg := &state.EligereConfig{} + err := getConvertibleFromDAO(e.ID, d, key, cfg) + if err != nil { + return state.DefaultEligereConfig() + } + return cfg +} + +func (e *Eligere) putConfig(d *dao.Simple, cfg *state.EligereConfig) error { + key := []byte{eligerePrefixConfig} + return putConvertibleToDAO(e.ID, d, key, cfg) +} + +// ============ Counter ============ + +func (e *Eligere) getProposalCounter(d *dao.Simple) uint64 { + key := []byte{eligerePrefixProposalCounter} + return uint64(getIntWithKey(e.ID, d, key)) +} + +func (e *Eligere) getAndIncrementProposalCounter(d *dao.Simple) uint64 { + key := []byte{eligerePrefixProposalCounter} + current := getIntWithKey(e.ID, d, key) + setIntWithKey(e.ID, d, key, current+1) + return uint64(current + 1) +} + +// ============ Status Index ============ + +func (e *Eligere) updateStatusIndex(d *dao.Simple, proposalID uint64, oldStatus, newStatus state.ProposalStatus) { + // Remove from old status index + if oldStatus != newStatus { + oldKey := e.makeStatusIndexKey(oldStatus, proposalID) + d.DeleteStorageItem(e.ID, oldKey) + } + // Add to new status index + newKey := e.makeStatusIndexKey(newStatus, proposalID) + d.PutStorageItem(e.ID, newKey, []byte{1}) +} + +// ============ Authorization Helpers ============ + +func (e *Eligere) checkCommittee(ic *interop.Context) bool { + if e.Tutus == nil { + return false + } + return e.Tutus.CheckCommittee(ic) +} + +func (e *Eligere) hasLegislatorRole(d *dao.Simple, addr util.Uint160, blockHeight uint32) bool { + if e.RoleRegistry == nil { + return false + } + return e.RoleRegistry.HasRoleInternal(d, addr, RoleLegislator, blockHeight) +} + +// ============ Contract Methods ============ + +// createProposal creates a new democratic proposal. +func (e *Eligere) createProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { + title := toString(args[0]) + contentHashBytes := toBytes(args[1]) + category := state.ProposalCategory(toBigInt(args[2]).Uint64()) + votingStartsAt := uint32(toBigInt(args[3]).Uint64()) + votingEndsAt := uint32(toBigInt(args[4]).Uint64()) + targetContract := toUint160(args[5]) + targetMethod := toString(args[6]) + targetParams := toBytes(args[7]) + + caller := ic.VM.GetCallingScriptHash() + + // Validate caller has active Vita + token, err := e.Vita.GetTokenByOwner(ic.DAO, caller) + if err != nil || token == nil || token.Status != state.TokenStatusActive { + panic(ErrNoVitaToken) + } + + // Check voting right is not restricted (via Lex) + if e.Lex != nil && !e.Lex.HasRightInternal(ic.DAO, caller, state.RightVote, ic.Block.Index) { + panic(ErrVotingRightRestricted) + } + + // For law/constitutional amendments, require legislator role or committee + if category == state.ProposalCategoryLawAmendment || category == state.ProposalCategoryConstitutional { + if !e.hasLegislatorRole(ic.DAO, caller, ic.Block.Index) && !e.checkCommittee(ic) { + panic("legislative authority required for law amendments") + } + } + + // Validate inputs + if len(title) > 128 { + panic(ErrTitleTooLong) + } + if len(title) == 0 { + panic("title cannot be empty") + } + if category < state.ProposalCategoryLawAmendment || category > state.ProposalCategoryReferendum { + panic(ErrInvalidCategory) + } + + // Check title uniqueness + if e.titleExists(ic.DAO, title) { + panic(ErrProposalTitleExists) + } + + // Validate voting period + cfg := e.getConfigInternal(ic.DAO) + if votingStartsAt < ic.Block.Index { + votingStartsAt = ic.Block.Index // Can't start in past + } + duration := votingEndsAt - votingStartsAt + if duration < cfg.MinVotingPeriod || duration > cfg.MaxVotingPeriod { + panic(ErrInvalidVotingPeriod) + } + + // Determine threshold based on category + threshold := cfg.StandardThreshold + if category == state.ProposalCategoryConstitutional { + threshold = cfg.ConstitutionalThreshold + } + + // Get next proposal ID + proposalID := e.getAndIncrementProposalCounter(ic.DAO) + + // Create proposal + var contentHash util.Uint256 + if len(contentHashBytes) == 32 { + copy(contentHash[:], contentHashBytes) + } + + proposal := &state.Proposal{ + ID: proposalID, + Title: title, + ContentHash: contentHash, + Category: category, + Proposer: caller, + ProposerVitaID: token.TokenID, + CreatedAt: ic.Block.Index, + VotingStartsAt: votingStartsAt, + VotingEndsAt: votingEndsAt, + ExecutionDelay: cfg.DefaultExecutionDelay, + QuorumPercent: cfg.DefaultQuorum, + ThresholdPercent: threshold, + Status: state.ProposalStatusDraft, + TargetContract: targetContract, + TargetMethod: targetMethod, + TargetParams: targetParams, + } + + // If voting starts now, activate immediately + if votingStartsAt <= ic.Block.Index { + proposal.Status = state.ProposalStatusActive + } + + // Store proposal + if err := e.putProposal(ic.DAO, proposal); err != nil { + panic(err) + } + + // Store indexes + e.putTitleIndex(ic.DAO, title, proposalID) + e.updateStatusIndex(ic.DAO, proposalID, state.ProposalStatusDraft, proposal.Status) + + // Store category index + catKey := e.makeCategoryIndexKey(category, proposalID) + ic.DAO.PutStorageItem(e.ID, catKey, []byte{1}) + + // Emit event + ic.AddNotification(e.Hash, ProposalCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(proposalID))), + stackitem.NewByteArray([]byte(title)), + stackitem.NewBigInteger(big.NewInt(int64(category))), + stackitem.NewByteArray(caller.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(votingEndsAt))), + })) + + return stackitem.NewBigInteger(big.NewInt(int64(proposalID))) +} + +// cancelProposal cancels a proposal (proposer or committee only). +func (e *Eligere) cancelProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { + proposalID := toBigInt(args[0]).Uint64() + + caller := ic.VM.GetCallingScriptHash() + + proposal := e.getProposalInternal(ic.DAO, proposalID) + if proposal == nil { + panic(ErrProposalNotFound) + } + + // Check authorization: must be proposer or committee + isProposer := proposal.Proposer.Equals(caller) + isCommittee := e.checkCommittee(ic) + if !isProposer && !isCommittee { + panic(ErrNotCommitteeOrProposer) + } + + // Can only cancel draft or active proposals + if proposal.Status != state.ProposalStatusDraft && proposal.Status != state.ProposalStatusActive { + panic(ErrCannotCancelFinalized) + } + + oldStatus := proposal.Status + proposal.Status = state.ProposalStatusCancelled + + // Update proposal + if err := e.putProposal(ic.DAO, proposal); err != nil { + panic(err) + } + + // Update status index + e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) + + // Emit event + ic.AddNotification(e.Hash, ProposalCancelledEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(proposalID))), + stackitem.NewByteArray(caller.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +// vote casts a vote on a proposal. +func (e *Eligere) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item { + proposalID := toBigInt(args[0]).Uint64() + choice := state.VoteChoice(toBigInt(args[1]).Uint64()) + + caller := ic.VM.GetCallingScriptHash() + + // Validate vote choice + if choice > state.VoteChoiceNo { + panic(ErrInvalidVoteChoice) + } + + // Validate caller has active Vita + token, err := e.Vita.GetTokenByOwner(ic.DAO, caller) + if err != nil || token == nil || token.Status != state.TokenStatusActive { + panic(ErrNoVitaToken) + } + + // Check voting right is not restricted + if e.Lex != nil && !e.Lex.HasRightInternal(ic.DAO, caller, state.RightVote, ic.Block.Index) { + panic(ErrVotingRightRestricted) + } + + // Check voter is of voting age (18+) + if e.Annos != nil && !e.Annos.IsVotingAgeInternal(ic.DAO, caller, ic.Block.Timestamp) { + panic(ErrUnderVotingAge) + } + + // Get proposal + proposal := e.getProposalInternal(ic.DAO, proposalID) + if proposal == nil { + panic(ErrProposalNotFound) + } + + // Check proposal is active + if proposal.Status != state.ProposalStatusActive { + // Auto-activate if draft and voting period has started + if proposal.Status == state.ProposalStatusDraft && ic.Block.Index >= proposal.VotingStartsAt { + proposal.Status = state.ProposalStatusActive + e.updateStatusIndex(ic.DAO, proposalID, state.ProposalStatusDraft, state.ProposalStatusActive) + } else { + panic(ErrProposalNotActive) + } + } + + // Check voting period + if ic.Block.Index < proposal.VotingStartsAt { + panic(ErrVotingNotStarted) + } + if ic.Block.Index > proposal.VotingEndsAt { + panic(ErrVotingEnded) + } + + // Check not already voted (using Vita ID for one-person-one-vote) + if e.hasVotedInternal(ic.DAO, proposalID, token.TokenID) { + panic(ErrAlreadyVoted) + } + + // Create vote record + voteRecord := &state.Vote{ + ProposalID: proposalID, + VoterVitaID: token.TokenID, + Voter: caller, + Choice: choice, + VotedAt: ic.Block.Index, + Weight: 1, // Equal voting weight + } + + // Store vote + if err := e.putVote(ic.DAO, voteRecord); err != nil { + panic(err) + } + + // Store voter history + e.putVoterHistory(ic.DAO, token.TokenID, proposalID) + + // Update vote counts (incremental tallying) + proposal.TotalVotes++ + switch choice { + case state.VoteChoiceYes: + proposal.YesVotes++ + case state.VoteChoiceNo: + proposal.NoVotes++ + case state.VoteChoiceAbstain: + proposal.AbstainVotes++ + } + + // Update proposal + if err := e.putProposal(ic.DAO, proposal); err != nil { + panic(err) + } + + // Emit event + ic.AddNotification(e.Hash, VoteCastEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(proposalID))), + stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))), + stackitem.NewBigInteger(big.NewInt(int64(choice))), + })) + + return stackitem.NewBool(true) +} + +// tallyVotes finalizes voting after the deadline. +func (e *Eligere) tallyVotes(ic *interop.Context, args []stackitem.Item) stackitem.Item { + proposalID := toBigInt(args[0]).Uint64() + + proposal := e.getProposalInternal(ic.DAO, proposalID) + if proposal == nil { + panic(ErrProposalNotFound) + } + + // Must be after voting period + if ic.Block.Index <= proposal.VotingEndsAt { + panic(ErrVotingPeriodNotEnded) + } + + // Must still be active + if proposal.Status != state.ProposalStatusActive { + panic(ErrProposalAlreadyFinalized) + } + + // Get total eligible voters for quorum calculation + // Use Vita token count as the voter base + totalVoters := e.Vita.GetTotalTokenCount(ic.DAO) + if totalVoters == 0 { + totalVoters = 1 // Avoid division by zero + } + + oldStatus := proposal.Status + + // Check quorum (total votes including abstentions) + quorumRequired := (totalVoters * uint64(proposal.QuorumPercent)) / 100 + if proposal.TotalVotes < quorumRequired { + proposal.Status = state.ProposalStatusExpired + + if err := e.putProposal(ic.DAO, proposal); err != nil { + panic(err) + } + e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) + + ic.AddNotification(e.Hash, ProposalExpiredEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(proposalID))), + stackitem.NewByteArray([]byte("quorum not met")), + })) + return stackitem.NewBool(false) + } + + // Check threshold (yes votes vs yes+no, abstentions don't count toward threshold) + totalDecisiveVotes := proposal.YesVotes + proposal.NoVotes + var supportPercent uint64 = 0 + if totalDecisiveVotes > 0 { + supportPercent = (proposal.YesVotes * 100) / totalDecisiveVotes + } + + if totalDecisiveVotes == 0 || supportPercent < uint64(proposal.ThresholdPercent) { + proposal.Status = state.ProposalStatusRejected + + if err := e.putProposal(ic.DAO, proposal); err != nil { + panic(err) + } + e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) + + ic.AddNotification(e.Hash, ProposalRejectedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(proposalID))), + stackitem.NewBigInteger(big.NewInt(int64(supportPercent))), + })) + return stackitem.NewBool(false) + } + + // Proposal passed + proposal.Status = state.ProposalStatusPassed + + if err := e.putProposal(ic.DAO, proposal); err != nil { + panic(err) + } + e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) + + ic.AddNotification(e.Hash, ProposalPassedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(proposalID))), + stackitem.NewBigInteger(big.NewInt(int64(supportPercent))), + stackitem.NewBigInteger(big.NewInt(int64(proposal.TotalVotes))), + })) + + return stackitem.NewBool(true) +} + +// executeProposal executes a passed proposal after the execution delay. +func (e *Eligere) executeProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { + proposalID := toBigInt(args[0]).Uint64() + + caller := ic.VM.GetCallingScriptHash() + + proposal := e.getProposalInternal(ic.DAO, proposalID) + if proposal == nil { + panic(ErrProposalNotFound) + } + + // Must have passed + if proposal.Status != state.ProposalStatusPassed { + panic(ErrProposalNotPassed) + } + + // Check execution delay has passed + executionBlock := proposal.VotingEndsAt + proposal.ExecutionDelay + if ic.Block.Index < executionBlock { + panic(ErrExecutionDelayNotPassed) + } + + // Already executed check + if proposal.ExecutedAt > 0 { + panic(ErrProposalAlreadyExecuted) + } + + oldStatus := proposal.Status + + // Execute based on category + switch proposal.Category { + case state.ProposalCategoryLawAmendment: + // Call Lex.ratifyAmendment if Lex is available + if e.Lex != nil { + e.executeLawAmendment(ic, proposal) + } + case state.ProposalCategoryConstitutional: + // Constitutional amendments also go through Lex + if e.Lex != nil { + e.executeLawAmendment(ic, proposal) + } + case state.ProposalCategoryGovernanceAction: + // Governance actions may call target contract + // Implementation depends on specific action + case state.ProposalCategoryInvestment: + // Investment proposals - future integration + case state.ProposalCategoryReferendum: + // Referendums are advisory, no automatic execution + } + + // Update proposal status + proposal.Status = state.ProposalStatusExecuted + proposal.ExecutedAt = ic.Block.Index + proposal.ExecutedBy = caller + + if err := e.putProposal(ic.DAO, proposal); err != nil { + panic(err) + } + e.updateStatusIndex(ic.DAO, proposalID, oldStatus, proposal.Status) + + // Emit event + ic.AddNotification(e.Hash, ProposalExecutedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(proposalID))), + stackitem.NewByteArray(caller.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))), + })) + + return stackitem.NewBool(true) +} + +// executeLawAmendment calls Lex to ratify a law amendment. +func (e *Eligere) executeLawAmendment(ic *interop.Context, proposal *state.Proposal) { + // The Lex contract's ratifyAmendment method will be called + // This creates or updates a law based on the proposal + if e.Lex != nil { + // All amendments create Federal-level laws + // Constitutional rights (RightLife, RightLiberty, etc.) are immutable in code + // Constitutional proposals require 67% supermajority but still create Federal laws + lawCategory := state.LawCategoryFederal + + // Call Lex to ratify the amendment + e.Lex.RatifyAmendmentInternal(ic, proposal.ID, proposal.ContentHash, lawCategory, 0) + } +} + +// ============ Query Methods ============ + +func (e *Eligere) getProposal(ic *interop.Context, args []stackitem.Item) stackitem.Item { + proposalID := toBigInt(args[0]).Uint64() + + proposal := e.getProposalInternal(ic.DAO, proposalID) + if proposal == nil { + return stackitem.Null{} + } + + item, err := proposal.ToStackItem() + if err != nil { + return stackitem.Null{} + } + return item +} + +func (e *Eligere) hasVoted(ic *interop.Context, args []stackitem.Item) stackitem.Item { + proposalID := toBigInt(args[0]).Uint64() + voter := toUint160(args[1]) + + // Get voter's Vita token + token, err := e.Vita.GetTokenByOwner(ic.DAO, voter) + if err != nil || token == nil { + return stackitem.NewBool(false) + } + + return stackitem.NewBool(e.hasVotedInternal(ic.DAO, proposalID, token.TokenID)) +} + +func (e *Eligere) getVote(ic *interop.Context, args []stackitem.Item) stackitem.Item { + proposalID := toBigInt(args[0]).Uint64() + voter := toUint160(args[1]) + + // Get voter's Vita token + token, err := e.Vita.GetTokenByOwner(ic.DAO, voter) + if err != nil || token == nil { + return stackitem.Null{} + } + + vote := e.getVoteInternal(ic.DAO, proposalID, token.TokenID) + if vote == nil { + return stackitem.Null{} + } + + item, err := vote.ToStackItem() + if err != nil { + return stackitem.Null{} + } + return item +} + +func (e *Eligere) getProposalCount(ic *interop.Context, args []stackitem.Item) stackitem.Item { + key := []byte{eligerePrefixProposalCounter} + count := getIntWithKey(e.ID, ic.DAO, key) + return stackitem.NewBigInteger(big.NewInt(count)) +} + +func (e *Eligere) getConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cfg := e.getConfigInternal(ic.DAO) + item, err := cfg.ToStackItem() + if err != nil { + return stackitem.Null{} + } + return item +} diff --git a/pkg/core/native/event_archival.go b/pkg/core/native/event_archival.go index fc785bb..92a5012 100644 --- a/pkg/core/native/event_archival.go +++ b/pkg/core/native/event_archival.go @@ -3,10 +3,10 @@ package native import ( "encoding/binary" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // LOW-001: Event archival system for managing event log growth. diff --git a/pkg/core/native/federation.go b/pkg/core/native/federation.go index c660437..9ce26e5 100644 --- a/pkg/core/native/federation.go +++ b/pkg/core/native/federation.go @@ -1,722 +1,722 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Federation represents the Federation native contract for cross-chain Vita coordination. -// It manages: -// - Visiting fee percentage (what % of fees the host chain pays for visitors) -// - Visitor registry (tracking Vita holders from other chains) -// - Inter-chain debt (tracking fees owed to other chains) -type Federation struct { - interop.ContractMD - Tutus ITutus -} - -// Storage key prefixes for Federation. -const ( - prefixVisitingFeePercent byte = 0x01 // -> uint8 (0-100, % host chain pays for visitors) - prefixVisitorRegistry byte = 0x02 // owner (Uint160) -> home chain ID (uint32) - prefixInterChainDebt byte = 0x03 // chain ID (uint32) -> *big.Int (amount owed) - prefixAsylumRegistry byte = 0x04 // owner (Uint160) -> AsylumRecord - prefixNaturalizedCitizen byte = 0x05 // owner (Uint160) -> NaturalizationRecord -) - -// Default values. -const ( - defaultVisitingFeePercent uint8 = 50 // 50% local, 50% inter-chain debt -) - -// Event names for Federation. -const ( - VisitorRegisteredEvent = "VisitorRegistered" - VisitorUnregisteredEvent = "VisitorUnregistered" - FeePercentChangedEvent = "FeePercentChanged" - DebtSettledEvent = "DebtSettled" - AsylumGrantedEvent = "AsylumGranted" - AsylumRevokedEvent = "AsylumRevoked" - CitizenNaturalizedEvent = "CitizenNaturalized" -) - -// Various errors. -var ( - ErrInvalidFeePercent = errors.New("fee percent must be 0-100") - ErrVisitorAlreadyExists = errors.New("visitor already registered") - ErrVisitorNotFound = errors.New("visitor not found") - ErrInvalidChainID = errors.New("invalid chain ID") - ErrInsufficientDebt = errors.New("insufficient debt to settle") - ErrAsylumAlreadyGranted = errors.New("asylum already granted") - ErrAsylumNotFound = errors.New("asylum not found") - ErrAlreadyNaturalized = errors.New("already naturalized") - ErrNotNaturalized = errors.New("not naturalized") -) - -var _ interop.Contract = (*Federation)(nil) - -// newFederation creates a new Federation native contract. -func newFederation() *Federation { - f := &Federation{ - ContractMD: *interop.NewContractMD(nativenames.Federation, nativeids.Federation), - } - defer f.BuildHFSpecificMD(f.ActiveIn()) - - // getFeePercent method - desc := NewDescriptor("getFeePercent", smartcontract.IntegerType) - md := NewMethodAndPrice(f.getFeePercent, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // setFeePercent method (committee only) - desc = NewDescriptor("setFeePercent", smartcontract.BoolType, - manifest.NewParameter("percent", smartcontract.IntegerType)) - md = NewMethodAndPrice(f.setFeePercent, 1<<16, callflag.States|callflag.AllowNotify) - f.AddMethod(md, desc) - - // registerVisitor method (committee only) - desc = NewDescriptor("registerVisitor", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("homeChainID", smartcontract.IntegerType)) - md = NewMethodAndPrice(f.registerVisitor, 1<<16, callflag.States|callflag.AllowNotify) - f.AddMethod(md, desc) - - // unregisterVisitor method (committee only) - desc = NewDescriptor("unregisterVisitor", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.unregisterVisitor, 1<<16, callflag.States|callflag.AllowNotify) - f.AddMethod(md, desc) - - // isVisitor method - desc = NewDescriptor("isVisitor", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.isVisitor, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // getHomeChain method - desc = NewDescriptor("getHomeChain", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.getHomeChain, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // getInterChainDebt method - desc = NewDescriptor("getInterChainDebt", smartcontract.IntegerType, - manifest.NewParameter("chainID", smartcontract.IntegerType)) - md = NewMethodAndPrice(f.getInterChainDebt, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // settleDebt method (committee only) - desc = NewDescriptor("settleDebt", smartcontract.BoolType, - manifest.NewParameter("chainID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - md = NewMethodAndPrice(f.settleDebt, 1<<16, callflag.States|callflag.AllowNotify) - f.AddMethod(md, desc) - - // grantAsylum method (committee only) - desc = NewDescriptor("grantAsylum", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("homeChainID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(f.grantAsylum, 1<<16, callflag.States|callflag.AllowNotify) - f.AddMethod(md, desc) - - // revokeAsylum method (committee only) - desc = NewDescriptor("revokeAsylum", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.revokeAsylum, 1<<16, callflag.States|callflag.AllowNotify) - f.AddMethod(md, desc) - - // hasAsylum method - desc = NewDescriptor("hasAsylum", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.hasAsylum, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // getAsylumInfo method - desc = NewDescriptor("getAsylumInfo", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.getAsylumInfo, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // naturalize method (committee only) - desc = NewDescriptor("naturalize", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("originalHomeChain", smartcontract.IntegerType)) - md = NewMethodAndPrice(f.naturalize, 1<<16, callflag.States|callflag.AllowNotify) - f.AddMethod(md, desc) - - // isNaturalizedCitizen method - desc = NewDescriptor("isNaturalizedCitizen", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.isNaturalizedCitizen, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // getNaturalizationInfo method - desc = NewDescriptor("getNaturalizationInfo", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(f.getNaturalizationInfo, 1<<15, callflag.ReadStates) - f.AddMethod(md, desc) - - // Events - eDesc := NewEventDescriptor(VisitorRegisteredEvent, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("homeChainID", smartcontract.IntegerType)) - f.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VisitorUnregisteredEvent, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - f.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(FeePercentChangedEvent, - manifest.NewParameter("oldPercent", smartcontract.IntegerType), - manifest.NewParameter("newPercent", smartcontract.IntegerType)) - f.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(DebtSettledEvent, - manifest.NewParameter("chainID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("remaining", smartcontract.IntegerType)) - f.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AsylumGrantedEvent, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("homeChainID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - f.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AsylumRevokedEvent, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - f.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(CitizenNaturalizedEvent, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("originalHomeChain", smartcontract.IntegerType)) - f.AddEvent(NewEvent(eDesc)) - - return f -} - -// Metadata returns contract metadata. -func (f *Federation) Metadata() *interop.ContractMD { - return &f.ContractMD -} - -// Initialize initializes Federation contract at the specified hardfork. -func (f *Federation) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != f.ActiveIn() { - return nil - } - - // Initialize default fee percent - f.setFeePercentInternal(ic.DAO, defaultVisitingFeePercent) - - return nil -} - -// InitializeCache fills native Federation cache from DAO on node restart. -func (f *Federation) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - return nil -} - -// OnPersist implements the Contract interface. -func (f *Federation) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (f *Federation) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn returns the hardfork this contract activates in. -func (f *Federation) ActiveIn() *config.Hardfork { - var hf = config.HFFaun - return &hf -} - -// Storage key helpers - -func makeFeePercentKey() []byte { - return []byte{prefixVisitingFeePercent} -} - -func makeVisitorKey(owner util.Uint160) []byte { - key := make([]byte, 1+util.Uint160Size) - key[0] = prefixVisitorRegistry - copy(key[1:], owner.BytesBE()) - return key -} - -func makeDebtKey(chainID uint32) []byte { - key := make([]byte, 5) - key[0] = prefixInterChainDebt - binary.BigEndian.PutUint32(key[1:], chainID) - return key -} - -func makeAsylumKey(owner util.Uint160) []byte { - key := make([]byte, 1+util.Uint160Size) - key[0] = prefixAsylumRegistry - copy(key[1:], owner.BytesBE()) - return key -} - -func makeNaturalizedKey(owner util.Uint160) []byte { - key := make([]byte, 1+util.Uint160Size) - key[0] = prefixNaturalizedCitizen - copy(key[1:], owner.BytesBE()) - return key -} - -// Internal storage methods - -func (f *Federation) getFeePercentInternal(d *dao.Simple) uint8 { - si := d.GetStorageItem(f.ID, makeFeePercentKey()) - if si == nil { - return defaultVisitingFeePercent - } - return si[0] -} - -func (f *Federation) setFeePercentInternal(d *dao.Simple, percent uint8) { - d.PutStorageItem(f.ID, makeFeePercentKey(), []byte{percent}) -} - -func (f *Federation) getVisitorChainInternal(d *dao.Simple, owner util.Uint160) (uint32, bool) { - si := d.GetStorageItem(f.ID, makeVisitorKey(owner)) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint32(si), true -} - -func (f *Federation) setVisitorChainInternal(d *dao.Simple, owner util.Uint160, chainID uint32) { - buf := make([]byte, 4) - binary.BigEndian.PutUint32(buf, chainID) - d.PutStorageItem(f.ID, makeVisitorKey(owner), buf) -} - -func (f *Federation) deleteVisitorInternal(d *dao.Simple, owner util.Uint160) { - d.DeleteStorageItem(f.ID, makeVisitorKey(owner)) -} - -func (f *Federation) getDebtInternal(d *dao.Simple, chainID uint32) *big.Int { - si := d.GetStorageItem(f.ID, makeDebtKey(chainID)) - if si == nil { - return big.NewInt(0) - } - return new(big.Int).SetBytes(si) -} - -func (f *Federation) setDebtInternal(d *dao.Simple, chainID uint32, amount *big.Int) { - if amount.Sign() <= 0 { - d.DeleteStorageItem(f.ID, makeDebtKey(chainID)) - return - } - d.PutStorageItem(f.ID, makeDebtKey(chainID), amount.Bytes()) -} - -func (f *Federation) addDebtInternal(d *dao.Simple, chainID uint32, amount *big.Int) { - current := f.getDebtInternal(d, chainID) - newDebt := new(big.Int).Add(current, amount) - f.setDebtInternal(d, chainID, newDebt) -} - -// Asylum record storage format: homeChainID (4 bytes) + grantedAt (4 bytes) + reason (variable) -func (f *Federation) getAsylumInternal(d *dao.Simple, owner util.Uint160) (homeChainID uint32, grantedAt uint32, reason string, exists bool) { - si := d.GetStorageItem(f.ID, makeAsylumKey(owner)) - if si == nil || len(si) < 8 { - return 0, 0, "", false - } - homeChainID = binary.BigEndian.Uint32(si[0:4]) - grantedAt = binary.BigEndian.Uint32(si[4:8]) - if len(si) > 8 { - reason = string(si[8:]) - } - return homeChainID, grantedAt, reason, true -} - -func (f *Federation) setAsylumInternal(d *dao.Simple, owner util.Uint160, homeChainID uint32, grantedAt uint32, reason string) { - buf := make([]byte, 8+len(reason)) - binary.BigEndian.PutUint32(buf[0:4], homeChainID) - binary.BigEndian.PutUint32(buf[4:8], grantedAt) - copy(buf[8:], reason) - d.PutStorageItem(f.ID, makeAsylumKey(owner), buf) -} - -func (f *Federation) deleteAsylumInternal(d *dao.Simple, owner util.Uint160) { - d.DeleteStorageItem(f.ID, makeAsylumKey(owner)) -} - -func (f *Federation) hasAsylumInternal(d *dao.Simple, owner util.Uint160) bool { - si := d.GetStorageItem(f.ID, makeAsylumKey(owner)) - return si != nil -} - -// Naturalization record storage format: originalHomeChain (4 bytes) + naturalizedAt (4 bytes) -func (f *Federation) getNaturalizedInternal(d *dao.Simple, owner util.Uint160) (originalHomeChain uint32, naturalizedAt uint32, exists bool) { - si := d.GetStorageItem(f.ID, makeNaturalizedKey(owner)) - if si == nil || len(si) < 8 { - return 0, 0, false - } - originalHomeChain = binary.BigEndian.Uint32(si[0:4]) - naturalizedAt = binary.BigEndian.Uint32(si[4:8]) - return originalHomeChain, naturalizedAt, true -} - -func (f *Federation) setNaturalizedInternal(d *dao.Simple, owner util.Uint160, originalHomeChain uint32, naturalizedAt uint32) { - buf := make([]byte, 8) - binary.BigEndian.PutUint32(buf[0:4], originalHomeChain) - binary.BigEndian.PutUint32(buf[4:8], naturalizedAt) - d.PutStorageItem(f.ID, makeNaturalizedKey(owner), buf) -} - -func (f *Federation) isNaturalizedInternal(d *dao.Simple, owner util.Uint160) bool { - si := d.GetStorageItem(f.ID, makeNaturalizedKey(owner)) - return si != nil -} - -// Contract methods - -func (f *Federation) getFeePercent(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - percent := f.getFeePercentInternal(ic.DAO) - return stackitem.NewBigInteger(big.NewInt(int64(percent))) -} - -func (f *Federation) setFeePercent(ic *interop.Context, args []stackitem.Item) stackitem.Item { - percent := toBigInt(args[0]).Int64() - - // Validate percent - if percent < 0 || percent > 100 { - panic(ErrInvalidFeePercent) - } - - // Check committee - if !f.Tutus.CheckCommittee(ic) { - panic(ErrNotCommittee) - } - - // Get old value for event - oldPercent := f.getFeePercentInternal(ic.DAO) - - // Set new value - f.setFeePercentInternal(ic.DAO, uint8(percent)) - - // Emit event - err := ic.AddNotification(f.Hash, FeePercentChangedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(oldPercent))), - stackitem.NewBigInteger(big.NewInt(percent)), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (f *Federation) registerVisitor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - chainID := uint32(toBigInt(args[1]).Int64()) - - // Validate chain ID - if chainID == 0 { - panic(ErrInvalidChainID) - } - - // Check committee - if !f.Tutus.CheckCommittee(ic) { - panic(ErrNotCommittee) - } - - // Check if already registered - if _, exists := f.getVisitorChainInternal(ic.DAO, owner); exists { - panic(ErrVisitorAlreadyExists) - } - - // Register visitor - f.setVisitorChainInternal(ic.DAO, owner, chainID) - - // Emit event - err := ic.AddNotification(f.Hash, VisitorRegisteredEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(owner.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(chainID))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (f *Federation) unregisterVisitor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - // Check committee - if !f.Tutus.CheckCommittee(ic) { - panic(ErrNotCommittee) - } - - // Check if registered - if _, exists := f.getVisitorChainInternal(ic.DAO, owner); !exists { - panic(ErrVisitorNotFound) - } - - // Unregister visitor - f.deleteVisitorInternal(ic.DAO, owner) - - // Emit event - err := ic.AddNotification(f.Hash, VisitorUnregisteredEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(owner.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (f *Federation) isVisitor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - _, exists := f.getVisitorChainInternal(ic.DAO, owner) - return stackitem.NewBool(exists) -} - -func (f *Federation) getHomeChain(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - chainID, exists := f.getVisitorChainInternal(ic.DAO, owner) - if !exists { - return stackitem.NewBigInteger(big.NewInt(0)) - } - return stackitem.NewBigInteger(big.NewInt(int64(chainID))) -} - -func (f *Federation) getInterChainDebt(ic *interop.Context, args []stackitem.Item) stackitem.Item { - chainID := uint32(toBigInt(args[0]).Int64()) - debt := f.getDebtInternal(ic.DAO, chainID) - return stackitem.NewBigInteger(debt) -} - -func (f *Federation) settleDebt(ic *interop.Context, args []stackitem.Item) stackitem.Item { - chainID := uint32(toBigInt(args[0]).Int64()) - amount := toBigInt(args[1]) - - // Validate chain ID - if chainID == 0 { - panic(ErrInvalidChainID) - } - - // Check committee - if !f.Tutus.CheckCommittee(ic) { - panic(ErrNotCommittee) - } - - // Get current debt - currentDebt := f.getDebtInternal(ic.DAO, chainID) - - // Check sufficient debt - if currentDebt.Cmp(amount) < 0 { - panic(ErrInsufficientDebt) - } - - // Subtract settled amount - remaining := new(big.Int).Sub(currentDebt, amount) - f.setDebtInternal(ic.DAO, chainID, remaining) - - // Emit event - err := ic.AddNotification(f.Hash, DebtSettledEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(chainID))), - stackitem.NewBigInteger(amount), - stackitem.NewBigInteger(remaining), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// Asylum methods - -func (f *Federation) grantAsylum(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - homeChainID := uint32(toBigInt(args[1]).Int64()) - reason := toString(args[2]) - - // Validate chain ID - if homeChainID == 0 { - panic(ErrInvalidChainID) - } - - // Check committee - if !f.Tutus.CheckCommittee(ic) { - panic(ErrNotCommittee) - } - - // Check if already has asylum - if f.hasAsylumInternal(ic.DAO, owner) { - panic(ErrAsylumAlreadyGranted) - } - - // Grant asylum - f.setAsylumInternal(ic.DAO, owner, homeChainID, ic.BlockHeight(), reason) - - // Emit event - err := ic.AddNotification(f.Hash, AsylumGrantedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(owner.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(homeChainID))), - stackitem.NewByteArray([]byte(reason)), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (f *Federation) revokeAsylum(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - // Check committee - if !f.Tutus.CheckCommittee(ic) { - panic(ErrNotCommittee) - } - - // Check if has asylum - if !f.hasAsylumInternal(ic.DAO, owner) { - panic(ErrAsylumNotFound) - } - - // Revoke asylum - f.deleteAsylumInternal(ic.DAO, owner) - - // Emit event - err := ic.AddNotification(f.Hash, AsylumRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(owner.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (f *Federation) hasAsylum(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - return stackitem.NewBool(f.hasAsylumInternal(ic.DAO, owner)) -} - -func (f *Federation) getAsylumInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - homeChainID, grantedAt, reason, exists := f.getAsylumInternal(ic.DAO, owner) - if !exists { - return stackitem.Null{} - } - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(homeChainID))), - stackitem.NewBigInteger(big.NewInt(int64(grantedAt))), - stackitem.NewByteArray([]byte(reason)), - }) -} - -// Naturalization methods - -func (f *Federation) naturalize(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - originalHomeChain := uint32(toBigInt(args[1]).Int64()) - - // Check committee - if !f.Tutus.CheckCommittee(ic) { - panic(ErrNotCommittee) - } - - // Check if already naturalized - if f.isNaturalizedInternal(ic.DAO, owner) { - panic(ErrAlreadyNaturalized) - } - - // Naturalize the citizen - f.setNaturalizedInternal(ic.DAO, owner, originalHomeChain, ic.BlockHeight()) - - // Emit event - err := ic.AddNotification(f.Hash, CitizenNaturalizedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(owner.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(originalHomeChain))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (f *Federation) isNaturalizedCitizen(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - return stackitem.NewBool(f.isNaturalizedInternal(ic.DAO, owner)) -} - -func (f *Federation) getNaturalizationInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - originalHomeChain, naturalizedAt, exists := f.getNaturalizedInternal(ic.DAO, owner) - if !exists { - return stackitem.Null{} - } - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(originalHomeChain))), - stackitem.NewBigInteger(big.NewInt(int64(naturalizedAt))), - }) -} - -// Public methods for cross-native access - -// GetVisitingFeePercent returns the visiting fee percent (for cross-native access). -func (f *Federation) GetVisitingFeePercent(d *dao.Simple) uint8 { - return f.getFeePercentInternal(d) -} - -// IsVisitor checks if an address is a registered visitor (for cross-native access). -func (f *Federation) IsVisitor(d *dao.Simple, owner util.Uint160) bool { - _, exists := f.getVisitorChainInternal(d, owner) - return exists -} - -// GetHomeChain returns the home chain ID for a visitor (for cross-native access). -func (f *Federation) GetHomeChain(d *dao.Simple, owner util.Uint160) uint32 { - chainID, _ := f.getVisitorChainInternal(d, owner) - return chainID -} - -// AddInterChainDebt adds to the inter-chain debt for a specific chain (for cross-native access). -func (f *Federation) AddInterChainDebt(d *dao.Simple, chainID uint32, amount *big.Int) { - f.addDebtInternal(d, chainID, amount) -} - -// GetInterChainDebt returns the inter-chain debt for a specific chain (for cross-native access). -func (f *Federation) GetInterChainDebt(d *dao.Simple, chainID uint32) *big.Int { - return f.getDebtInternal(d, chainID) -} - -// Address returns the contract's script hash. -func (f *Federation) Address() util.Uint160 { - return f.Hash -} - -// HasAsylum checks if an address has asylum status (for cross-native access). -func (f *Federation) HasAsylum(d *dao.Simple, owner util.Uint160) bool { - return f.hasAsylumInternal(d, owner) -} - -// IsNaturalizedCitizen checks if an address is a naturalized citizen (for cross-native access). -func (f *Federation) IsNaturalizedCitizen(d *dao.Simple, owner util.Uint160) bool { - return f.isNaturalizedInternal(d, owner) -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Federation represents the Federation native contract for cross-chain Vita coordination. +// It manages: +// - Visiting fee percentage (what % of fees the host chain pays for visitors) +// - Visitor registry (tracking Vita holders from other chains) +// - Inter-chain debt (tracking fees owed to other chains) +type Federation struct { + interop.ContractMD + Tutus ITutus +} + +// Storage key prefixes for Federation. +const ( + prefixVisitingFeePercent byte = 0x01 // -> uint8 (0-100, % host chain pays for visitors) + prefixVisitorRegistry byte = 0x02 // owner (Uint160) -> home chain ID (uint32) + prefixInterChainDebt byte = 0x03 // chain ID (uint32) -> *big.Int (amount owed) + prefixAsylumRegistry byte = 0x04 // owner (Uint160) -> AsylumRecord + prefixNaturalizedCitizen byte = 0x05 // owner (Uint160) -> NaturalizationRecord +) + +// Default values. +const ( + defaultVisitingFeePercent uint8 = 50 // 50% local, 50% inter-chain debt +) + +// Event names for Federation. +const ( + VisitorRegisteredEvent = "VisitorRegistered" + VisitorUnregisteredEvent = "VisitorUnregistered" + FeePercentChangedEvent = "FeePercentChanged" + DebtSettledEvent = "DebtSettled" + AsylumGrantedEvent = "AsylumGranted" + AsylumRevokedEvent = "AsylumRevoked" + CitizenNaturalizedEvent = "CitizenNaturalized" +) + +// Various errors. +var ( + ErrInvalidFeePercent = errors.New("fee percent must be 0-100") + ErrVisitorAlreadyExists = errors.New("visitor already registered") + ErrVisitorNotFound = errors.New("visitor not found") + ErrInvalidChainID = errors.New("invalid chain ID") + ErrInsufficientDebt = errors.New("insufficient debt to settle") + ErrAsylumAlreadyGranted = errors.New("asylum already granted") + ErrAsylumNotFound = errors.New("asylum not found") + ErrAlreadyNaturalized = errors.New("already naturalized") + ErrNotNaturalized = errors.New("not naturalized") +) + +var _ interop.Contract = (*Federation)(nil) + +// newFederation creates a new Federation native contract. +func newFederation() *Federation { + f := &Federation{ + ContractMD: *interop.NewContractMD(nativenames.Federation, nativeids.Federation), + } + defer f.BuildHFSpecificMD(f.ActiveIn()) + + // getFeePercent method + desc := NewDescriptor("getFeePercent", smartcontract.IntegerType) + md := NewMethodAndPrice(f.getFeePercent, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // setFeePercent method (committee only) + desc = NewDescriptor("setFeePercent", smartcontract.BoolType, + manifest.NewParameter("percent", smartcontract.IntegerType)) + md = NewMethodAndPrice(f.setFeePercent, 1<<16, callflag.States|callflag.AllowNotify) + f.AddMethod(md, desc) + + // registerVisitor method (committee only) + desc = NewDescriptor("registerVisitor", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("homeChainID", smartcontract.IntegerType)) + md = NewMethodAndPrice(f.registerVisitor, 1<<16, callflag.States|callflag.AllowNotify) + f.AddMethod(md, desc) + + // unregisterVisitor method (committee only) + desc = NewDescriptor("unregisterVisitor", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.unregisterVisitor, 1<<16, callflag.States|callflag.AllowNotify) + f.AddMethod(md, desc) + + // isVisitor method + desc = NewDescriptor("isVisitor", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.isVisitor, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // getHomeChain method + desc = NewDescriptor("getHomeChain", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.getHomeChain, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // getInterChainDebt method + desc = NewDescriptor("getInterChainDebt", smartcontract.IntegerType, + manifest.NewParameter("chainID", smartcontract.IntegerType)) + md = NewMethodAndPrice(f.getInterChainDebt, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // settleDebt method (committee only) + desc = NewDescriptor("settleDebt", smartcontract.BoolType, + manifest.NewParameter("chainID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + md = NewMethodAndPrice(f.settleDebt, 1<<16, callflag.States|callflag.AllowNotify) + f.AddMethod(md, desc) + + // grantAsylum method (committee only) + desc = NewDescriptor("grantAsylum", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("homeChainID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(f.grantAsylum, 1<<16, callflag.States|callflag.AllowNotify) + f.AddMethod(md, desc) + + // revokeAsylum method (committee only) + desc = NewDescriptor("revokeAsylum", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.revokeAsylum, 1<<16, callflag.States|callflag.AllowNotify) + f.AddMethod(md, desc) + + // hasAsylum method + desc = NewDescriptor("hasAsylum", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.hasAsylum, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // getAsylumInfo method + desc = NewDescriptor("getAsylumInfo", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.getAsylumInfo, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // naturalize method (committee only) + desc = NewDescriptor("naturalize", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("originalHomeChain", smartcontract.IntegerType)) + md = NewMethodAndPrice(f.naturalize, 1<<16, callflag.States|callflag.AllowNotify) + f.AddMethod(md, desc) + + // isNaturalizedCitizen method + desc = NewDescriptor("isNaturalizedCitizen", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.isNaturalizedCitizen, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // getNaturalizationInfo method + desc = NewDescriptor("getNaturalizationInfo", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(f.getNaturalizationInfo, 1<<15, callflag.ReadStates) + f.AddMethod(md, desc) + + // Events + eDesc := NewEventDescriptor(VisitorRegisteredEvent, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("homeChainID", smartcontract.IntegerType)) + f.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VisitorUnregisteredEvent, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + f.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(FeePercentChangedEvent, + manifest.NewParameter("oldPercent", smartcontract.IntegerType), + manifest.NewParameter("newPercent", smartcontract.IntegerType)) + f.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(DebtSettledEvent, + manifest.NewParameter("chainID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("remaining", smartcontract.IntegerType)) + f.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AsylumGrantedEvent, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("homeChainID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + f.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AsylumRevokedEvent, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + f.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(CitizenNaturalizedEvent, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("originalHomeChain", smartcontract.IntegerType)) + f.AddEvent(NewEvent(eDesc)) + + return f +} + +// Metadata returns contract metadata. +func (f *Federation) Metadata() *interop.ContractMD { + return &f.ContractMD +} + +// Initialize initializes Federation contract at the specified hardfork. +func (f *Federation) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != f.ActiveIn() { + return nil + } + + // Initialize default fee percent + f.setFeePercentInternal(ic.DAO, defaultVisitingFeePercent) + + return nil +} + +// InitializeCache fills native Federation cache from DAO on node restart. +func (f *Federation) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + return nil +} + +// OnPersist implements the Contract interface. +func (f *Federation) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (f *Federation) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn returns the hardfork this contract activates in. +func (f *Federation) ActiveIn() *config.Hardfork { + var hf = config.HFFaun + return &hf +} + +// Storage key helpers + +func makeFeePercentKey() []byte { + return []byte{prefixVisitingFeePercent} +} + +func makeVisitorKey(owner util.Uint160) []byte { + key := make([]byte, 1+util.Uint160Size) + key[0] = prefixVisitorRegistry + copy(key[1:], owner.BytesBE()) + return key +} + +func makeDebtKey(chainID uint32) []byte { + key := make([]byte, 5) + key[0] = prefixInterChainDebt + binary.BigEndian.PutUint32(key[1:], chainID) + return key +} + +func makeAsylumKey(owner util.Uint160) []byte { + key := make([]byte, 1+util.Uint160Size) + key[0] = prefixAsylumRegistry + copy(key[1:], owner.BytesBE()) + return key +} + +func makeNaturalizedKey(owner util.Uint160) []byte { + key := make([]byte, 1+util.Uint160Size) + key[0] = prefixNaturalizedCitizen + copy(key[1:], owner.BytesBE()) + return key +} + +// Internal storage methods + +func (f *Federation) getFeePercentInternal(d *dao.Simple) uint8 { + si := d.GetStorageItem(f.ID, makeFeePercentKey()) + if si == nil { + return defaultVisitingFeePercent + } + return si[0] +} + +func (f *Federation) setFeePercentInternal(d *dao.Simple, percent uint8) { + d.PutStorageItem(f.ID, makeFeePercentKey(), []byte{percent}) +} + +func (f *Federation) getVisitorChainInternal(d *dao.Simple, owner util.Uint160) (uint32, bool) { + si := d.GetStorageItem(f.ID, makeVisitorKey(owner)) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint32(si), true +} + +func (f *Federation) setVisitorChainInternal(d *dao.Simple, owner util.Uint160, chainID uint32) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, chainID) + d.PutStorageItem(f.ID, makeVisitorKey(owner), buf) +} + +func (f *Federation) deleteVisitorInternal(d *dao.Simple, owner util.Uint160) { + d.DeleteStorageItem(f.ID, makeVisitorKey(owner)) +} + +func (f *Federation) getDebtInternal(d *dao.Simple, chainID uint32) *big.Int { + si := d.GetStorageItem(f.ID, makeDebtKey(chainID)) + if si == nil { + return big.NewInt(0) + } + return new(big.Int).SetBytes(si) +} + +func (f *Federation) setDebtInternal(d *dao.Simple, chainID uint32, amount *big.Int) { + if amount.Sign() <= 0 { + d.DeleteStorageItem(f.ID, makeDebtKey(chainID)) + return + } + d.PutStorageItem(f.ID, makeDebtKey(chainID), amount.Bytes()) +} + +func (f *Federation) addDebtInternal(d *dao.Simple, chainID uint32, amount *big.Int) { + current := f.getDebtInternal(d, chainID) + newDebt := new(big.Int).Add(current, amount) + f.setDebtInternal(d, chainID, newDebt) +} + +// Asylum record storage format: homeChainID (4 bytes) + grantedAt (4 bytes) + reason (variable) +func (f *Federation) getAsylumInternal(d *dao.Simple, owner util.Uint160) (homeChainID uint32, grantedAt uint32, reason string, exists bool) { + si := d.GetStorageItem(f.ID, makeAsylumKey(owner)) + if si == nil || len(si) < 8 { + return 0, 0, "", false + } + homeChainID = binary.BigEndian.Uint32(si[0:4]) + grantedAt = binary.BigEndian.Uint32(si[4:8]) + if len(si) > 8 { + reason = string(si[8:]) + } + return homeChainID, grantedAt, reason, true +} + +func (f *Federation) setAsylumInternal(d *dao.Simple, owner util.Uint160, homeChainID uint32, grantedAt uint32, reason string) { + buf := make([]byte, 8+len(reason)) + binary.BigEndian.PutUint32(buf[0:4], homeChainID) + binary.BigEndian.PutUint32(buf[4:8], grantedAt) + copy(buf[8:], reason) + d.PutStorageItem(f.ID, makeAsylumKey(owner), buf) +} + +func (f *Federation) deleteAsylumInternal(d *dao.Simple, owner util.Uint160) { + d.DeleteStorageItem(f.ID, makeAsylumKey(owner)) +} + +func (f *Federation) hasAsylumInternal(d *dao.Simple, owner util.Uint160) bool { + si := d.GetStorageItem(f.ID, makeAsylumKey(owner)) + return si != nil +} + +// Naturalization record storage format: originalHomeChain (4 bytes) + naturalizedAt (4 bytes) +func (f *Federation) getNaturalizedInternal(d *dao.Simple, owner util.Uint160) (originalHomeChain uint32, naturalizedAt uint32, exists bool) { + si := d.GetStorageItem(f.ID, makeNaturalizedKey(owner)) + if si == nil || len(si) < 8 { + return 0, 0, false + } + originalHomeChain = binary.BigEndian.Uint32(si[0:4]) + naturalizedAt = binary.BigEndian.Uint32(si[4:8]) + return originalHomeChain, naturalizedAt, true +} + +func (f *Federation) setNaturalizedInternal(d *dao.Simple, owner util.Uint160, originalHomeChain uint32, naturalizedAt uint32) { + buf := make([]byte, 8) + binary.BigEndian.PutUint32(buf[0:4], originalHomeChain) + binary.BigEndian.PutUint32(buf[4:8], naturalizedAt) + d.PutStorageItem(f.ID, makeNaturalizedKey(owner), buf) +} + +func (f *Federation) isNaturalizedInternal(d *dao.Simple, owner util.Uint160) bool { + si := d.GetStorageItem(f.ID, makeNaturalizedKey(owner)) + return si != nil +} + +// Contract methods + +func (f *Federation) getFeePercent(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + percent := f.getFeePercentInternal(ic.DAO) + return stackitem.NewBigInteger(big.NewInt(int64(percent))) +} + +func (f *Federation) setFeePercent(ic *interop.Context, args []stackitem.Item) stackitem.Item { + percent := toBigInt(args[0]).Int64() + + // Validate percent + if percent < 0 || percent > 100 { + panic(ErrInvalidFeePercent) + } + + // Check committee + if !f.Tutus.CheckCommittee(ic) { + panic(ErrNotCommittee) + } + + // Get old value for event + oldPercent := f.getFeePercentInternal(ic.DAO) + + // Set new value + f.setFeePercentInternal(ic.DAO, uint8(percent)) + + // Emit event + err := ic.AddNotification(f.Hash, FeePercentChangedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(oldPercent))), + stackitem.NewBigInteger(big.NewInt(percent)), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (f *Federation) registerVisitor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + chainID := uint32(toBigInt(args[1]).Int64()) + + // Validate chain ID + if chainID == 0 { + panic(ErrInvalidChainID) + } + + // Check committee + if !f.Tutus.CheckCommittee(ic) { + panic(ErrNotCommittee) + } + + // Check if already registered + if _, exists := f.getVisitorChainInternal(ic.DAO, owner); exists { + panic(ErrVisitorAlreadyExists) + } + + // Register visitor + f.setVisitorChainInternal(ic.DAO, owner, chainID) + + // Emit event + err := ic.AddNotification(f.Hash, VisitorRegisteredEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(owner.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(chainID))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (f *Federation) unregisterVisitor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + // Check committee + if !f.Tutus.CheckCommittee(ic) { + panic(ErrNotCommittee) + } + + // Check if registered + if _, exists := f.getVisitorChainInternal(ic.DAO, owner); !exists { + panic(ErrVisitorNotFound) + } + + // Unregister visitor + f.deleteVisitorInternal(ic.DAO, owner) + + // Emit event + err := ic.AddNotification(f.Hash, VisitorUnregisteredEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(owner.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (f *Federation) isVisitor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + _, exists := f.getVisitorChainInternal(ic.DAO, owner) + return stackitem.NewBool(exists) +} + +func (f *Federation) getHomeChain(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + chainID, exists := f.getVisitorChainInternal(ic.DAO, owner) + if !exists { + return stackitem.NewBigInteger(big.NewInt(0)) + } + return stackitem.NewBigInteger(big.NewInt(int64(chainID))) +} + +func (f *Federation) getInterChainDebt(ic *interop.Context, args []stackitem.Item) stackitem.Item { + chainID := uint32(toBigInt(args[0]).Int64()) + debt := f.getDebtInternal(ic.DAO, chainID) + return stackitem.NewBigInteger(debt) +} + +func (f *Federation) settleDebt(ic *interop.Context, args []stackitem.Item) stackitem.Item { + chainID := uint32(toBigInt(args[0]).Int64()) + amount := toBigInt(args[1]) + + // Validate chain ID + if chainID == 0 { + panic(ErrInvalidChainID) + } + + // Check committee + if !f.Tutus.CheckCommittee(ic) { + panic(ErrNotCommittee) + } + + // Get current debt + currentDebt := f.getDebtInternal(ic.DAO, chainID) + + // Check sufficient debt + if currentDebt.Cmp(amount) < 0 { + panic(ErrInsufficientDebt) + } + + // Subtract settled amount + remaining := new(big.Int).Sub(currentDebt, amount) + f.setDebtInternal(ic.DAO, chainID, remaining) + + // Emit event + err := ic.AddNotification(f.Hash, DebtSettledEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(chainID))), + stackitem.NewBigInteger(amount), + stackitem.NewBigInteger(remaining), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// Asylum methods + +func (f *Federation) grantAsylum(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + homeChainID := uint32(toBigInt(args[1]).Int64()) + reason := toString(args[2]) + + // Validate chain ID + if homeChainID == 0 { + panic(ErrInvalidChainID) + } + + // Check committee + if !f.Tutus.CheckCommittee(ic) { + panic(ErrNotCommittee) + } + + // Check if already has asylum + if f.hasAsylumInternal(ic.DAO, owner) { + panic(ErrAsylumAlreadyGranted) + } + + // Grant asylum + f.setAsylumInternal(ic.DAO, owner, homeChainID, ic.BlockHeight(), reason) + + // Emit event + err := ic.AddNotification(f.Hash, AsylumGrantedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(owner.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(homeChainID))), + stackitem.NewByteArray([]byte(reason)), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (f *Federation) revokeAsylum(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + // Check committee + if !f.Tutus.CheckCommittee(ic) { + panic(ErrNotCommittee) + } + + // Check if has asylum + if !f.hasAsylumInternal(ic.DAO, owner) { + panic(ErrAsylumNotFound) + } + + // Revoke asylum + f.deleteAsylumInternal(ic.DAO, owner) + + // Emit event + err := ic.AddNotification(f.Hash, AsylumRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(owner.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (f *Federation) hasAsylum(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + return stackitem.NewBool(f.hasAsylumInternal(ic.DAO, owner)) +} + +func (f *Federation) getAsylumInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + homeChainID, grantedAt, reason, exists := f.getAsylumInternal(ic.DAO, owner) + if !exists { + return stackitem.Null{} + } + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(homeChainID))), + stackitem.NewBigInteger(big.NewInt(int64(grantedAt))), + stackitem.NewByteArray([]byte(reason)), + }) +} + +// Naturalization methods + +func (f *Federation) naturalize(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + originalHomeChain := uint32(toBigInt(args[1]).Int64()) + + // Check committee + if !f.Tutus.CheckCommittee(ic) { + panic(ErrNotCommittee) + } + + // Check if already naturalized + if f.isNaturalizedInternal(ic.DAO, owner) { + panic(ErrAlreadyNaturalized) + } + + // Naturalize the citizen + f.setNaturalizedInternal(ic.DAO, owner, originalHomeChain, ic.BlockHeight()) + + // Emit event + err := ic.AddNotification(f.Hash, CitizenNaturalizedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(owner.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(originalHomeChain))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (f *Federation) isNaturalizedCitizen(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + return stackitem.NewBool(f.isNaturalizedInternal(ic.DAO, owner)) +} + +func (f *Federation) getNaturalizationInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + originalHomeChain, naturalizedAt, exists := f.getNaturalizedInternal(ic.DAO, owner) + if !exists { + return stackitem.Null{} + } + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(originalHomeChain))), + stackitem.NewBigInteger(big.NewInt(int64(naturalizedAt))), + }) +} + +// Public methods for cross-native access + +// GetVisitingFeePercent returns the visiting fee percent (for cross-native access). +func (f *Federation) GetVisitingFeePercent(d *dao.Simple) uint8 { + return f.getFeePercentInternal(d) +} + +// IsVisitor checks if an address is a registered visitor (for cross-native access). +func (f *Federation) IsVisitor(d *dao.Simple, owner util.Uint160) bool { + _, exists := f.getVisitorChainInternal(d, owner) + return exists +} + +// GetHomeChain returns the home chain ID for a visitor (for cross-native access). +func (f *Federation) GetHomeChain(d *dao.Simple, owner util.Uint160) uint32 { + chainID, _ := f.getVisitorChainInternal(d, owner) + return chainID +} + +// AddInterChainDebt adds to the inter-chain debt for a specific chain (for cross-native access). +func (f *Federation) AddInterChainDebt(d *dao.Simple, chainID uint32, amount *big.Int) { + f.addDebtInternal(d, chainID, amount) +} + +// GetInterChainDebt returns the inter-chain debt for a specific chain (for cross-native access). +func (f *Federation) GetInterChainDebt(d *dao.Simple, chainID uint32) *big.Int { + return f.getDebtInternal(d, chainID) +} + +// Address returns the contract's script hash. +func (f *Federation) Address() util.Uint160 { + return f.Hash +} + +// HasAsylum checks if an address has asylum status (for cross-native access). +func (f *Federation) HasAsylum(d *dao.Simple, owner util.Uint160) bool { + return f.hasAsylumInternal(d, owner) +} + +// IsNaturalizedCitizen checks if an address is a naturalized citizen (for cross-native access). +func (f *Federation) IsNaturalizedCitizen(d *dao.Simple, owner util.Uint160) bool { + return f.isNaturalizedInternal(d, owner) +} diff --git a/pkg/core/native/interop.go b/pkg/core/native/interop.go index 56dfb0b..459a6d6 100644 --- a/pkg/core/native/interop.go +++ b/pkg/core/native/interop.go @@ -4,12 +4,12 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Call calls the specified native contract method. diff --git a/pkg/core/native/invariants.go b/pkg/core/native/invariants.go index 3c5b5b2..3dde6dc 100644 --- a/pkg/core/native/invariants.go +++ b/pkg/core/native/invariants.go @@ -1,528 +1,528 @@ -package native - -import ( - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" -) - -// ARCH-003: Formal Verification Invariants -// This file documents critical system invariants that must hold at all times. -// These invariants serve multiple purposes: -// 1. Runtime verification during testing and canary deployments -// 2. Documentation for formal verification tools (TLA+, Coq, etc.) -// 3. Post-deployment monitoring and alerting - -// InvariantCategory categorizes invariants by domain. -type InvariantCategory uint8 - -const ( - InvariantCategoryToken InvariantCategory = iota - InvariantCategoryIdentity - InvariantCategoryGovernance - InvariantCategoryEconomic - InvariantCategorySecurity - InvariantCategoryCrossContract -) - -// InvariantSeverity indicates the impact of violation. -type InvariantSeverity uint8 - -const ( - // InvariantSeverityCritical means system halt required - InvariantSeverityCritical InvariantSeverity = iota - // InvariantSeverityHigh means immediate investigation required - InvariantSeverityHigh - // InvariantSeverityMedium means potential issue to investigate - InvariantSeverityMedium - // InvariantSeverityLow means informational anomaly - InvariantSeverityLow -) - -// Invariant represents a system invariant that must hold. -type Invariant struct { - ID string - Name string - Description string - Category InvariantCategory - Severity InvariantSeverity - // FormalSpec is TLA+/Coq-style formal specification - FormalSpec string -} - -// InvariantViolation records a violation of a system invariant. -type InvariantViolation struct { - InvariantID string - BlockHeight uint32 - Details string - ActualValue string - ExpectedSpec string -} - -// Invariant violations are critical errors. -var ( - ErrInvariantViolation = errors.New("invariant violation detected") - ErrTokenSupplyMismatch = errors.New("token supply invariant violated") - ErrVitaUniqueness = errors.New("vita uniqueness invariant violated") - ErrRightsConsistency = errors.New("rights consistency invariant violated") - ErrBalanceNonNegative = errors.New("balance non-negativity invariant violated") - ErrCrossContractConsistency = errors.New("cross-contract consistency invariant violated") -) - -// CriticalInvariants defines all system invariants that must always hold. -var CriticalInvariants = []Invariant{ - // ============================================ - // TOKEN INVARIANTS - // ============================================ - { - ID: "TOK-001", - Name: "VTS Total Supply Conservation", - Description: "Sum of all VTS balances must equal total supply minus burned amount", - Category: InvariantCategoryToken, - Severity: InvariantSeverityCritical, - FormalSpec: ` - INVARIANT VTSSupplyConservation == - \A state \in ValidStates: - Sum({state.vts.balance[addr] : addr \in DOMAIN state.vts.balance}) - = state.vts.totalSupply - state.vts.burnedAmount - `, - }, - { - ID: "TOK-002", - Name: "Non-Negative Balances", - Description: "No token balance can ever be negative", - Category: InvariantCategoryToken, - Severity: InvariantSeverityCritical, - FormalSpec: ` - INVARIANT NonNegativeBalances == - \A state \in ValidStates: - \A addr \in DOMAIN state.vts.balance: - state.vts.balance[addr] >= 0 - `, - }, - { - ID: "TOK-003", - Name: "Lub Total Supply Conservation", - Description: "Lub supply follows predictable generation schedule", - Category: InvariantCategoryToken, - Severity: InvariantSeverityCritical, - FormalSpec: ` - INVARIANT LubSupplyConservation == - \A state \in ValidStates: - state.lub.totalSupply <= MaxLubSupply - /\ Sum({state.lub.balance[addr] : addr \in DOMAIN state.lub.balance}) - = state.lub.totalSupply - `, - }, - - // ============================================ - // IDENTITY INVARIANTS - // ============================================ - { - ID: "IDN-001", - Name: "Vita Uniqueness Per Person", - Description: "Each natural person can hold at most one Vita token globally", - Category: InvariantCategoryIdentity, - Severity: InvariantSeverityCritical, - FormalSpec: ` - INVARIANT VitaUniqueness == - \A state \in ValidStates: - \A p1, p2 \in state.vita.tokens: - p1.biometricHash = p2.biometricHash => p1.tokenID = p2.tokenID - `, - }, - { - ID: "IDN-002", - Name: "Vita Non-Transferability", - Description: "Vita tokens cannot be transferred except through death/recovery", - Category: InvariantCategoryIdentity, - Severity: InvariantSeverityCritical, - FormalSpec: ` - INVARIANT VitaNonTransferable == - \A state, state' \in ValidStates: - \A vita \in state.vita.tokens: - state'.vita.tokens[vita.tokenID].owner # vita.owner - => state'.vita.tokens[vita.tokenID].status \in {Suspended, Revoked} - \/ vita.owner.status = Deceased - `, - }, - { - ID: "IDN-003", - Name: "Vita Count Equals Active Citizens", - Description: "Active Vita count must match registered citizen count", - Category: InvariantCategoryIdentity, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT VitaCountConsistency == - \A state \in ValidStates: - Cardinality({v \in state.vita.tokens : v.status = Active}) - = state.vita.activeCount - `, - }, - - // ============================================ - // GOVERNANCE INVARIANTS - // ============================================ - { - ID: "GOV-001", - Name: "One Person One Vote", - Description: "Each Vita holder can cast at most one vote per proposal", - Category: InvariantCategoryGovernance, - Severity: InvariantSeverityCritical, - FormalSpec: ` - INVARIANT OnePersonOneVote == - \A state \in ValidStates: - \A p \in state.eligere.proposals: - \A v \in state.vita.tokens: - Cardinality({vote \in p.votes : vote.vitaID = v.tokenID}) <= 1 - `, - }, - { - ID: "GOV-002", - Name: "Voting Age Enforcement", - Description: "Only Vita holders >= voting age can vote", - Category: InvariantCategoryGovernance, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT VotingAgeEnforced == - \A state \in ValidStates: - \A vote \in AllVotes(state): - AgeOf(vote.vitaID, state.currentBlock) >= state.config.votingAge - `, - }, - { - ID: "GOV-003", - Name: "Committee Authority Separation", - Description: "Domain committees have authority only within their domain", - Category: InvariantCategoryGovernance, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT CommitteeAuthoritySeparation == - \A state \in ValidStates: - \A action \in state.actions: - action.requiredDomain = DomainHealth - => action.authorizer \in state.committees.health - `, - }, - - // ============================================ - // ECONOMIC INVARIANTS - // ============================================ - { - ID: "ECO-001", - Name: "Tribute Conservation", - Description: "Total tribute collected equals sum of redistributions plus treasury", - Category: InvariantCategoryEconomic, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT TributeConservation == - \A state \in ValidStates: - state.tribute.totalCollected - = state.tribute.totalRedistributed + state.treasury.tributeBalance - `, - }, - { - ID: "ECO-002", - Name: "Investment Opportunity Bounds", - Description: "Investment totals cannot exceed opportunity limits", - Category: InvariantCategoryEconomic, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT InvestmentBounds == - \A state \in ValidStates: - \A opp \in state.collocatio.opportunities: - Sum({inv.amount : inv \in opp.investments}) <= opp.maxAmount - `, - }, - - // ============================================ - // SECURITY INVARIANTS - // ============================================ - { - ID: "SEC-001", - Name: "Rights Restriction Requires Due Process", - Description: "Rights can only be restricted with valid judicial order", - Category: InvariantCategorySecurity, - Severity: InvariantSeverityCritical, - FormalSpec: ` - INVARIANT RightsRestrictionDueProcess == - \A state \in ValidStates: - \A restriction \in state.lex.restrictions: - restriction.caseID # "" - /\ restriction.authorizedBy \in state.roles.judges - /\ restriction.expiresAt > state.currentBlock - `, - }, - { - ID: "SEC-002", - Name: "Role Expiry Enforcement", - Description: "Expired roles grant no permissions", - Category: InvariantCategorySecurity, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT ExpiredRolesInactive == - \A state \in ValidStates: - \A role \in state.roleRegistry.assignments: - role.expiresAt <= state.currentBlock - => ~HasPermission(role.holder, role.roleID, state) - `, - }, - { - ID: "SEC-003", - Name: "Circuit Breaker Effectiveness", - Description: "Open circuit breakers block all protected operations", - Category: InvariantCategorySecurity, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT CircuitBreakerEffective == - \A state \in ValidStates: - \A breaker \in state.circuitBreakers: - breaker.state = Open - => ~\E op \in state.operations: - op.protected = breaker.name /\ op.status = Completed - `, - }, - - // ============================================ - // CROSS-CONTRACT INVARIANTS - // ============================================ - { - ID: "XCT-001", - Name: "Annos-Vita Consistency", - Description: "Every Annos lifespan record has corresponding Vita token", - Category: InvariantCategoryCrossContract, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT AnnosVitaConsistency == - \A state \in ValidStates: - \A record \in state.annos.lifespans: - \E vita \in state.vita.tokens: - vita.tokenID = record.vitaID - `, - }, - { - ID: "XCT-002", - Name: "Scire-Vita Consistency", - Description: "Education accounts require valid Vita holders", - Category: InvariantCategoryCrossContract, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT ScireVitaConsistency == - \A state \in ValidStates: - \A account \in state.scire.accounts: - \E vita \in state.vita.tokens: - vita.tokenID = account.vitaID /\ vita.status = Active - `, - }, - { - ID: "XCT-003", - Name: "Salus-Vita Consistency", - Description: "Healthcare accounts require valid Vita holders", - Category: InvariantCategoryCrossContract, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT SalusVitaConsistency == - \A state \in ValidStates: - \A account \in state.salus.accounts: - \E vita \in state.vita.tokens: - vita.tokenID = account.vitaID - `, - }, - { - ID: "XCT-004", - Name: "Federation Debt Balance", - Description: "Inter-chain debts sum to zero globally", - Category: InvariantCategoryCrossContract, - Severity: InvariantSeverityHigh, - FormalSpec: ` - INVARIANT FederationDebtBalance == - \A globalState \in ValidGlobalStates: - Sum({chain.federation.debt[other] : - chain \in globalState.chains, - other \in globalState.chains}) - = 0 - `, - }, -} - -// InvariantChecker provides runtime invariant verification. -type InvariantChecker struct { - contractID int32 - violations []InvariantViolation -} - -// NewInvariantChecker creates a new invariant checker. -func NewInvariantChecker(contractID int32) *InvariantChecker { - return &InvariantChecker{ - contractID: contractID, - violations: make([]InvariantViolation, 0), - } -} - -// CheckVTSSupplyInvariant verifies VTS supply conservation (TOK-001). -func (ic *InvariantChecker) CheckVTSSupplyInvariant(d *dao.Simple, vtsContractID int32) error { - // Get total supply - supplyKey := []byte{0x11} // VTS total supply prefix - supplyData := d.GetStorageItem(vtsContractID, supplyKey) - if supplyData == nil { - return nil // Not initialized - } - totalSupply := new(big.Int).SetBytes(supplyData) - - // Sum all balances - balanceSum := big.NewInt(0) - balancePrefix := []byte{0x14} // VTS balance prefix - - d.Seek(vtsContractID, storage.SeekRange{Prefix: balancePrefix}, func(k, v []byte) bool { - balance := new(big.Int).SetBytes(v) - balanceSum.Add(balanceSum, balance) - return true - }) - - if totalSupply.Cmp(balanceSum) != 0 { - ic.recordViolation("TOK-001", 0, "Supply mismatch", - balanceSum.String(), totalSupply.String()) - return ErrTokenSupplyMismatch - } - - return nil -} - -// CheckVitaUniquenessInvariant verifies Vita uniqueness (IDN-001). -func (ic *InvariantChecker) CheckVitaUniquenessInvariant(d *dao.Simple, vitaContractID int32) error { - biometricHashes := make(map[util.Uint256]uint64) - tokenPrefix := []byte{0x01} // Vita token prefix - - var violation bool - d.Seek(vitaContractID, storage.SeekRange{Prefix: tokenPrefix}, func(k, v []byte) bool { - if len(v) < 32 { - return true - } - // Extract biometric hash from token data - var bioHash util.Uint256 - copy(bioHash[:], v[:32]) - - if existingID, exists := biometricHashes[bioHash]; exists { - ic.recordViolation("IDN-001", 0, "Duplicate biometric hash", - "Multiple tokens", "Single token per biometric") - _ = existingID // Reference to suppress unused warning - violation = true - return false - } - - // Extract token ID from key - if len(k) >= 8 { - tokenID := uint64(k[0])<<56 | uint64(k[1])<<48 | uint64(k[2])<<40 | - uint64(k[3])<<32 | uint64(k[4])<<24 | uint64(k[5])<<16 | - uint64(k[6])<<8 | uint64(k[7]) - biometricHashes[bioHash] = tokenID - } - return true - }) - - if violation { - return ErrVitaUniqueness - } - return nil -} - -// CheckNonNegativeBalances verifies no negative balances exist (TOK-002). -func (ic *InvariantChecker) CheckNonNegativeBalances(d *dao.Simple, contractID int32, balancePrefix byte) error { - prefix := []byte{balancePrefix} - - var violation bool - d.Seek(contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - balance := new(big.Int).SetBytes(v) - if balance.Sign() < 0 { - ic.recordViolation("TOK-002", 0, "Negative balance detected", - balance.String(), ">= 0") - violation = true - return false - } - return true - }) - - if violation { - return ErrBalanceNonNegative - } - return nil -} - -// CheckRightsConsistency verifies rights restrictions have valid due process (SEC-001). -func (ic *InvariantChecker) CheckRightsConsistency(d *dao.Simple, lexContractID int32, currentBlock uint32) error { - restrictionPrefix := []byte{0x20} // Lex restriction prefix - - var violation bool - d.Seek(lexContractID, storage.SeekRange{Prefix: restrictionPrefix}, func(k, v []byte) bool { - if len(v) < 40 { - return true - } - - // Check if restriction has case ID (non-empty) - caseIDLen := v[0] - if caseIDLen == 0 { - ic.recordViolation("SEC-001", currentBlock, - "Rights restriction without case ID", "empty", "non-empty case ID") - violation = true - return false - } - - return true - }) - - if violation { - return ErrRightsConsistency - } - return nil -} - -// recordViolation records an invariant violation. -func (ic *InvariantChecker) recordViolation(invariantID string, blockHeight uint32, details, actual, expected string) { - ic.violations = append(ic.violations, InvariantViolation{ - InvariantID: invariantID, - BlockHeight: blockHeight, - Details: details, - ActualValue: actual, - ExpectedSpec: expected, - }) -} - -// GetViolations returns all recorded violations. -func (ic *InvariantChecker) GetViolations() []InvariantViolation { - return ic.violations -} - -// ClearViolations clears recorded violations. -func (ic *InvariantChecker) ClearViolations() { - ic.violations = make([]InvariantViolation, 0) -} - -// RunAllCriticalChecks runs all critical invariant checks. -func (ic *InvariantChecker) RunAllCriticalChecks(d *dao.Simple, contracts ContractIDs, currentBlock uint32) []InvariantViolation { - ic.ClearViolations() - - // Run critical checks - _ = ic.CheckVTSSupplyInvariant(d, contracts.VTS) - _ = ic.CheckVitaUniquenessInvariant(d, contracts.Vita) - _ = ic.CheckNonNegativeBalances(d, contracts.VTS, 0x14) - _ = ic.CheckNonNegativeBalances(d, contracts.Tutus, 0x14) - _ = ic.CheckNonNegativeBalances(d, contracts.Lub, 0x14) - _ = ic.CheckRightsConsistency(d, contracts.Lex, currentBlock) - - return ic.GetViolations() -} - -// ContractIDs holds contract identifiers for invariant checking. -type ContractIDs struct { - VTS int32 - Vita int32 - Tutus int32 - Lub int32 - Lex int32 - Annos int32 - Scire int32 - Salus int32 -} +package native + +import ( + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" +) + +// ARCH-003: Formal Verification Invariants +// This file documents critical system invariants that must hold at all times. +// These invariants serve multiple purposes: +// 1. Runtime verification during testing and canary deployments +// 2. Documentation for formal verification tools (TLA+, Coq, etc.) +// 3. Post-deployment monitoring and alerting + +// InvariantCategory categorizes invariants by domain. +type InvariantCategory uint8 + +const ( + InvariantCategoryToken InvariantCategory = iota + InvariantCategoryIdentity + InvariantCategoryGovernance + InvariantCategoryEconomic + InvariantCategorySecurity + InvariantCategoryCrossContract +) + +// InvariantSeverity indicates the impact of violation. +type InvariantSeverity uint8 + +const ( + // InvariantSeverityCritical means system halt required + InvariantSeverityCritical InvariantSeverity = iota + // InvariantSeverityHigh means immediate investigation required + InvariantSeverityHigh + // InvariantSeverityMedium means potential issue to investigate + InvariantSeverityMedium + // InvariantSeverityLow means informational anomaly + InvariantSeverityLow +) + +// Invariant represents a system invariant that must hold. +type Invariant struct { + ID string + Name string + Description string + Category InvariantCategory + Severity InvariantSeverity + // FormalSpec is TLA+/Coq-style formal specification + FormalSpec string +} + +// InvariantViolation records a violation of a system invariant. +type InvariantViolation struct { + InvariantID string + BlockHeight uint32 + Details string + ActualValue string + ExpectedSpec string +} + +// Invariant violations are critical errors. +var ( + ErrInvariantViolation = errors.New("invariant violation detected") + ErrTokenSupplyMismatch = errors.New("token supply invariant violated") + ErrVitaUniqueness = errors.New("vita uniqueness invariant violated") + ErrRightsConsistency = errors.New("rights consistency invariant violated") + ErrBalanceNonNegative = errors.New("balance non-negativity invariant violated") + ErrCrossContractConsistency = errors.New("cross-contract consistency invariant violated") +) + +// CriticalInvariants defines all system invariants that must always hold. +var CriticalInvariants = []Invariant{ + // ============================================ + // TOKEN INVARIANTS + // ============================================ + { + ID: "TOK-001", + Name: "VTS Total Supply Conservation", + Description: "Sum of all VTS balances must equal total supply minus burned amount", + Category: InvariantCategoryToken, + Severity: InvariantSeverityCritical, + FormalSpec: ` + INVARIANT VTSSupplyConservation == + \A state \in ValidStates: + Sum({state.vts.balance[addr] : addr \in DOMAIN state.vts.balance}) + = state.vts.totalSupply - state.vts.burnedAmount + `, + }, + { + ID: "TOK-002", + Name: "Non-Negative Balances", + Description: "No token balance can ever be negative", + Category: InvariantCategoryToken, + Severity: InvariantSeverityCritical, + FormalSpec: ` + INVARIANT NonNegativeBalances == + \A state \in ValidStates: + \A addr \in DOMAIN state.vts.balance: + state.vts.balance[addr] >= 0 + `, + }, + { + ID: "TOK-003", + Name: "Lub Total Supply Conservation", + Description: "Lub supply follows predictable generation schedule", + Category: InvariantCategoryToken, + Severity: InvariantSeverityCritical, + FormalSpec: ` + INVARIANT LubSupplyConservation == + \A state \in ValidStates: + state.lub.totalSupply <= MaxLubSupply + /\ Sum({state.lub.balance[addr] : addr \in DOMAIN state.lub.balance}) + = state.lub.totalSupply + `, + }, + + // ============================================ + // IDENTITY INVARIANTS + // ============================================ + { + ID: "IDN-001", + Name: "Vita Uniqueness Per Person", + Description: "Each natural person can hold at most one Vita token globally", + Category: InvariantCategoryIdentity, + Severity: InvariantSeverityCritical, + FormalSpec: ` + INVARIANT VitaUniqueness == + \A state \in ValidStates: + \A p1, p2 \in state.vita.tokens: + p1.biometricHash = p2.biometricHash => p1.tokenID = p2.tokenID + `, + }, + { + ID: "IDN-002", + Name: "Vita Non-Transferability", + Description: "Vita tokens cannot be transferred except through death/recovery", + Category: InvariantCategoryIdentity, + Severity: InvariantSeverityCritical, + FormalSpec: ` + INVARIANT VitaNonTransferable == + \A state, state' \in ValidStates: + \A vita \in state.vita.tokens: + state'.vita.tokens[vita.tokenID].owner # vita.owner + => state'.vita.tokens[vita.tokenID].status \in {Suspended, Revoked} + \/ vita.owner.status = Deceased + `, + }, + { + ID: "IDN-003", + Name: "Vita Count Equals Active Citizens", + Description: "Active Vita count must match registered citizen count", + Category: InvariantCategoryIdentity, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT VitaCountConsistency == + \A state \in ValidStates: + Cardinality({v \in state.vita.tokens : v.status = Active}) + = state.vita.activeCount + `, + }, + + // ============================================ + // GOVERNANCE INVARIANTS + // ============================================ + { + ID: "GOV-001", + Name: "One Person One Vote", + Description: "Each Vita holder can cast at most one vote per proposal", + Category: InvariantCategoryGovernance, + Severity: InvariantSeverityCritical, + FormalSpec: ` + INVARIANT OnePersonOneVote == + \A state \in ValidStates: + \A p \in state.eligere.proposals: + \A v \in state.vita.tokens: + Cardinality({vote \in p.votes : vote.vitaID = v.tokenID}) <= 1 + `, + }, + { + ID: "GOV-002", + Name: "Voting Age Enforcement", + Description: "Only Vita holders >= voting age can vote", + Category: InvariantCategoryGovernance, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT VotingAgeEnforced == + \A state \in ValidStates: + \A vote \in AllVotes(state): + AgeOf(vote.vitaID, state.currentBlock) >= state.config.votingAge + `, + }, + { + ID: "GOV-003", + Name: "Committee Authority Separation", + Description: "Domain committees have authority only within their domain", + Category: InvariantCategoryGovernance, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT CommitteeAuthoritySeparation == + \A state \in ValidStates: + \A action \in state.actions: + action.requiredDomain = DomainHealth + => action.authorizer \in state.committees.health + `, + }, + + // ============================================ + // ECONOMIC INVARIANTS + // ============================================ + { + ID: "ECO-001", + Name: "Tribute Conservation", + Description: "Total tribute collected equals sum of redistributions plus treasury", + Category: InvariantCategoryEconomic, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT TributeConservation == + \A state \in ValidStates: + state.tribute.totalCollected + = state.tribute.totalRedistributed + state.treasury.tributeBalance + `, + }, + { + ID: "ECO-002", + Name: "Investment Opportunity Bounds", + Description: "Investment totals cannot exceed opportunity limits", + Category: InvariantCategoryEconomic, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT InvestmentBounds == + \A state \in ValidStates: + \A opp \in state.collocatio.opportunities: + Sum({inv.amount : inv \in opp.investments}) <= opp.maxAmount + `, + }, + + // ============================================ + // SECURITY INVARIANTS + // ============================================ + { + ID: "SEC-001", + Name: "Rights Restriction Requires Due Process", + Description: "Rights can only be restricted with valid judicial order", + Category: InvariantCategorySecurity, + Severity: InvariantSeverityCritical, + FormalSpec: ` + INVARIANT RightsRestrictionDueProcess == + \A state \in ValidStates: + \A restriction \in state.lex.restrictions: + restriction.caseID # "" + /\ restriction.authorizedBy \in state.roles.judges + /\ restriction.expiresAt > state.currentBlock + `, + }, + { + ID: "SEC-002", + Name: "Role Expiry Enforcement", + Description: "Expired roles grant no permissions", + Category: InvariantCategorySecurity, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT ExpiredRolesInactive == + \A state \in ValidStates: + \A role \in state.roleRegistry.assignments: + role.expiresAt <= state.currentBlock + => ~HasPermission(role.holder, role.roleID, state) + `, + }, + { + ID: "SEC-003", + Name: "Circuit Breaker Effectiveness", + Description: "Open circuit breakers block all protected operations", + Category: InvariantCategorySecurity, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT CircuitBreakerEffective == + \A state \in ValidStates: + \A breaker \in state.circuitBreakers: + breaker.state = Open + => ~\E op \in state.operations: + op.protected = breaker.name /\ op.status = Completed + `, + }, + + // ============================================ + // CROSS-CONTRACT INVARIANTS + // ============================================ + { + ID: "XCT-001", + Name: "Annos-Vita Consistency", + Description: "Every Annos lifespan record has corresponding Vita token", + Category: InvariantCategoryCrossContract, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT AnnosVitaConsistency == + \A state \in ValidStates: + \A record \in state.annos.lifespans: + \E vita \in state.vita.tokens: + vita.tokenID = record.vitaID + `, + }, + { + ID: "XCT-002", + Name: "Scire-Vita Consistency", + Description: "Education accounts require valid Vita holders", + Category: InvariantCategoryCrossContract, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT ScireVitaConsistency == + \A state \in ValidStates: + \A account \in state.scire.accounts: + \E vita \in state.vita.tokens: + vita.tokenID = account.vitaID /\ vita.status = Active + `, + }, + { + ID: "XCT-003", + Name: "Salus-Vita Consistency", + Description: "Healthcare accounts require valid Vita holders", + Category: InvariantCategoryCrossContract, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT SalusVitaConsistency == + \A state \in ValidStates: + \A account \in state.salus.accounts: + \E vita \in state.vita.tokens: + vita.tokenID = account.vitaID + `, + }, + { + ID: "XCT-004", + Name: "Federation Debt Balance", + Description: "Inter-chain debts sum to zero globally", + Category: InvariantCategoryCrossContract, + Severity: InvariantSeverityHigh, + FormalSpec: ` + INVARIANT FederationDebtBalance == + \A globalState \in ValidGlobalStates: + Sum({chain.federation.debt[other] : + chain \in globalState.chains, + other \in globalState.chains}) + = 0 + `, + }, +} + +// InvariantChecker provides runtime invariant verification. +type InvariantChecker struct { + contractID int32 + violations []InvariantViolation +} + +// NewInvariantChecker creates a new invariant checker. +func NewInvariantChecker(contractID int32) *InvariantChecker { + return &InvariantChecker{ + contractID: contractID, + violations: make([]InvariantViolation, 0), + } +} + +// CheckVTSSupplyInvariant verifies VTS supply conservation (TOK-001). +func (ic *InvariantChecker) CheckVTSSupplyInvariant(d *dao.Simple, vtsContractID int32) error { + // Get total supply + supplyKey := []byte{0x11} // VTS total supply prefix + supplyData := d.GetStorageItem(vtsContractID, supplyKey) + if supplyData == nil { + return nil // Not initialized + } + totalSupply := new(big.Int).SetBytes(supplyData) + + // Sum all balances + balanceSum := big.NewInt(0) + balancePrefix := []byte{0x14} // VTS balance prefix + + d.Seek(vtsContractID, storage.SeekRange{Prefix: balancePrefix}, func(k, v []byte) bool { + balance := new(big.Int).SetBytes(v) + balanceSum.Add(balanceSum, balance) + return true + }) + + if totalSupply.Cmp(balanceSum) != 0 { + ic.recordViolation("TOK-001", 0, "Supply mismatch", + balanceSum.String(), totalSupply.String()) + return ErrTokenSupplyMismatch + } + + return nil +} + +// CheckVitaUniquenessInvariant verifies Vita uniqueness (IDN-001). +func (ic *InvariantChecker) CheckVitaUniquenessInvariant(d *dao.Simple, vitaContractID int32) error { + biometricHashes := make(map[util.Uint256]uint64) + tokenPrefix := []byte{0x01} // Vita token prefix + + var violation bool + d.Seek(vitaContractID, storage.SeekRange{Prefix: tokenPrefix}, func(k, v []byte) bool { + if len(v) < 32 { + return true + } + // Extract biometric hash from token data + var bioHash util.Uint256 + copy(bioHash[:], v[:32]) + + if existingID, exists := biometricHashes[bioHash]; exists { + ic.recordViolation("IDN-001", 0, "Duplicate biometric hash", + "Multiple tokens", "Single token per biometric") + _ = existingID // Reference to suppress unused warning + violation = true + return false + } + + // Extract token ID from key + if len(k) >= 8 { + tokenID := uint64(k[0])<<56 | uint64(k[1])<<48 | uint64(k[2])<<40 | + uint64(k[3])<<32 | uint64(k[4])<<24 | uint64(k[5])<<16 | + uint64(k[6])<<8 | uint64(k[7]) + biometricHashes[bioHash] = tokenID + } + return true + }) + + if violation { + return ErrVitaUniqueness + } + return nil +} + +// CheckNonNegativeBalances verifies no negative balances exist (TOK-002). +func (ic *InvariantChecker) CheckNonNegativeBalances(d *dao.Simple, contractID int32, balancePrefix byte) error { + prefix := []byte{balancePrefix} + + var violation bool + d.Seek(contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + balance := new(big.Int).SetBytes(v) + if balance.Sign() < 0 { + ic.recordViolation("TOK-002", 0, "Negative balance detected", + balance.String(), ">= 0") + violation = true + return false + } + return true + }) + + if violation { + return ErrBalanceNonNegative + } + return nil +} + +// CheckRightsConsistency verifies rights restrictions have valid due process (SEC-001). +func (ic *InvariantChecker) CheckRightsConsistency(d *dao.Simple, lexContractID int32, currentBlock uint32) error { + restrictionPrefix := []byte{0x20} // Lex restriction prefix + + var violation bool + d.Seek(lexContractID, storage.SeekRange{Prefix: restrictionPrefix}, func(k, v []byte) bool { + if len(v) < 40 { + return true + } + + // Check if restriction has case ID (non-empty) + caseIDLen := v[0] + if caseIDLen == 0 { + ic.recordViolation("SEC-001", currentBlock, + "Rights restriction without case ID", "empty", "non-empty case ID") + violation = true + return false + } + + return true + }) + + if violation { + return ErrRightsConsistency + } + return nil +} + +// recordViolation records an invariant violation. +func (ic *InvariantChecker) recordViolation(invariantID string, blockHeight uint32, details, actual, expected string) { + ic.violations = append(ic.violations, InvariantViolation{ + InvariantID: invariantID, + BlockHeight: blockHeight, + Details: details, + ActualValue: actual, + ExpectedSpec: expected, + }) +} + +// GetViolations returns all recorded violations. +func (ic *InvariantChecker) GetViolations() []InvariantViolation { + return ic.violations +} + +// ClearViolations clears recorded violations. +func (ic *InvariantChecker) ClearViolations() { + ic.violations = make([]InvariantViolation, 0) +} + +// RunAllCriticalChecks runs all critical invariant checks. +func (ic *InvariantChecker) RunAllCriticalChecks(d *dao.Simple, contracts ContractIDs, currentBlock uint32) []InvariantViolation { + ic.ClearViolations() + + // Run critical checks + _ = ic.CheckVTSSupplyInvariant(d, contracts.VTS) + _ = ic.CheckVitaUniquenessInvariant(d, contracts.Vita) + _ = ic.CheckNonNegativeBalances(d, contracts.VTS, 0x14) + _ = ic.CheckNonNegativeBalances(d, contracts.Tutus, 0x14) + _ = ic.CheckNonNegativeBalances(d, contracts.Lub, 0x14) + _ = ic.CheckRightsConsistency(d, contracts.Lex, currentBlock) + + return ic.GetViolations() +} + +// ContractIDs holds contract identifiers for invariant checking. +type ContractIDs struct { + VTS int32 + Vita int32 + Tutus int32 + Lub int32 + Lex int32 + Annos int32 + Scire int32 + Salus int32 +} diff --git a/pkg/core/native/invocation_test.go b/pkg/core/native/invocation_test.go index 0566b67..b8e8a77 100644 --- a/pkg/core/native/invocation_test.go +++ b/pkg/core/native/invocation_test.go @@ -7,20 +7,20 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/ledger.go b/pkg/core/native/ledger.go index cf612f8..1668d48 100644 --- a/pkg/core/native/ledger.go +++ b/pkg/core/native/ledger.go @@ -4,18 +4,18 @@ import ( "fmt" "math" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) // Ledger provides an interface to blocks/transactions storage for smart diff --git a/pkg/core/native/lex.go b/pkg/core/native/lex.go index 3874cda..2ee8c70 100644 --- a/pkg/core/native/lex.go +++ b/pkg/core/native/lex.go @@ -6,17 +6,17 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Lex represents the Lex native contract for universal law and rights enforcement. diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 4b63ad2..0bf2368 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -11,25 +11,25 @@ import ( "math/big" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/contract" - istorage "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/util/bitfield" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/contract" + istorage "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util/bitfield" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Management is a contract-managing native contract. diff --git a/pkg/core/native/management_neotest_test.go b/pkg/core/native/management_neotest_test.go index 9d42638..269300f 100644 --- a/pkg/core/native/management_neotest_test.go +++ b/pkg/core/native/management_neotest_test.go @@ -4,18 +4,18 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/management_test.go b/pkg/core/native/management_test.go index cd72b11..acc2b00 100644 --- a/pkg/core/native/management_test.go +++ b/pkg/core/native/management_test.go @@ -4,17 +4,17 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/native_lub.go b/pkg/core/native/native_lub.go index c139d9b..5babee5 100644 --- a/pkg/core/native/native_lub.go +++ b/pkg/core/native/native_lub.go @@ -4,17 +4,17 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Lub represents Lub native contract (utility/fee token - lubrication). diff --git a/pkg/core/native/native_neo_test.go b/pkg/core/native/native_neo_test.go index 13d55ef..ccd7c4d 100644 --- a/pkg/core/native/native_neo_test.go +++ b/pkg/core/native/native_neo_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" ) func TestCandidate_Bytes(t *testing.T) { diff --git a/pkg/core/native/native_nep17.go b/pkg/core/native/native_nep17.go index ae556a3..5e5a598 100644 --- a/pkg/core/native/native_nep17.go +++ b/pkg/core/native/native_nep17.go @@ -6,18 +6,18 @@ import ( "math" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // prefixAccount is the standard prefix used to store account data. diff --git a/pkg/core/native/native_test/ancora_test.go b/pkg/core/native/native_test/ancora_test.go index 64a97fd..14e51e2 100644 --- a/pkg/core/native/native_test/ancora_test.go +++ b/pkg/core/native/native_test/ancora_test.go @@ -1,282 +1,282 @@ -package native_test - -import ( - "testing" - - "github.com/nspcc-dev/neo-go/pkg/crypto/hash" - "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -func newAncoraClient(t *testing.T) *tutustest.ContractInvoker { - return newNativeClient(t, nativenames.Ancora) -} - -// TestAncora_GetConfig tests the getConfig method. -func TestAncora_GetConfig(t *testing.T) { - c := newAncoraClient(t) - - c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { - require.Equal(t, 1, len(stack)) - arr, ok := stack[0].Value().([]stackitem.Item) - require.True(t, ok, "expected array result") - require.Equal(t, 7, len(arr)) // StateAnchorsConfig has 7 fields - - // Check default values - defaultAlgo, _ := arr[0].TryInteger() - require.Equal(t, int64(state.TreeAlgorithmSHA256), defaultAlgo.Int64()) - - maxProofDepth, _ := arr[1].TryInteger() - require.Equal(t, int64(32), maxProofDepth.Int64()) - - defaultMaxUpdates, _ := arr[2].TryInteger() - require.Equal(t, int64(10), defaultMaxUpdates.Int64()) - - defaultCooldown, _ := arr[3].TryInteger() - require.Equal(t, int64(1), defaultCooldown.Int64()) - - maxHistory, _ := arr[4].TryInteger() - require.Equal(t, int64(100), maxHistory.Int64()) - - erasureGrace, _ := arr[5].TryInteger() - require.Equal(t, int64(1000), erasureGrace.Int64()) - - attestValid, _ := arr[6].TryInteger() - require.Equal(t, int64(86400), attestValid.Int64()) - }, "getConfig") -} - -// TestAncora_GetDataRoot_NonExistent tests getting a non-existent data root. -func TestAncora_GetDataRoot_NonExistent(t *testing.T) { - c := newAncoraClient(t) - - // Non-existent root should return null - c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { - require.Equal(t, 1, len(stack)) - require.Nil(t, stack[0].Value(), "expected null for non-existent root") - }, "getDataRoot", uint64(999), uint32(state.DataTypeMedical)) -} - -// TestAncora_GetProvider_NonExistent tests getting a non-existent provider config. -func TestAncora_GetProvider_NonExistent(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - - // Non-existent provider should return null - c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { - require.Equal(t, 1, len(stack)) - require.Nil(t, stack[0].Value(), "expected null for non-existent provider") - }, "getProvider", uint32(state.DataTypeMedical), acc.ScriptHash()) -} - -// TestAncora_GetErasureInfo_NonExistent tests getting non-existent erasure info. -func TestAncora_GetErasureInfo_NonExistent(t *testing.T) { - c := newAncoraClient(t) - - // Non-existent erasure info should return null - c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { - require.Equal(t, 1, len(stack)) - require.Nil(t, stack[0].Value(), "expected null for non-existent erasure info") - }, "getErasureInfo", uint64(999), uint32(state.DataTypeMedical)) -} - -// TestAncora_IsProviderActive_NonExistent tests checking non-existent provider. -func TestAncora_IsProviderActive_NonExistent(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - - // Non-existent provider should return false - c.Invoke(t, false, "isProviderActive", uint32(state.DataTypeMedical), acc.ScriptHash()) -} - -// TestAncora_RegisterProvider_NoCommittee tests that non-committee cannot register providers. -func TestAncora_RegisterProvider_NoCommittee(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - randomInvoker := c.WithSigners(acc) - - // registerProvider takes: dataType, provider, description, maxUpdatesPerBlock, updateCooldown - randomInvoker.InvokeFail(t, "invalid committee signature", - "registerProvider", - uint32(state.DataTypeMedical), - acc.ScriptHash(), - "Test Healthcare Provider", - uint32(10), - uint32(1), - ) -} - -// TestAncora_RegisterProvider_CommitteeSuccess tests committee can register providers. -func TestAncora_RegisterProvider_CommitteeSuccess(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - providerAcc := e.NewAccount(t) - - // Register a healthcare provider (c is already a CommitteeInvoker) - c.Invoke(t, true, - "registerProvider", - uint32(state.DataTypeMedical), - providerAcc.ScriptHash(), - "Test Healthcare Provider", - uint32(10), - uint32(1), - ) - - // Verify provider is active - c.Invoke(t, true, "isProviderActive", uint32(state.DataTypeMedical), providerAcc.ScriptHash()) - - // Verify provider info - c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { - require.Equal(t, 1, len(stack)) - arr, ok := stack[0].Value().([]stackitem.Item) - require.True(t, ok, "expected array result") - require.Equal(t, 7, len(arr)) // ProviderConfig has 7 fields - - // Check dataType - dataType, _ := arr[0].TryInteger() - require.Equal(t, int64(state.DataTypeMedical), dataType.Int64()) - - // Check active status - active, _ := arr[4].TryBool() - require.True(t, active) - }, "getProvider", uint32(state.DataTypeMedical), providerAcc.ScriptHash()) -} - -// TestAncora_RevokeProvider_NoCommittee tests that non-committee cannot revoke providers. -func TestAncora_RevokeProvider_NoCommittee(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - randomInvoker := c.WithSigners(acc) - - randomInvoker.InvokeFail(t, "invalid committee signature", - "revokeProvider", - uint32(state.DataTypeMedical), - acc.ScriptHash(), - ) -} - -// TestAncora_UpdateDataRoot_VitaNotFound tests that updating root for non-existent Vita fails. -// The contract should fail when the Vita doesn't exist. -func TestAncora_UpdateDataRoot_VitaNotFound(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - randomInvoker := c.WithSigners(acc) - - root := hash.Sha256([]byte("test data")).BytesBE() - - // Should fail - when calling updateDataRoot with invalid Vita ID - // The error could be "vita not found" or "unauthorized" depending on check order - randomInvoker.InvokeFail(t, "", - "updateDataRoot", - uint64(999), - uint32(state.DataTypeMedical), - root, - uint64(100), - uint32(state.TreeAlgorithmSHA256), - "1.0.0", - []byte{}, - ) -} - -// Note: TestAncora_VerifyProof tests would require complex array serialization -// that the test framework doesn't support directly. Proof verification is tested -// via cross-contract integration tests. - -// TestAncora_RequestErasure_Unauthorized tests that erasure request requires Vita ownership. -// When Vita doesn't exist, OwnerOfInternal returns empty Uint160 and the caller -// fails the witness check, resulting in "unauthorized" error. -func TestAncora_RequestErasure_Unauthorized(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - randomInvoker := c.WithSigners(acc) - - // Should fail - caller is not the Vita owner (Vita doesn't exist) - randomInvoker.InvokeFail(t, "unauthorized", - "requestErasure", - uint64(999), - uint32(state.DataTypeMedical), - "GDPR Art. 17", - ) -} - -// TestAncora_ConfirmErasure_Unauthorized tests that unauthorized providers cannot confirm erasure. -func TestAncora_ConfirmErasure_Unauthorized(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - randomInvoker := c.WithSigners(acc) - - randomInvoker.InvokeFail(t, "unauthorized: caller is not authorized provider", - "confirmErasure", - uint64(1), - uint32(state.DataTypeMedical), - ) -} - -// TestAncora_DenyErasure_NoPending tests that denying non-existent erasure fails. -func TestAncora_DenyErasure_NoPending(t *testing.T) { - c := newAncoraClient(t) - e := c.Executor - - acc := e.NewAccount(t) - randomInvoker := c.WithSigners(acc) - - randomInvoker.InvokeFail(t, "no pending erasure request", - "denyErasure", - uint64(1), - uint32(state.DataTypeMedical), - "Legal hold", - ) -} - -// TestAncora_VerifyPortabilityAttestation_Invalid tests verification of invalid attestation. -func TestAncora_VerifyPortabilityAttestation_Invalid(t *testing.T) { - c := newAncoraClient(t) - - // Invalid attestation should return false - c.Invoke(t, false, "verifyPortabilityAttestation", []byte("invalid attestation")) -} - -// TestAncora_DataTypes tests that all data types are valid. -func TestAncora_DataTypes(t *testing.T) { - // Verify data type constants - require.Equal(t, state.DataType(0), state.DataTypeMedical) - require.Equal(t, state.DataType(1), state.DataTypeEducation) - require.Equal(t, state.DataType(2), state.DataTypeInvestment) - require.Equal(t, state.DataType(3), state.DataTypeDocuments) - require.Equal(t, state.DataType(4), state.DataTypePresence) // VPP presence/heartbeat data - require.Equal(t, state.DataType(5), state.DataTypeCustom) -} - -// TestAncora_TreeAlgorithms tests that all tree algorithms are valid. -func TestAncora_TreeAlgorithms(t *testing.T) { - // Verify tree algorithm constants - require.Equal(t, state.TreeAlgorithm(0), state.TreeAlgorithmSHA256) - require.Equal(t, state.TreeAlgorithm(1), state.TreeAlgorithmKeccak256) - require.Equal(t, state.TreeAlgorithm(2), state.TreeAlgorithmPoseidon) -} - -// TestAncora_ErasureStatuses tests that all erasure statuses are valid. -func TestAncora_ErasureStatuses(t *testing.T) { - // Verify erasure status constants - require.Equal(t, state.ErasureStatus(0), state.ErasurePending) - require.Equal(t, state.ErasureStatus(1), state.ErasureConfirmed) - require.Equal(t, state.ErasureStatus(2), state.ErasureDenied) -} +package native_test + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/stretchr/testify/require" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +func newAncoraClient(t *testing.T) *tutustest.ContractInvoker { + return newNativeClient(t, nativenames.Ancora) +} + +// TestAncora_GetConfig tests the getConfig method. +func TestAncora_GetConfig(t *testing.T) { + c := newAncoraClient(t) + + c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { + require.Equal(t, 1, len(stack)) + arr, ok := stack[0].Value().([]stackitem.Item) + require.True(t, ok, "expected array result") + require.Equal(t, 7, len(arr)) // StateAnchorsConfig has 7 fields + + // Check default values + defaultAlgo, _ := arr[0].TryInteger() + require.Equal(t, int64(state.TreeAlgorithmSHA256), defaultAlgo.Int64()) + + maxProofDepth, _ := arr[1].TryInteger() + require.Equal(t, int64(32), maxProofDepth.Int64()) + + defaultMaxUpdates, _ := arr[2].TryInteger() + require.Equal(t, int64(10), defaultMaxUpdates.Int64()) + + defaultCooldown, _ := arr[3].TryInteger() + require.Equal(t, int64(1), defaultCooldown.Int64()) + + maxHistory, _ := arr[4].TryInteger() + require.Equal(t, int64(100), maxHistory.Int64()) + + erasureGrace, _ := arr[5].TryInteger() + require.Equal(t, int64(1000), erasureGrace.Int64()) + + attestValid, _ := arr[6].TryInteger() + require.Equal(t, int64(86400), attestValid.Int64()) + }, "getConfig") +} + +// TestAncora_GetDataRoot_NonExistent tests getting a non-existent data root. +func TestAncora_GetDataRoot_NonExistent(t *testing.T) { + c := newAncoraClient(t) + + // Non-existent root should return null + c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { + require.Equal(t, 1, len(stack)) + require.Nil(t, stack[0].Value(), "expected null for non-existent root") + }, "getDataRoot", uint64(999), uint32(state.DataTypeMedical)) +} + +// TestAncora_GetProvider_NonExistent tests getting a non-existent provider config. +func TestAncora_GetProvider_NonExistent(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + + // Non-existent provider should return null + c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { + require.Equal(t, 1, len(stack)) + require.Nil(t, stack[0].Value(), "expected null for non-existent provider") + }, "getProvider", uint32(state.DataTypeMedical), acc.ScriptHash()) +} + +// TestAncora_GetErasureInfo_NonExistent tests getting non-existent erasure info. +func TestAncora_GetErasureInfo_NonExistent(t *testing.T) { + c := newAncoraClient(t) + + // Non-existent erasure info should return null + c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { + require.Equal(t, 1, len(stack)) + require.Nil(t, stack[0].Value(), "expected null for non-existent erasure info") + }, "getErasureInfo", uint64(999), uint32(state.DataTypeMedical)) +} + +// TestAncora_IsProviderActive_NonExistent tests checking non-existent provider. +func TestAncora_IsProviderActive_NonExistent(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + + // Non-existent provider should return false + c.Invoke(t, false, "isProviderActive", uint32(state.DataTypeMedical), acc.ScriptHash()) +} + +// TestAncora_RegisterProvider_NoCommittee tests that non-committee cannot register providers. +func TestAncora_RegisterProvider_NoCommittee(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + randomInvoker := c.WithSigners(acc) + + // registerProvider takes: dataType, provider, description, maxUpdatesPerBlock, updateCooldown + randomInvoker.InvokeFail(t, "invalid committee signature", + "registerProvider", + uint32(state.DataTypeMedical), + acc.ScriptHash(), + "Test Healthcare Provider", + uint32(10), + uint32(1), + ) +} + +// TestAncora_RegisterProvider_CommitteeSuccess tests committee can register providers. +func TestAncora_RegisterProvider_CommitteeSuccess(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + providerAcc := e.NewAccount(t) + + // Register a healthcare provider (c is already a CommitteeInvoker) + c.Invoke(t, true, + "registerProvider", + uint32(state.DataTypeMedical), + providerAcc.ScriptHash(), + "Test Healthcare Provider", + uint32(10), + uint32(1), + ) + + // Verify provider is active + c.Invoke(t, true, "isProviderActive", uint32(state.DataTypeMedical), providerAcc.ScriptHash()) + + // Verify provider info + c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { + require.Equal(t, 1, len(stack)) + arr, ok := stack[0].Value().([]stackitem.Item) + require.True(t, ok, "expected array result") + require.Equal(t, 7, len(arr)) // ProviderConfig has 7 fields + + // Check dataType + dataType, _ := arr[0].TryInteger() + require.Equal(t, int64(state.DataTypeMedical), dataType.Int64()) + + // Check active status + active, _ := arr[4].TryBool() + require.True(t, active) + }, "getProvider", uint32(state.DataTypeMedical), providerAcc.ScriptHash()) +} + +// TestAncora_RevokeProvider_NoCommittee tests that non-committee cannot revoke providers. +func TestAncora_RevokeProvider_NoCommittee(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + randomInvoker := c.WithSigners(acc) + + randomInvoker.InvokeFail(t, "invalid committee signature", + "revokeProvider", + uint32(state.DataTypeMedical), + acc.ScriptHash(), + ) +} + +// TestAncora_UpdateDataRoot_VitaNotFound tests that updating root for non-existent Vita fails. +// The contract should fail when the Vita doesn't exist. +func TestAncora_UpdateDataRoot_VitaNotFound(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + randomInvoker := c.WithSigners(acc) + + root := hash.Sha256([]byte("test data")).BytesBE() + + // Should fail - when calling updateDataRoot with invalid Vita ID + // The error could be "vita not found" or "unauthorized" depending on check order + randomInvoker.InvokeFail(t, "", + "updateDataRoot", + uint64(999), + uint32(state.DataTypeMedical), + root, + uint64(100), + uint32(state.TreeAlgorithmSHA256), + "1.0.0", + []byte{}, + ) +} + +// Note: TestAncora_VerifyProof tests would require complex array serialization +// that the test framework doesn't support directly. Proof verification is tested +// via cross-contract integration tests. + +// TestAncora_RequestErasure_Unauthorized tests that erasure request requires Vita ownership. +// When Vita doesn't exist, OwnerOfInternal returns empty Uint160 and the caller +// fails the witness check, resulting in "unauthorized" error. +func TestAncora_RequestErasure_Unauthorized(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + randomInvoker := c.WithSigners(acc) + + // Should fail - caller is not the Vita owner (Vita doesn't exist) + randomInvoker.InvokeFail(t, "unauthorized", + "requestErasure", + uint64(999), + uint32(state.DataTypeMedical), + "GDPR Art. 17", + ) +} + +// TestAncora_ConfirmErasure_Unauthorized tests that unauthorized providers cannot confirm erasure. +func TestAncora_ConfirmErasure_Unauthorized(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + randomInvoker := c.WithSigners(acc) + + randomInvoker.InvokeFail(t, "unauthorized: caller is not authorized provider", + "confirmErasure", + uint64(1), + uint32(state.DataTypeMedical), + ) +} + +// TestAncora_DenyErasure_NoPending tests that denying non-existent erasure fails. +func TestAncora_DenyErasure_NoPending(t *testing.T) { + c := newAncoraClient(t) + e := c.Executor + + acc := e.NewAccount(t) + randomInvoker := c.WithSigners(acc) + + randomInvoker.InvokeFail(t, "no pending erasure request", + "denyErasure", + uint64(1), + uint32(state.DataTypeMedical), + "Legal hold", + ) +} + +// TestAncora_VerifyPortabilityAttestation_Invalid tests verification of invalid attestation. +func TestAncora_VerifyPortabilityAttestation_Invalid(t *testing.T) { + c := newAncoraClient(t) + + // Invalid attestation should return false + c.Invoke(t, false, "verifyPortabilityAttestation", []byte("invalid attestation")) +} + +// TestAncora_DataTypes tests that all data types are valid. +func TestAncora_DataTypes(t *testing.T) { + // Verify data type constants + require.Equal(t, state.DataType(0), state.DataTypeMedical) + require.Equal(t, state.DataType(1), state.DataTypeEducation) + require.Equal(t, state.DataType(2), state.DataTypeInvestment) + require.Equal(t, state.DataType(3), state.DataTypeDocuments) + require.Equal(t, state.DataType(4), state.DataTypePresence) // VPP presence/heartbeat data + require.Equal(t, state.DataType(5), state.DataTypeCustom) +} + +// TestAncora_TreeAlgorithms tests that all tree algorithms are valid. +func TestAncora_TreeAlgorithms(t *testing.T) { + // Verify tree algorithm constants + require.Equal(t, state.TreeAlgorithm(0), state.TreeAlgorithmSHA256) + require.Equal(t, state.TreeAlgorithm(1), state.TreeAlgorithmKeccak256) + require.Equal(t, state.TreeAlgorithm(2), state.TreeAlgorithmPoseidon) +} + +// TestAncora_ErasureStatuses tests that all erasure statuses are valid. +func TestAncora_ErasureStatuses(t *testing.T) { + // Verify erasure status constants + require.Equal(t, state.ErasureStatus(0), state.ErasurePending) + require.Equal(t, state.ErasureStatus(1), state.ErasureConfirmed) + require.Equal(t, state.ErasureStatus(2), state.ErasureDenied) +} diff --git a/pkg/core/native/native_test/annos_test.go b/pkg/core/native/native_test/annos_test.go index 12226b9..d476462 100644 --- a/pkg/core/native/native_test/annos_test.go +++ b/pkg/core/native/native_test/annos_test.go @@ -6,10 +6,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // genesisTimestamp is the timestamp of the genesis block in the test framework. diff --git a/pkg/core/native/native_test/collocatio_test.go b/pkg/core/native/native_test/collocatio_test.go index 09d2cdf..eb8bd61 100644 --- a/pkg/core/native/native_test/collocatio_test.go +++ b/pkg/core/native/native_test/collocatio_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newCollocatioClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/common_test.go b/pkg/core/native/native_test/common_test.go index 66b6575..1093e46 100644 --- a/pkg/core/native/native_test/common_test.go +++ b/pkg/core/native/native_test/common_test.go @@ -5,19 +5,19 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newNativeClient(t *testing.T, name string) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/cryptolib_test.go b/pkg/core/native/native_test/cryptolib_test.go index 9c25b36..2123d3c 100644 --- a/pkg/core/native/native_test/cryptolib_test.go +++ b/pkg/core/native/native_test/cryptolib_test.go @@ -10,16 +10,16 @@ import ( bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/native_test/cryptolib_verification_test.go b/pkg/core/native/native_test/cryptolib_verification_test.go index affbb62..748f2e1 100644 --- a/pkg/core/native/native_test/cryptolib_verification_test.go +++ b/pkg/core/native/native_test/cryptolib_verification_test.go @@ -5,19 +5,19 @@ import ( "slices" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/native_test/designate_test.go b/pkg/core/native/native_test/designate_test.go index 5f110b4..c6b463f 100644 --- a/pkg/core/native/native_test/designate_test.go +++ b/pkg/core/native/native_test/designate_test.go @@ -4,19 +4,19 @@ import ( "slices" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/native_test/eligere_test.go b/pkg/core/native/native_test/eligere_test.go index b4f3861..0321e50 100644 --- a/pkg/core/native/native_test/eligere_test.go +++ b/pkg/core/native/native_test/eligere_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newEligereClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/federation_test.go b/pkg/core/native/native_test/federation_test.go index d82a577..fa4b376 100644 --- a/pkg/core/native/native_test/federation_test.go +++ b/pkg/core/native/native_test/federation_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newFederationClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/generate_expected_test.go b/pkg/core/native/native_test/generate_expected_test.go index 46e9b43..b7dabb4 100644 --- a/pkg/core/native/native_test/generate_expected_test.go +++ b/pkg/core/native/native_test/generate_expected_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" ) // TestGenerateExpectedContractStates outputs the current contract states as JSON diff --git a/pkg/core/native/native_test/helpers/vitahelper/vitahelper.go b/pkg/core/native/native_test/helpers/vitahelper/vitahelper.go index 2c2c095..bc32ae1 100644 --- a/pkg/core/native/native_test/helpers/vitahelper/vitahelper.go +++ b/pkg/core/native/native_test/helpers/vitahelper/vitahelper.go @@ -1,76 +1,76 @@ -// Package vitahelper provides a helper contract for testing Vita-dependent operations. -// Many Tutus contracts use GetCallingScriptHash() for authorization, requiring -// cross-contract calls to properly test these paths. -package vitahelper - -import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" -) - -// VitaHash is the Vita native contract hash (from nativehashes). -// This must match the actual Vita contract hash. -const VitaHash = "\xd9\x0f\x0d\x15\x59\x8c\x45\xcc\xe5\xe0\x48\x69\x5b\x6b\x32\x78\x26\x5c\x58\x54" - -// RegisterVita calls Vita.register() from this contract's context. -// This tests cross-contract registration where the caller is this helper contract. -func RegisterVita(vitaHash interop.Hash160, owner interop.Hash160, birthTimestamp int) bool { - return contract.Call(vitaHash, "register", contract.All, owner, birthTimestamp).(bool) -} - -// GetVitaOwner calls Vita.ownerOf() for a token ID. -func GetVitaOwner(vitaHash interop.Hash160, tokenID []byte) interop.Hash160 { - return contract.Call(vitaHash, "ownerOf", contract.ReadStates, tokenID).(interop.Hash160) -} - -// GetVitaBalance returns the Vita balance for an account. -func GetVitaBalance(vitaHash interop.Hash160, owner interop.Hash160) int { - return contract.Call(vitaHash, "balanceOf", contract.ReadStates, owner).(int) -} - -// GetTotalVita returns the total number of Vita tokens. -func GetTotalVita(vitaHash interop.Hash160) int { - return contract.Call(vitaHash, "totalSupply", contract.ReadStates).(int) -} - -// IsVitaHolder checks if an address has a Vita token. -func IsVitaHolder(vitaHash interop.Hash160, owner interop.Hash160) bool { - return contract.Call(vitaHash, "balanceOf", contract.ReadStates, owner).(int) > 0 -} - -// GetCallerHash returns the calling script hash for testing. -// Useful for verifying GetCallingScriptHash() behavior. -func GetCallerHash() interop.Hash160 { - return runtime.GetCallingScriptHash() -} - -// GetExecutingHash returns this contract's hash. -func GetExecutingHash() interop.Hash160 { - return runtime.GetExecutingScriptHash() -} - -// CallWithContext calls a contract method and returns whether this contract -// is correctly identified as the caller. -func CallWithContext(targetHash interop.Hash160, method string, args []any) any { - return contract.Call(targetHash, method, contract.All, args...) -} - -// ProxyCall is a generic proxy for calling any contract method. -// Useful for testing authorization that depends on GetCallingScriptHash(). -func ProxyCall(targetHash interop.Hash160, method string, args []any) any { - return contract.Call(targetHash, method, contract.All, args...) -} - -// GetContractHash returns the hash of a contract by its name. -// Useful for getting native contract hashes at runtime. -func GetContractHash(name string) interop.Hash160 { - cs := management.GetContractByID(-1) // Management is always -1 - if cs == nil { - return nil - } - // For getting native hashes by name, we'd need to call management.getContract - // This is a simplified version that just returns nil - return nil -} +// Package vitahelper provides a helper contract for testing Vita-dependent operations. +// Many Tutus contracts use GetCallingScriptHash() for authorization, requiring +// cross-contract calls to properly test these paths. +package vitahelper + +import ( + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" +) + +// VitaHash is the Vita native contract hash (from nativehashes). +// This must match the actual Vita contract hash. +const VitaHash = "\xd9\x0f\x0d\x15\x59\x8c\x45\xcc\xe5\xe0\x48\x69\x5b\x6b\x32\x78\x26\x5c\x58\x54" + +// RegisterVita calls Vita.register() from this contract's context. +// This tests cross-contract registration where the caller is this helper contract. +func RegisterVita(vitaHash interop.Hash160, owner interop.Hash160, birthTimestamp int) bool { + return contract.Call(vitaHash, "register", contract.All, owner, birthTimestamp).(bool) +} + +// GetVitaOwner calls Vita.ownerOf() for a token ID. +func GetVitaOwner(vitaHash interop.Hash160, tokenID []byte) interop.Hash160 { + return contract.Call(vitaHash, "ownerOf", contract.ReadStates, tokenID).(interop.Hash160) +} + +// GetVitaBalance returns the Vita balance for an account. +func GetVitaBalance(vitaHash interop.Hash160, owner interop.Hash160) int { + return contract.Call(vitaHash, "balanceOf", contract.ReadStates, owner).(int) +} + +// GetTotalVita returns the total number of Vita tokens. +func GetTotalVita(vitaHash interop.Hash160) int { + return contract.Call(vitaHash, "totalSupply", contract.ReadStates).(int) +} + +// IsVitaHolder checks if an address has a Vita token. +func IsVitaHolder(vitaHash interop.Hash160, owner interop.Hash160) bool { + return contract.Call(vitaHash, "balanceOf", contract.ReadStates, owner).(int) > 0 +} + +// GetCallerHash returns the calling script hash for testing. +// Useful for verifying GetCallingScriptHash() behavior. +func GetCallerHash() interop.Hash160 { + return runtime.GetCallingScriptHash() +} + +// GetExecutingHash returns this contract's hash. +func GetExecutingHash() interop.Hash160 { + return runtime.GetExecutingScriptHash() +} + +// CallWithContext calls a contract method and returns whether this contract +// is correctly identified as the caller. +func CallWithContext(targetHash interop.Hash160, method string, args []any) any { + return contract.Call(targetHash, method, contract.All, args...) +} + +// ProxyCall is a generic proxy for calling any contract method. +// Useful for testing authorization that depends on GetCallingScriptHash(). +func ProxyCall(targetHash interop.Hash160, method string, args []any) any { + return contract.Call(targetHash, method, contract.All, args...) +} + +// GetContractHash returns the hash of a contract by its name. +// Useful for getting native contract hashes at runtime. +func GetContractHash(name string) interop.Hash160 { + cs := management.GetContractByID(-1) // Management is always -1 + if cs == nil { + return nil + } + // For getting native hashes by name, we'd need to call management.getContract + // This is a simplified version that just returns nil + return nil +} diff --git a/pkg/core/native/native_test/ledger_test.go b/pkg/core/native/native_test/ledger_test.go index 2d3adf5..d84a96f 100644 --- a/pkg/core/native/native_test/ledger_test.go +++ b/pkg/core/native/native_test/ledger_test.go @@ -6,16 +6,16 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) @@ -254,9 +254,9 @@ func TestLedger_GetTransactionSignersInteropAPI(t *testing.T) { // After that ensure interop API allows to retrieve signer with CalledByEntry rule-based scope. src := `package callledger import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) func CallLedger(accessValue bool) int { signers := ledger.GetTransactionSigners(interop.Hash256{` + hashStr.String() + `}) diff --git a/pkg/core/native/native_test/lex_test.go b/pkg/core/native/native_test/lex_test.go index e0e40b2..408e304 100644 --- a/pkg/core/native/native_test/lex_test.go +++ b/pkg/core/native/native_test/lex_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newLexClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/lub_test.go b/pkg/core/native/native_test/lub_test.go index 8434686..94ae8af 100644 --- a/pkg/core/native/native_test/lub_test.go +++ b/pkg/core/native/native_test/lub_test.go @@ -4,16 +4,16 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go index 4c5b3d0..af8d5fb 100644 --- a/pkg/core/native/native_test/management_test.go +++ b/pkg/core/native/native_test/management_test.go @@ -8,33 +8,33 @@ import ( "strings" "testing" - ojson "github.com/tutus-one/tutus-ordered-json" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/chaindump" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + ojson "git.marketally.com/tutus-one/tutus-ordered-json" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/chaindump" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) @@ -1249,7 +1249,7 @@ func TestManagement_WhitelistedUpdate(t *testing.T) { m := e.CommitteeInvoker(nativehashes.ContractManagement) p := e.CommitteeInvoker(nativehashes.PolicyContract) src := `package free - import "github.com/tutus-one/tutus-chain/pkg/interop/native/management" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" func FreeInc(i int) int { i++ return i diff --git a/pkg/core/native/native_test/notary_test.go b/pkg/core/native/native_test/notary_test.go index aa367e2..4a26839 100644 --- a/pkg/core/native/native_test/notary_test.go +++ b/pkg/core/native/native_test/notary_test.go @@ -6,20 +6,20 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/notary" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -198,10 +198,10 @@ func TestNotary_MaliciousWithdrawal(t *testing.T) { // Deploy malicious contract and make Notary deposit for it. src := `package foo import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/notary" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" ) const ( prefixMaxCallDepth = 0x01 diff --git a/pkg/core/native/native_test/opus_test.go b/pkg/core/native/native_test/opus_test.go index daeef2e..6eb3a28 100644 --- a/pkg/core/native/native_test/opus_test.go +++ b/pkg/core/native/native_test/opus_test.go @@ -6,9 +6,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newOpusClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/oracle_test.go b/pkg/core/native/native_test/oracle_test.go index a84bb62..42c0008 100644 --- a/pkg/core/native/native_test/oracle_test.go +++ b/pkg/core/native/native_test/oracle_test.go @@ -8,15 +8,15 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/native_test/palam_test.go b/pkg/core/native/native_test/palam_test.go index 7b51119..3b702b1 100644 --- a/pkg/core/native/native_test/palam_test.go +++ b/pkg/core/native/native_test/palam_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newPalamClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/policy_test.go b/pkg/core/native/native_test/policy_test.go index f598bdf..5b4b60d 100644 --- a/pkg/core/native/native_test/policy_test.go +++ b/pkg/core/native/native_test/policy_test.go @@ -8,25 +8,25 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -473,9 +473,9 @@ func TestPolicy_GetBlockedAccountsInteropAPI(t *testing.T) { src := `package testpolicy import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/native/policy" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/policy" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" ) func GetBlockedAccounts() []interop.Hash160 { i := policy.GetBlockedAccounts() @@ -532,7 +532,7 @@ func TestPolicy_ExecPicoFeeFactor_InteropAPI(t *testing.T) { p.Invoke(t, stackitem.Null{}, "setExecFeeFactor", 5) src := `package execfeefactor - import "github.com/tutus-one/tutus-chain/pkg/interop/native/policy" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/policy" func GetFactor() int { return policy.GetExecFeeFactor() } @@ -654,9 +654,9 @@ func TestPolicy_WhitelistContractsInteropAPI(t *testing.T) { src := `package policywrapper import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/native/policy" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/policy" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" ) func SetWhitelistFeeContract(contract interop.Hash160, method string, argCnt int, fee int) { policy.SetWhitelistFeeContract(contract, method, argCnt, fee) diff --git a/pkg/core/native/native_test/pons_test.go b/pkg/core/native/native_test/pons_test.go index 1670365..0352ce9 100644 --- a/pkg/core/native/native_test/pons_test.go +++ b/pkg/core/native/native_test/pons_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newPonsClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/role_registry_test.go b/pkg/core/native/native_test/role_registry_test.go index db6bb71..83e2867 100644 --- a/pkg/core/native/native_test/role_registry_test.go +++ b/pkg/core/native/native_test/role_registry_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newRoleRegistryClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/salus_test.go b/pkg/core/native/native_test/salus_test.go index 05f1b89..8873422 100644 --- a/pkg/core/native/native_test/salus_test.go +++ b/pkg/core/native/native_test/salus_test.go @@ -6,9 +6,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newSalusClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/scire_test.go b/pkg/core/native/native_test/scire_test.go index c09fd30..35ad714 100644 --- a/pkg/core/native/native_test/scire_test.go +++ b/pkg/core/native/native_test/scire_test.go @@ -6,9 +6,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newScireClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/sese_test.go b/pkg/core/native/native_test/sese_test.go index 411cac5..20bbc0f 100644 --- a/pkg/core/native/native_test/sese_test.go +++ b/pkg/core/native/native_test/sese_test.go @@ -6,9 +6,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newSeseClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/std_test.go b/pkg/core/native/native_test/std_test.go index bcdd852..31e031c 100644 --- a/pkg/core/native/native_test/std_test.go +++ b/pkg/core/native/native_test/std_test.go @@ -4,12 +4,12 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func TestStd_HexEncodeDecodeCompat(t *testing.T) { @@ -42,7 +42,7 @@ func TestStd_HexEncodeDecodeInteropAPI(t *testing.T) { src := `package teststd import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" ) func Encode(args []any) string { return std.HexEncode(args[0].([]byte)) diff --git a/pkg/core/native/native_test/treasury_test.go b/pkg/core/native/native_test/treasury_test.go index 27638a7..d9785ab 100644 --- a/pkg/core/native/native_test/treasury_test.go +++ b/pkg/core/native/native_test/treasury_test.go @@ -4,11 +4,11 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" ) func newTreasuryClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/tribute_test.go b/pkg/core/native/native_test/tribute_test.go index ffbb712..d39a4e7 100644 --- a/pkg/core/native/native_test/tribute_test.go +++ b/pkg/core/native/native_test/tribute_test.go @@ -6,9 +6,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newTributeClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/tutus_test.go b/pkg/core/native/native_test/tutus_test.go index 8631f14..f4acb04 100644 --- a/pkg/core/native/native_test/tutus_test.go +++ b/pkg/core/native/native_test/tutus_test.go @@ -10,27 +10,27 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -378,7 +378,7 @@ func TestTutus_Vote(t *testing.T) { require.Equal(t, uint64(0), stateAfterUnvote.LastGasPerVote.Uint64()) } -// TestTutus_RecursiveLubMint is a test for https://github.com/tutus-one/tutus-chain/pull/2181. +// TestTutus_RecursiveLubMint is a test for https://git.marketally.com/tutus-one/tutus-chain/pull/2181. func TestTutus_RecursiveLubMint(t *testing.T) { tutusCommitteeInvoker := newTutusCommitteeClient(t, 100_0000_0000) tutusValidatorInvoker := tutusCommitteeInvoker.WithSigners(tutusCommitteeInvoker.Validator) @@ -528,8 +528,8 @@ func TestTutus_GetAccountStateInteropAPI(t *testing.T) { } src := `package testaccountstate import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/tutus" - "github.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" ) func GetLastLubPerVote() int { accState := tutus.GetAccountState(interop.Hash160{` + hashAStr.String() + `}) @@ -724,7 +724,7 @@ func TestTutus_TransferZeroWithNonZeroBalance(t *testing.T) { }) } -// https://github.com/tutus-one/tutus-chain/issues/3190 +// https://git.marketally.com/tutus-one/tutus-chain/issues/3190 func TestTutus_TransferNonZeroWithZeroBalance(t *testing.T) { neoValidatorsInvoker := newTutusValidatorsClient(t) e := neoValidatorsInvoker.Executor diff --git a/pkg/core/native/native_test/vita_test.go b/pkg/core/native/native_test/vita_test.go index e164adf..2427dc4 100644 --- a/pkg/core/native/native_test/vita_test.go +++ b/pkg/core/native/native_test/vita_test.go @@ -6,10 +6,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newVitaClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_test/vts_test.go b/pkg/core/native/native_test/vts_test.go index a84a259..f5be470 100644 --- a/pkg/core/native/native_test/vts_test.go +++ b/pkg/core/native/native_test/vts_test.go @@ -6,10 +6,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) func newVTSClient(t *testing.T) *tutustest.ContractInvoker { diff --git a/pkg/core/native/native_tutus.go b/pkg/core/native/native_tutus.go index dd2b780..7cfd202 100644 --- a/pkg/core/native/native_tutus.go +++ b/pkg/core/native/native_tutus.go @@ -11,25 +11,25 @@ import ( "math/big" "slices" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - istorage "github.com/tutus-one/tutus-chain/pkg/core/interop/storage" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + istorage "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Tutus represents Tutus native contract (blockchain governance token). diff --git a/pkg/core/native/native_tutus_candidate.go b/pkg/core/native/native_tutus_candidate.go index 75f571c..6dbc56a 100644 --- a/pkg/core/native/native_tutus_candidate.go +++ b/pkg/core/native/native_tutus_candidate.go @@ -3,7 +3,7 @@ package native import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) type candidate struct { diff --git a/pkg/core/native/nativehashes/gen.go b/pkg/core/native/nativehashes/gen.go index 77334aa..3603e73 100644 --- a/pkg/core/native/nativehashes/gen.go +++ b/pkg/core/native/nativehashes/gen.go @@ -10,8 +10,8 @@ import ( "os" "text/template" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" ) // srcTmpl is a nativehashes package template. @@ -22,7 +22,7 @@ const srcTmpl = `// Code generated by "go generate go run gen.go"; DO NOT EDIT. // package nativehashes contains hashes of all native contracts in their LE and Uint160 representation. package nativehashes -import "github.com/tutus-one/tutus-chain/pkg/util" +import "git.marketally.com/tutus-one/tutus-chain/pkg/util" // Hashes of all native contracts. var ( diff --git a/pkg/core/native/nativehashes/hashes.go b/pkg/core/native/nativehashes/hashes.go index ed6d620..609e033 100644 --- a/pkg/core/native/nativehashes/hashes.go +++ b/pkg/core/native/nativehashes/hashes.go @@ -5,7 +5,7 @@ // package nativehashes contains hashes of all native contracts in their LE and Uint160 representation. package nativehashes -import "github.com/tutus-one/tutus-chain/pkg/util" +import "git.marketally.com/tutus-one/tutus-chain/pkg/util" // Hashes of all native contracts. var ( diff --git a/pkg/core/native/nativeids/gen.go b/pkg/core/native/nativeids/gen.go index d9fe0ad..1234c78 100644 --- a/pkg/core/native/nativeids/gen.go +++ b/pkg/core/native/nativeids/gen.go @@ -10,7 +10,7 @@ import ( "strconv" "text/template" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" ) // srcTmpl is a nativeids package template. diff --git a/pkg/core/native/nativenames_test.go b/pkg/core/native/nativenames_test.go index f92f22f..87caf96 100644 --- a/pkg/core/native/nativenames_test.go +++ b/pkg/core/native/nativenames_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/neo_types.go b/pkg/core/native/neo_types.go index 3a31bef..4a68c5a 100644 --- a/pkg/core/native/neo_types.go +++ b/pkg/core/native/neo_types.go @@ -5,8 +5,8 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // gasIndexPair contains the block index together with the generated gas per block. diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index 2bd587f..e888e9e 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -6,25 +6,25 @@ import ( "math" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeprices" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeprices" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Notary represents Notary native contract. diff --git a/pkg/core/native/opus.go b/pkg/core/native/opus.go index 932562c..2d3c8fc 100644 --- a/pkg/core/native/opus.go +++ b/pkg/core/native/opus.go @@ -5,18 +5,18 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Opus represents the AI workforce integration native contract. diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index db39ca4..398322a 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -10,24 +10,24 @@ import ( "strings" "sync/atomic" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Oracle represents Oracle native contract. diff --git a/pkg/core/native/oracle_types.go b/pkg/core/native/oracle_types.go index a6419a7..6f138ad 100644 --- a/pkg/core/native/oracle_types.go +++ b/pkg/core/native/oracle_types.go @@ -5,8 +5,8 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // IDList is a list of oracle request IDs. diff --git a/pkg/core/native/oracle_types_test.go b/pkg/core/native/oracle_types_test.go index ceaa130..96077dd 100644 --- a/pkg/core/native/oracle_types_test.go +++ b/pkg/core/native/oracle_types_test.go @@ -3,9 +3,9 @@ package native import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/palam.go b/pkg/core/native/palam.go index 186439e..3492f74 100644 --- a/pkg/core/native/palam.go +++ b/pkg/core/native/palam.go @@ -1,1127 +1,1127 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Palam represents the programmed transparency native contract. -// Palam (Latin for "openly/publicly") provides role-based encrypted -// transaction flows with judicial declassification. -type Palam struct { - interop.ContractMD - Tutus ITutus - Vita IVita - RoleRegistry IRoleRegistry - Lex ILex -} - -// PalamCache represents the cached state for Palam contract. -type PalamCache struct { - flowCount uint64 - requestCount uint64 - logCount uint64 - attachmentCount uint64 -} - -// Storage key prefixes for Palam. -const ( - palamPrefixFlow byte = 0x01 // flowID -> Flow - palamPrefixFlowByBucket byte = 0x02 // bucket + flowID -> exists - palamPrefixFlowByParticipant byte = 0x03 // participant + flowID -> exists - palamPrefixFlowByTag byte = 0x04 // tag + flowID -> exists - palamPrefixRequest byte = 0x10 // requestID -> DeclassifyRequest - palamPrefixRequestByFlow byte = 0x11 // flowID + requestID -> exists - palamPrefixRequestByRequester byte = 0x12 // requester + requestID -> exists - palamPrefixPendingRequest byte = 0x13 // flowID -> pending requestID - palamPrefixAccessLog byte = 0x20 // logID -> AccessLog - palamPrefixLogByFlow byte = 0x21 // flowID + logID -> exists - palamPrefixLogByAccessor byte = 0x22 // accessor + logID -> exists - palamPrefixAttachment byte = 0x30 // attachmentID -> FlowAttachment - palamPrefixAttachmentByFlow byte = 0x31 // flowID + attachmentID -> exists - palamPrefixFlowCounter byte = 0xF0 // -> uint64 - palamPrefixRequestCounter byte = 0xF1 // -> next request ID - palamPrefixLogCounter byte = 0xF2 // -> next log ID - palamPrefixAttachmentCounter byte = 0xF3 // -> next attachment ID - palamPrefixConfig byte = 0xFF // -> PalamConfig -) - -// Event names for Palam. -const ( - FlowRecordedEvent = "FlowRecorded" - FlowAttachmentEvent = "FlowAttachment" - FlowAccessedEvent = "FlowAccessed" - DeclassifyRequestedEvent = "DeclassifyRequested" - DeclassifyApprovalEvent = "DeclassifyApproval" - DeclassifyGrantedEvent = "DeclassifyGranted" - DeclassifyDeniedEvent = "DeclassifyDenied" - DeclassifyExpiredEvent = "DeclassifyExpired" -) - -// Role constants for Palam (from RoleRegistry). -const ( - RolePalamAuditor uint64 = 25 // Can request declassification - RolePalamJudge uint64 = 26 // Can approve declassification -) - -// Various errors for Palam. -var ( - ErrPalamFlowNotFound = errors.New("flow not found") - ErrPalamFlowExists = errors.New("flow already exists") - ErrPalamRequestNotFound = errors.New("declassify request not found") - ErrPalamRequestExists = errors.New("declassify request already exists") - ErrPalamNotParticipant = errors.New("caller is not a participant") - ErrPalamNotAuditor = errors.New("caller is not an auditor") - ErrPalamNotJudge = errors.New("caller is not a judge") - ErrPalamAlreadyApproved = errors.New("already approved this request") - ErrPalamRequestExpired = errors.New("request has expired") - ErrPalamRequestNotPending = errors.New("request is not pending") - ErrPalamRequestDenied = errors.New("request was denied") - ErrPalamNoVita = errors.New("caller must have an active Vita") - ErrPalamInvalidTag = errors.New("invalid tag") - ErrPalamInvalidBucket = errors.New("invalid bucket") - ErrPalamInvalidCaseID = errors.New("invalid case ID") - ErrPalamInvalidReason = errors.New("invalid reason (minimum 50 characters)") - ErrPalamCannotSelfApprove = errors.New("cannot approve own request") - ErrPalamNoPermission = errors.New("no permission for this action") - ErrPalamInvalidAttachment = errors.New("invalid attachment type") - ErrPalamAttachmentNotFound = errors.New("attachment not found") - ErrPalamNotCommittee = errors.New("invalid committee signature") -) - -// NewPalam creates a new Palam native contract. -func NewPalam() *Palam { - p := &Palam{} - - p.ContractMD = *interop.NewContractMD(nativenames.Palam, nativeids.Palam) - defer p.BuildHFSpecificMD(p.ActiveIn()) - - desc := NewDescriptor("getConfig", smartcontract.ArrayType) - md := NewMethodAndPrice(p.getConfig, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // ===== Flow Recording ===== - - desc = NewDescriptor("recordFlow", smartcontract.Hash256Type, - manifest.NewParameter("tag", smartcontract.StringType), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("bucket", smartcontract.StringType), - manifest.NewParameter("participants", smartcontract.ArrayType), - manifest.NewParameter("consumerData", smartcontract.ByteArrayType), - manifest.NewParameter("merchantData", smartcontract.ByteArrayType), - manifest.NewParameter("distributorData", smartcontract.ByteArrayType), - manifest.NewParameter("producerData", smartcontract.ByteArrayType), - manifest.NewParameter("ngoData", smartcontract.ByteArrayType), - manifest.NewParameter("auditorData", smartcontract.ByteArrayType), - manifest.NewParameter("previousFlowID", smartcontract.Hash256Type)) - md = NewMethodAndPrice(p.recordFlow, 1<<17, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - desc = NewDescriptor("attachToFlow", smartcontract.IntegerType, - manifest.NewParameter("flowID", smartcontract.Hash256Type), - manifest.NewParameter("attachmentType", smartcontract.StringType), - manifest.NewParameter("encryptedData", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(p.attachToFlow, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // ===== Flow Queries ===== - - desc = NewDescriptor("getFlow", smartcontract.ArrayType, - manifest.NewParameter("flowID", smartcontract.Hash256Type)) - md = NewMethodAndPrice(p.getFlow, 1<<15, callflag.ReadStates|callflag.AllowNotify) - p.AddMethod(md, desc) - - desc = NewDescriptor("getTotalFlows", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getTotalFlows, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - desc = NewDescriptor("getAttachment", smartcontract.ArrayType, - manifest.NewParameter("attachmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getAttachment, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - desc = NewDescriptor("getTotalAttachments", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getTotalAttachments, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // ===== Declassification ===== - - desc = NewDescriptor("requestDeclassify", smartcontract.IntegerType, - manifest.NewParameter("flowID", smartcontract.Hash256Type), - manifest.NewParameter("caseID", smartcontract.StringType), - manifest.NewParameter("reason", smartcontract.StringType), - manifest.NewParameter("expirationBlocks", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.requestDeclassify, 1<<17, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - desc = NewDescriptor("approveDeclassify", smartcontract.BoolType, - manifest.NewParameter("requestID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.approveDeclassify, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - desc = NewDescriptor("denyDeclassify", smartcontract.BoolType, - manifest.NewParameter("requestID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(p.denyDeclassify, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - desc = NewDescriptor("getDeclassifyRequest", smartcontract.ArrayType, - manifest.NewParameter("requestID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getDeclassifyRequest, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - desc = NewDescriptor("getTotalRequests", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getTotalRequests, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - desc = NewDescriptor("hasDeclassifyGrant", smartcontract.BoolType, - manifest.NewParameter("flowID", smartcontract.Hash256Type), - manifest.NewParameter("requester", smartcontract.Hash160Type)) - md = NewMethodAndPrice(p.hasDeclassifyGrant, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // ===== Access Logging ===== - - desc = NewDescriptor("getAccessLog", smartcontract.ArrayType, - manifest.NewParameter("logID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getAccessLog, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - desc = NewDescriptor("getTotalLogs", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getTotalLogs, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // ===== Role & Permissions ===== - - desc = NewDescriptor("getRolePermissions", smartcontract.ArrayType, - manifest.NewParameter("role", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getRolePermissions, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - return p -} - -// Metadata returns the metadata of the contract. -func (p *Palam) Metadata() *interop.ContractMD { - return &p.ContractMD -} - -// Initialize initializes the Palam contract. -func (p *Palam) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != p.ActiveIn() { - return nil - } - - // Initialize counters - p.setFlowCounter(ic.DAO, 0) - p.setRequestCounter(ic.DAO, 0) - p.setLogCounter(ic.DAO, 0) - p.setAttachmentCounter(ic.DAO, 0) - - // Set default config - cfg := state.DefaultPalamConfig() - cfgItem := cfg.ToStackItem() - cfgBytes, err := ic.DAO.GetItemCtx().Serialize(cfgItem, false) - if err != nil { - return err - } - ic.DAO.PutStorageItem(p.ID, []byte{palamPrefixConfig}, cfgBytes) - - return nil -} - -// InitializeCache initializes the contract cache. -func (p *Palam) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - return nil // No cache needed - use storage directly -} - -// ActiveIn returns the hardfork at which the contract becomes active. -func (p *Palam) ActiveIn() *config.Hardfork { - return nil // Always active -} - -// Address returns the contract's script hash. -func (p *Palam) Address() util.Uint160 { - return p.Hash -} - -// GetFlowInternal returns a flow by ID (for cross-contract use). -func (p *Palam) GetFlowInternal(d *dao.Simple, flowID util.Uint256) *state.Flow { - return p.getFlowInternal(d, flowID) -} - -// HasDeclassifyGrant checks if requester has declassify grant for a flow (for cross-contract use). -func (p *Palam) HasDeclassifyGrant(d *dao.Simple, flowID util.Uint256, requester util.Uint160) bool { - return p.hasGrantInternal(d, flowID, requester) -} - -// OnPersist implements the Contract interface. -func (p *Palam) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (p *Palam) PostPersist(ic *interop.Context) error { - return nil -} - -// ===== Configuration ===== - -func (p *Palam) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - cfg := p.getConfigInternal(ic.DAO) - return cfg.ToStackItem() -} - -func (p *Palam) getConfigInternal(d *dao.Simple) *state.PalamConfig { - si := d.GetStorageItem(p.ID, []byte{palamPrefixConfig}) - if si == nil { - return state.DefaultPalamConfig() - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return state.DefaultPalamConfig() - } - - cfg := &state.PalamConfig{} - if err := cfg.FromStackItem(item); err != nil { - return state.DefaultPalamConfig() - } - return cfg -} - -// ===== Counter Helpers ===== - -func (p *Palam) getFlowCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(p.ID, []byte{palamPrefixFlowCounter}) - if si == nil || len(si) == 0 { - return 0 - } - return binary.LittleEndian.Uint64(si) -} - -func (p *Palam) setFlowCounter(d *dao.Simple, count uint64) { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, count) - d.PutStorageItem(p.ID, []byte{palamPrefixFlowCounter}, data) -} - -func (p *Palam) getRequestCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(p.ID, []byte{palamPrefixRequestCounter}) - if si == nil || len(si) == 0 { - return 0 - } - return binary.LittleEndian.Uint64(si) -} - -func (p *Palam) setRequestCounter(d *dao.Simple, count uint64) { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, count) - d.PutStorageItem(p.ID, []byte{palamPrefixRequestCounter}, data) -} - -func (p *Palam) getNextRequestID(d *dao.Simple) uint64 { - id := p.getRequestCounter(d) + 1 - p.setRequestCounter(d, id) - return id -} - -func (p *Palam) getLogCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(p.ID, []byte{palamPrefixLogCounter}) - if si == nil || len(si) == 0 { - return 0 - } - return binary.LittleEndian.Uint64(si) -} - -func (p *Palam) setLogCounter(d *dao.Simple, count uint64) { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, count) - d.PutStorageItem(p.ID, []byte{palamPrefixLogCounter}, data) -} - -func (p *Palam) getNextLogID(d *dao.Simple) uint64 { - id := p.getLogCounter(d) + 1 - p.setLogCounter(d, id) - return id -} - -func (p *Palam) getAttachmentCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(p.ID, []byte{palamPrefixAttachmentCounter}) - if si == nil || len(si) == 0 { - return 0 - } - return binary.LittleEndian.Uint64(si) -} - -func (p *Palam) setAttachmentCounter(d *dao.Simple, count uint64) { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, count) - d.PutStorageItem(p.ID, []byte{palamPrefixAttachmentCounter}, data) -} - -func (p *Palam) getNextAttachmentID(d *dao.Simple) uint64 { - id := p.getAttachmentCounter(d) + 1 - p.setAttachmentCounter(d, id) - return id -} - -// ===== Flow Recording ===== - -func (p *Palam) recordFlow(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tag, err := stackitem.ToString(args[0]) - if err != nil || len(tag) == 0 || len(tag) > 64 { - panic(ErrPalamInvalidTag) - } - - amountBI, err := args[1].TryInteger() - if err != nil { - panic(err) - } - amount := amountBI.Uint64() - - bucket, err := stackitem.ToString(args[2]) - if err != nil || len(bucket) == 0 || len(bucket) > 32 { - panic(ErrPalamInvalidBucket) - } - - participantsArr, ok := args[3].Value().([]stackitem.Item) - if !ok { - panic("invalid participants array") - } - participants := make([]util.Uint160, len(participantsArr)) - for i, p := range participantsArr { - pBytes, err := p.TryBytes() - if err != nil { - panic(err) - } - participants[i], err = util.Uint160DecodeBytesBE(pBytes) - if err != nil { - panic(err) - } - } - - consumerData, err := args[4].TryBytes() - if err != nil { - panic(err) - } - merchantData, err := args[5].TryBytes() - if err != nil { - panic(err) - } - distributorData, err := args[6].TryBytes() - if err != nil { - panic(err) - } - producerData, err := args[7].TryBytes() - if err != nil { - panic(err) - } - ngoData, err := args[8].TryBytes() - if err != nil { - panic(err) - } - auditorData, err := args[9].TryBytes() - if err != nil { - panic(err) - } - - prevFlowBytes, err := args[10].TryBytes() - if err != nil { - panic(err) - } - var previousFlowID util.Uint256 - if len(prevFlowBytes) == 32 { - previousFlowID, err = util.Uint256DecodeBytesBE(prevFlowBytes) - if err != nil { - panic(err) - } - } - - caller := ic.VM.GetCallingScriptHash() - - // Verify caller has Vita - if p.Vita != nil && !p.Vita.TokenExists(ic.DAO, caller) { - panic(ErrPalamNoVita) - } - - // Generate flow ID from content hash - flowData := append([]byte(tag), []byte(bucket)...) - flowData = append(flowData, caller.BytesBE()...) - flowData = append(flowData, consumerData...) - flowData = append(flowData, merchantData...) - blockBytes := make([]byte, 4) - binary.LittleEndian.PutUint32(blockBytes, ic.Block.Index) - flowData = append(flowData, blockBytes...) - flowID := hash.Sha256(flowData) - - // Check if flow already exists - if p.getFlowInternal(ic.DAO, flowID) != nil { - panic(ErrPalamFlowExists) - } - - flow := &state.Flow{ - FlowID: flowID, - Bucket: bucket, - Tag: tag, - Amount: amount, - Timestamp: ic.Block.Index, - Creator: caller, - ConsumerData: consumerData, - MerchantData: merchantData, - DistributorData: distributorData, - ProducerData: producerData, - NGOData: ngoData, - AuditorData: auditorData, - Participants: participants, - PreviousFlowID: previousFlowID, - Status: state.FlowStatusActive, - } - - // Store flow - p.putFlow(ic.DAO, flow) - - // Update counter - count := p.getFlowCounter(ic.DAO) + 1 - p.setFlowCounter(ic.DAO, count) - - // Emit event - ic.AddNotification(p.Hash, FlowRecordedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(flowID.BytesBE()), - stackitem.NewByteArray([]byte(tag)), - stackitem.NewByteArray([]byte(bucket)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ic.Block.Index))), - })) - - return stackitem.NewByteArray(flowID.BytesBE()) -} - -func (p *Palam) putFlow(d *dao.Simple, flow *state.Flow) { - flowItem := flow.ToStackItem() - flowBytes, err := d.GetItemCtx().Serialize(flowItem, false) - if err != nil { - panic(err) - } - - // Store main record - key := append([]byte{palamPrefixFlow}, flow.FlowID.BytesBE()...) - d.PutStorageItem(p.ID, key, flowBytes) - - // Index by bucket - bucketKey := append([]byte{palamPrefixFlowByBucket}, []byte(flow.Bucket)...) - bucketKey = append(bucketKey, flow.FlowID.BytesBE()...) - d.PutStorageItem(p.ID, bucketKey, []byte{1}) - - // Index by tag - tagKey := append([]byte{palamPrefixFlowByTag}, []byte(flow.Tag)...) - tagKey = append(tagKey, flow.FlowID.BytesBE()...) - d.PutStorageItem(p.ID, tagKey, []byte{1}) - - // Index by participants - for _, participant := range flow.Participants { - partKey := append([]byte{palamPrefixFlowByParticipant}, participant.BytesBE()...) - partKey = append(partKey, flow.FlowID.BytesBE()...) - d.PutStorageItem(p.ID, partKey, []byte{1}) - } -} - -func (p *Palam) getFlowInternal(d *dao.Simple, flowID util.Uint256) *state.Flow { - key := append([]byte{palamPrefixFlow}, flowID.BytesBE()...) - si := d.GetStorageItem(p.ID, key) - if si == nil { - return nil - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return nil - } - - flow := &state.Flow{} - if err := flow.FromStackItem(item); err != nil { - return nil - } - return flow -} - -func (p *Palam) getFlow(ic *interop.Context, args []stackitem.Item) stackitem.Item { - flowIDBytes, err := args[0].TryBytes() - if err != nil { - panic(err) - } - flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) - if err != nil { - panic(err) - } - - flow := p.getFlowInternal(ic.DAO, flowID) - if flow == nil { - return stackitem.Null{} - } - - // Log access if configured - cfg := p.getConfigInternal(ic.DAO) - if cfg.LogAllAccess { - caller := ic.VM.GetCallingScriptHash() - p.logAccess(ic, flowID, caller, state.AccessTypeView, "getFlow") - } - - return flow.ToStackItem() -} - -func (p *Palam) getTotalFlows(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getFlowCounter(ic.DAO))) -} - -// ===== Attachments ===== - -func (p *Palam) attachToFlow(ic *interop.Context, args []stackitem.Item) stackitem.Item { - flowIDBytes, err := args[0].TryBytes() - if err != nil { - panic(err) - } - flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) - if err != nil { - panic(err) - } - - attachmentType, err := stackitem.ToString(args[1]) - if err != nil || len(attachmentType) == 0 || len(attachmentType) > 64 { - panic(ErrPalamInvalidAttachment) - } - - encryptedData, err := args[2].TryBytes() - if err != nil { - panic(err) - } - - caller := ic.VM.GetCallingScriptHash() - - // Verify flow exists - flow := p.getFlowInternal(ic.DAO, flowID) - if flow == nil { - panic(ErrPalamFlowNotFound) - } - - // Verify caller is a participant - isParticipant := false - for _, p := range flow.Participants { - if p.Equals(caller) { - isParticipant = true - break - } - } - if !isParticipant && !flow.Creator.Equals(caller) { - panic(ErrPalamNotParticipant) - } - - attachmentID := p.getNextAttachmentID(ic.DAO) - - attachment := &state.FlowAttachment{ - AttachmentID: attachmentID, - FlowID: flowID, - AttachmentType: attachmentType, - EncryptedData: encryptedData, - Attacher: caller, - AttachedAt: ic.Block.Index, - } - - p.putAttachment(ic.DAO, attachment) - - // Emit event - ic.AddNotification(p.Hash, FlowAttachmentEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(flowID.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(attachmentID)), - stackitem.NewByteArray([]byte(attachmentType)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(attachmentID)) -} - -func (p *Palam) putAttachment(d *dao.Simple, att *state.FlowAttachment) { - attItem := att.ToStackItem() - attBytes, err := d.GetItemCtx().Serialize(attItem, false) - if err != nil { - panic(err) - } - - idBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(idBytes, att.AttachmentID) - - // Store main record - key := append([]byte{palamPrefixAttachment}, idBytes...) - d.PutStorageItem(p.ID, key, attBytes) - - // Index by flow - flowKey := append([]byte{palamPrefixAttachmentByFlow}, att.FlowID.BytesBE()...) - flowKey = append(flowKey, idBytes...) - d.PutStorageItem(p.ID, flowKey, []byte{1}) -} - -func (p *Palam) getAttachmentInternal(d *dao.Simple, attachmentID uint64) *state.FlowAttachment { - idBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(idBytes, attachmentID) - - key := append([]byte{palamPrefixAttachment}, idBytes...) - si := d.GetStorageItem(p.ID, key) - if si == nil { - return nil - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return nil - } - - att := &state.FlowAttachment{} - if err := att.FromStackItem(item); err != nil { - return nil - } - return att -} - -func (p *Palam) getAttachment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - attachmentID, err := args[0].TryInteger() - if err != nil { - panic(err) - } - - att := p.getAttachmentInternal(ic.DAO, attachmentID.Uint64()) - if att == nil { - return stackitem.Null{} - } - - return att.ToStackItem() -} - -func (p *Palam) getTotalAttachments(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getAttachmentCounter(ic.DAO))) -} - -// ===== Declassification ===== - -func (p *Palam) requestDeclassify(ic *interop.Context, args []stackitem.Item) stackitem.Item { - flowIDBytes, err := args[0].TryBytes() - if err != nil { - panic(err) - } - flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) - if err != nil { - panic(err) - } - - caseID, err := stackitem.ToString(args[1]) - if err != nil || len(caseID) == 0 || len(caseID) > 64 { - panic(ErrPalamInvalidCaseID) - } - - reason, err := stackitem.ToString(args[2]) - if err != nil || len(reason) < 50 || len(reason) > 1024 { - panic(ErrPalamInvalidReason) - } - - expirationBlocks, err := args[3].TryInteger() - if err != nil { - panic(err) - } - expBlocks := uint32(expirationBlocks.Uint64()) - - caller := ic.VM.GetCallingScriptHash() - - // Verify flow exists - flow := p.getFlowInternal(ic.DAO, flowID) - if flow == nil { - panic(ErrPalamFlowNotFound) - } - - // Verify caller is an auditor - if p.RoleRegistry != nil { - if !p.RoleRegistry.HasRoleInternal(ic.DAO, caller, RolePalamAuditor, ic.Block.Index) { - panic(ErrPalamNotAuditor) - } - } - - // Get config for defaults - cfg := p.getConfigInternal(ic.DAO) - if expBlocks == 0 { - expBlocks = cfg.DefaultExpiration - } - - requestID := p.getNextRequestID(ic.DAO) - - request := &state.DeclassifyRequest{ - RequestID: requestID, - FlowID: flowID, - CaseID: caseID, - Reason: reason, - Requester: caller, - RequesterRole: state.PalamRoleAuditor, - RequiredApprovals: cfg.MinApprovals, - Approvals: []util.Uint160{}, - ApprovalTimes: []uint32{}, - Status: state.DeclassifyPending, - CreatedAt: ic.Block.Index, - ExpiresAt: ic.Block.Index + expBlocks, - GrantedAt: 0, - } - - p.putDeclassifyRequest(ic.DAO, request) - - // Emit event - ic.AddNotification(p.Hash, DeclassifyRequestedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)), - stackitem.NewByteArray(flowID.BytesBE()), - stackitem.NewByteArray([]byte(caseID)), - stackitem.NewByteArray(caller.BytesBE()), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)) -} - -func (p *Palam) putDeclassifyRequest(d *dao.Simple, req *state.DeclassifyRequest) { - reqItem := req.ToStackItem() - reqBytes, err := d.GetItemCtx().Serialize(reqItem, false) - if err != nil { - panic(err) - } - - idBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(idBytes, req.RequestID) - - // Store main record - key := append([]byte{palamPrefixRequest}, idBytes...) - d.PutStorageItem(p.ID, key, reqBytes) - - // Index by flow - flowKey := append([]byte{palamPrefixRequestByFlow}, req.FlowID.BytesBE()...) - flowKey = append(flowKey, idBytes...) - d.PutStorageItem(p.ID, flowKey, []byte{1}) - - // Index by requester - reqKey := append([]byte{palamPrefixRequestByRequester}, req.Requester.BytesBE()...) - reqKey = append(reqKey, idBytes...) - d.PutStorageItem(p.ID, reqKey, []byte{1}) - - // Track pending request for flow - if req.Status == state.DeclassifyPending { - pendingKey := append([]byte{palamPrefixPendingRequest}, req.FlowID.BytesBE()...) - d.PutStorageItem(p.ID, pendingKey, idBytes) - } -} - -func (p *Palam) getRequestInternal(d *dao.Simple, requestID uint64) *state.DeclassifyRequest { - idBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(idBytes, requestID) - - key := append([]byte{palamPrefixRequest}, idBytes...) - si := d.GetStorageItem(p.ID, key) - if si == nil { - return nil - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return nil - } - - req := &state.DeclassifyRequest{} - if err := req.FromStackItem(item); err != nil { - return nil - } - return req -} - -func (p *Palam) approveDeclassify(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestID, err := args[0].TryInteger() - if err != nil { - panic(err) - } - - caller := ic.VM.GetCallingScriptHash() - - // Verify caller is a judge - if p.RoleRegistry != nil { - if !p.RoleRegistry.HasRoleInternal(ic.DAO, caller, RolePalamJudge, ic.Block.Index) { - panic(ErrPalamNotJudge) - } - } - - req := p.getRequestInternal(ic.DAO, requestID.Uint64()) - if req == nil { - panic(ErrPalamRequestNotFound) - } - - // Check request is pending - if req.Status != state.DeclassifyPending { - panic(ErrPalamRequestNotPending) - } - - // Check not expired - if ic.Block.Index > req.ExpiresAt { - req.Status = state.DeclassifyExpired - p.putDeclassifyRequest(ic.DAO, req) - ic.AddNotification(p.Hash, DeclassifyExpiredEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), - })) - panic(ErrPalamRequestExpired) - } - - // Cannot approve own request - if caller.Equals(req.Requester) { - panic(ErrPalamCannotSelfApprove) - } - - // Check not already approved by this caller - for _, approver := range req.Approvals { - if approver.Equals(caller) { - panic(ErrPalamAlreadyApproved) - } - } - - // Add approval - req.Approvals = append(req.Approvals, caller) - req.ApprovalTimes = append(req.ApprovalTimes, ic.Block.Index) - - // Check if enough approvals - if uint32(len(req.Approvals)) >= req.RequiredApprovals { - req.Status = state.DeclassifyApproved - req.GrantedAt = ic.Block.Index - - // Emit granted event - ic.AddNotification(p.Hash, DeclassifyGrantedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), - stackitem.NewByteArray(req.FlowID.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(req.GrantedAt))), - })) - } - - p.putDeclassifyRequest(ic.DAO, req) - - // Emit approval event - ic.AddNotification(p.Hash, DeclassifyApprovalEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), - stackitem.NewByteArray(caller.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetInt64(int64(len(req.Approvals)))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(req.RequiredApprovals))), - })) - - return stackitem.NewBool(true) -} - -func (p *Palam) denyDeclassify(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestID, err := args[0].TryInteger() - if err != nil { - panic(err) - } - - reason, err := stackitem.ToString(args[1]) - if err != nil { - panic(err) - } - - caller := ic.VM.GetCallingScriptHash() - - // Verify caller is a judge - if p.RoleRegistry != nil { - if !p.RoleRegistry.HasRoleInternal(ic.DAO, caller, RolePalamJudge, ic.Block.Index) { - panic(ErrPalamNotJudge) - } - } - - req := p.getRequestInternal(ic.DAO, requestID.Uint64()) - if req == nil { - panic(ErrPalamRequestNotFound) - } - - if req.Status != state.DeclassifyPending { - panic(ErrPalamRequestNotPending) - } - - req.Status = state.DeclassifyDenied - p.putDeclassifyRequest(ic.DAO, req) - - // Emit denied event - ic.AddNotification(p.Hash, DeclassifyDeniedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), - stackitem.NewByteArray(caller.BytesBE()), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -func (p *Palam) getDeclassifyRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestID, err := args[0].TryInteger() - if err != nil { - panic(err) - } - - req := p.getRequestInternal(ic.DAO, requestID.Uint64()) - if req == nil { - return stackitem.Null{} - } - - return req.ToStackItem() -} - -func (p *Palam) getTotalRequests(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getRequestCounter(ic.DAO))) -} - -func (p *Palam) hasDeclassifyGrant(ic *interop.Context, args []stackitem.Item) stackitem.Item { - flowIDBytes, err := args[0].TryBytes() - if err != nil { - panic(err) - } - flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) - if err != nil { - panic(err) - } - - requesterBytes, err := args[1].TryBytes() - if err != nil { - panic(err) - } - requester, err := util.Uint160DecodeBytesBE(requesterBytes) - if err != nil { - panic(err) - } - - // Search for approved request by this requester for this flow - return stackitem.NewBool(p.hasGrantInternal(ic.DAO, flowID, requester)) -} - -func (p *Palam) hasGrantInternal(d *dao.Simple, flowID util.Uint256, requester util.Uint160) bool { - // Iterate through requests for this flow - prefix := append([]byte{palamPrefixRequestByFlow}, flowID.BytesBE()...) - d.Seek(p.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) < 8 { - return true - } - requestID := binary.LittleEndian.Uint64(k[len(k)-8:]) - req := p.getRequestInternal(d, requestID) - if req != nil && req.Status == state.DeclassifyApproved && req.Requester.Equals(requester) { - return false // Found a grant - } - return true - }) - // Note: This is a simplification - in production would track this more efficiently - return false -} - -// ===== Access Logging ===== - -func (p *Palam) logAccess(ic *interop.Context, flowID util.Uint256, accessor util.Uint160, accessType state.AccessType, details string) { - logID := p.getNextLogID(ic.DAO) - - log := &state.AccessLog{ - LogID: logID, - FlowID: flowID, - Accessor: accessor, - AccessType: accessType, - Timestamp: ic.Block.Index, - Details: details, - } - - p.putAccessLog(ic.DAO, log) - - // Emit event - ic.AddNotification(p.Hash, FlowAccessedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(flowID.BytesBE()), - stackitem.NewByteArray(accessor.BytesBE()), - stackitem.NewByteArray([]byte(details)), - })) -} - -func (p *Palam) putAccessLog(d *dao.Simple, log *state.AccessLog) { - logItem := log.ToStackItem() - logBytes, err := d.GetItemCtx().Serialize(logItem, false) - if err != nil { - panic(err) - } - - idBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(idBytes, log.LogID) - - // Store main record - key := append([]byte{palamPrefixAccessLog}, idBytes...) - d.PutStorageItem(p.ID, key, logBytes) - - // Index by flow - flowKey := append([]byte{palamPrefixLogByFlow}, log.FlowID.BytesBE()...) - flowKey = append(flowKey, idBytes...) - d.PutStorageItem(p.ID, flowKey, []byte{1}) - - // Index by accessor - accKey := append([]byte{palamPrefixLogByAccessor}, log.Accessor.BytesBE()...) - accKey = append(accKey, idBytes...) - d.PutStorageItem(p.ID, accKey, []byte{1}) -} - -func (p *Palam) getAccessLogInternal(d *dao.Simple, logID uint64) *state.AccessLog { - idBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(idBytes, logID) - - key := append([]byte{palamPrefixAccessLog}, idBytes...) - si := d.GetStorageItem(p.ID, key) - if si == nil { - return nil - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return nil - } - - log := &state.AccessLog{} - if err := log.FromStackItem(item); err != nil { - return nil - } - return log -} - -func (p *Palam) getAccessLog(ic *interop.Context, args []stackitem.Item) stackitem.Item { - logID, err := args[0].TryInteger() - if err != nil { - panic(err) - } - - log := p.getAccessLogInternal(ic.DAO, logID.Uint64()) - if log == nil { - return stackitem.Null{} - } - - return log.ToStackItem() -} - -func (p *Palam) getTotalLogs(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getLogCounter(ic.DAO))) -} - -// ===== Role Permissions ===== - -func (p *Palam) getRolePermissions(ic *interop.Context, args []stackitem.Item) stackitem.Item { - roleInt, err := args[0].TryInteger() - if err != nil { - panic(err) - } - role := state.PalamRole(roleInt.Uint64()) - - permissions := state.DefaultRolePermissions(role) - return permissions.ToStackItem() -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Palam represents the programmed transparency native contract. +// Palam (Latin for "openly/publicly") provides role-based encrypted +// transaction flows with judicial declassification. +type Palam struct { + interop.ContractMD + Tutus ITutus + Vita IVita + RoleRegistry IRoleRegistry + Lex ILex +} + +// PalamCache represents the cached state for Palam contract. +type PalamCache struct { + flowCount uint64 + requestCount uint64 + logCount uint64 + attachmentCount uint64 +} + +// Storage key prefixes for Palam. +const ( + palamPrefixFlow byte = 0x01 // flowID -> Flow + palamPrefixFlowByBucket byte = 0x02 // bucket + flowID -> exists + palamPrefixFlowByParticipant byte = 0x03 // participant + flowID -> exists + palamPrefixFlowByTag byte = 0x04 // tag + flowID -> exists + palamPrefixRequest byte = 0x10 // requestID -> DeclassifyRequest + palamPrefixRequestByFlow byte = 0x11 // flowID + requestID -> exists + palamPrefixRequestByRequester byte = 0x12 // requester + requestID -> exists + palamPrefixPendingRequest byte = 0x13 // flowID -> pending requestID + palamPrefixAccessLog byte = 0x20 // logID -> AccessLog + palamPrefixLogByFlow byte = 0x21 // flowID + logID -> exists + palamPrefixLogByAccessor byte = 0x22 // accessor + logID -> exists + palamPrefixAttachment byte = 0x30 // attachmentID -> FlowAttachment + palamPrefixAttachmentByFlow byte = 0x31 // flowID + attachmentID -> exists + palamPrefixFlowCounter byte = 0xF0 // -> uint64 + palamPrefixRequestCounter byte = 0xF1 // -> next request ID + palamPrefixLogCounter byte = 0xF2 // -> next log ID + palamPrefixAttachmentCounter byte = 0xF3 // -> next attachment ID + palamPrefixConfig byte = 0xFF // -> PalamConfig +) + +// Event names for Palam. +const ( + FlowRecordedEvent = "FlowRecorded" + FlowAttachmentEvent = "FlowAttachment" + FlowAccessedEvent = "FlowAccessed" + DeclassifyRequestedEvent = "DeclassifyRequested" + DeclassifyApprovalEvent = "DeclassifyApproval" + DeclassifyGrantedEvent = "DeclassifyGranted" + DeclassifyDeniedEvent = "DeclassifyDenied" + DeclassifyExpiredEvent = "DeclassifyExpired" +) + +// Role constants for Palam (from RoleRegistry). +const ( + RolePalamAuditor uint64 = 25 // Can request declassification + RolePalamJudge uint64 = 26 // Can approve declassification +) + +// Various errors for Palam. +var ( + ErrPalamFlowNotFound = errors.New("flow not found") + ErrPalamFlowExists = errors.New("flow already exists") + ErrPalamRequestNotFound = errors.New("declassify request not found") + ErrPalamRequestExists = errors.New("declassify request already exists") + ErrPalamNotParticipant = errors.New("caller is not a participant") + ErrPalamNotAuditor = errors.New("caller is not an auditor") + ErrPalamNotJudge = errors.New("caller is not a judge") + ErrPalamAlreadyApproved = errors.New("already approved this request") + ErrPalamRequestExpired = errors.New("request has expired") + ErrPalamRequestNotPending = errors.New("request is not pending") + ErrPalamRequestDenied = errors.New("request was denied") + ErrPalamNoVita = errors.New("caller must have an active Vita") + ErrPalamInvalidTag = errors.New("invalid tag") + ErrPalamInvalidBucket = errors.New("invalid bucket") + ErrPalamInvalidCaseID = errors.New("invalid case ID") + ErrPalamInvalidReason = errors.New("invalid reason (minimum 50 characters)") + ErrPalamCannotSelfApprove = errors.New("cannot approve own request") + ErrPalamNoPermission = errors.New("no permission for this action") + ErrPalamInvalidAttachment = errors.New("invalid attachment type") + ErrPalamAttachmentNotFound = errors.New("attachment not found") + ErrPalamNotCommittee = errors.New("invalid committee signature") +) + +// NewPalam creates a new Palam native contract. +func NewPalam() *Palam { + p := &Palam{} + + p.ContractMD = *interop.NewContractMD(nativenames.Palam, nativeids.Palam) + defer p.BuildHFSpecificMD(p.ActiveIn()) + + desc := NewDescriptor("getConfig", smartcontract.ArrayType) + md := NewMethodAndPrice(p.getConfig, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // ===== Flow Recording ===== + + desc = NewDescriptor("recordFlow", smartcontract.Hash256Type, + manifest.NewParameter("tag", smartcontract.StringType), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("bucket", smartcontract.StringType), + manifest.NewParameter("participants", smartcontract.ArrayType), + manifest.NewParameter("consumerData", smartcontract.ByteArrayType), + manifest.NewParameter("merchantData", smartcontract.ByteArrayType), + manifest.NewParameter("distributorData", smartcontract.ByteArrayType), + manifest.NewParameter("producerData", smartcontract.ByteArrayType), + manifest.NewParameter("ngoData", smartcontract.ByteArrayType), + manifest.NewParameter("auditorData", smartcontract.ByteArrayType), + manifest.NewParameter("previousFlowID", smartcontract.Hash256Type)) + md = NewMethodAndPrice(p.recordFlow, 1<<17, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + desc = NewDescriptor("attachToFlow", smartcontract.IntegerType, + manifest.NewParameter("flowID", smartcontract.Hash256Type), + manifest.NewParameter("attachmentType", smartcontract.StringType), + manifest.NewParameter("encryptedData", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(p.attachToFlow, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // ===== Flow Queries ===== + + desc = NewDescriptor("getFlow", smartcontract.ArrayType, + manifest.NewParameter("flowID", smartcontract.Hash256Type)) + md = NewMethodAndPrice(p.getFlow, 1<<15, callflag.ReadStates|callflag.AllowNotify) + p.AddMethod(md, desc) + + desc = NewDescriptor("getTotalFlows", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getTotalFlows, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + desc = NewDescriptor("getAttachment", smartcontract.ArrayType, + manifest.NewParameter("attachmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getAttachment, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + desc = NewDescriptor("getTotalAttachments", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getTotalAttachments, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // ===== Declassification ===== + + desc = NewDescriptor("requestDeclassify", smartcontract.IntegerType, + manifest.NewParameter("flowID", smartcontract.Hash256Type), + manifest.NewParameter("caseID", smartcontract.StringType), + manifest.NewParameter("reason", smartcontract.StringType), + manifest.NewParameter("expirationBlocks", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.requestDeclassify, 1<<17, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + desc = NewDescriptor("approveDeclassify", smartcontract.BoolType, + manifest.NewParameter("requestID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.approveDeclassify, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + desc = NewDescriptor("denyDeclassify", smartcontract.BoolType, + manifest.NewParameter("requestID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(p.denyDeclassify, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + desc = NewDescriptor("getDeclassifyRequest", smartcontract.ArrayType, + manifest.NewParameter("requestID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getDeclassifyRequest, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + desc = NewDescriptor("getTotalRequests", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getTotalRequests, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + desc = NewDescriptor("hasDeclassifyGrant", smartcontract.BoolType, + manifest.NewParameter("flowID", smartcontract.Hash256Type), + manifest.NewParameter("requester", smartcontract.Hash160Type)) + md = NewMethodAndPrice(p.hasDeclassifyGrant, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // ===== Access Logging ===== + + desc = NewDescriptor("getAccessLog", smartcontract.ArrayType, + manifest.NewParameter("logID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getAccessLog, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + desc = NewDescriptor("getTotalLogs", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getTotalLogs, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // ===== Role & Permissions ===== + + desc = NewDescriptor("getRolePermissions", smartcontract.ArrayType, + manifest.NewParameter("role", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getRolePermissions, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + return p +} + +// Metadata returns the metadata of the contract. +func (p *Palam) Metadata() *interop.ContractMD { + return &p.ContractMD +} + +// Initialize initializes the Palam contract. +func (p *Palam) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != p.ActiveIn() { + return nil + } + + // Initialize counters + p.setFlowCounter(ic.DAO, 0) + p.setRequestCounter(ic.DAO, 0) + p.setLogCounter(ic.DAO, 0) + p.setAttachmentCounter(ic.DAO, 0) + + // Set default config + cfg := state.DefaultPalamConfig() + cfgItem := cfg.ToStackItem() + cfgBytes, err := ic.DAO.GetItemCtx().Serialize(cfgItem, false) + if err != nil { + return err + } + ic.DAO.PutStorageItem(p.ID, []byte{palamPrefixConfig}, cfgBytes) + + return nil +} + +// InitializeCache initializes the contract cache. +func (p *Palam) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + return nil // No cache needed - use storage directly +} + +// ActiveIn returns the hardfork at which the contract becomes active. +func (p *Palam) ActiveIn() *config.Hardfork { + return nil // Always active +} + +// Address returns the contract's script hash. +func (p *Palam) Address() util.Uint160 { + return p.Hash +} + +// GetFlowInternal returns a flow by ID (for cross-contract use). +func (p *Palam) GetFlowInternal(d *dao.Simple, flowID util.Uint256) *state.Flow { + return p.getFlowInternal(d, flowID) +} + +// HasDeclassifyGrant checks if requester has declassify grant for a flow (for cross-contract use). +func (p *Palam) HasDeclassifyGrant(d *dao.Simple, flowID util.Uint256, requester util.Uint160) bool { + return p.hasGrantInternal(d, flowID, requester) +} + +// OnPersist implements the Contract interface. +func (p *Palam) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (p *Palam) PostPersist(ic *interop.Context) error { + return nil +} + +// ===== Configuration ===== + +func (p *Palam) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + cfg := p.getConfigInternal(ic.DAO) + return cfg.ToStackItem() +} + +func (p *Palam) getConfigInternal(d *dao.Simple) *state.PalamConfig { + si := d.GetStorageItem(p.ID, []byte{palamPrefixConfig}) + if si == nil { + return state.DefaultPalamConfig() + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return state.DefaultPalamConfig() + } + + cfg := &state.PalamConfig{} + if err := cfg.FromStackItem(item); err != nil { + return state.DefaultPalamConfig() + } + return cfg +} + +// ===== Counter Helpers ===== + +func (p *Palam) getFlowCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(p.ID, []byte{palamPrefixFlowCounter}) + if si == nil || len(si) == 0 { + return 0 + } + return binary.LittleEndian.Uint64(si) +} + +func (p *Palam) setFlowCounter(d *dao.Simple, count uint64) { + data := make([]byte, 8) + binary.LittleEndian.PutUint64(data, count) + d.PutStorageItem(p.ID, []byte{palamPrefixFlowCounter}, data) +} + +func (p *Palam) getRequestCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(p.ID, []byte{palamPrefixRequestCounter}) + if si == nil || len(si) == 0 { + return 0 + } + return binary.LittleEndian.Uint64(si) +} + +func (p *Palam) setRequestCounter(d *dao.Simple, count uint64) { + data := make([]byte, 8) + binary.LittleEndian.PutUint64(data, count) + d.PutStorageItem(p.ID, []byte{palamPrefixRequestCounter}, data) +} + +func (p *Palam) getNextRequestID(d *dao.Simple) uint64 { + id := p.getRequestCounter(d) + 1 + p.setRequestCounter(d, id) + return id +} + +func (p *Palam) getLogCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(p.ID, []byte{palamPrefixLogCounter}) + if si == nil || len(si) == 0 { + return 0 + } + return binary.LittleEndian.Uint64(si) +} + +func (p *Palam) setLogCounter(d *dao.Simple, count uint64) { + data := make([]byte, 8) + binary.LittleEndian.PutUint64(data, count) + d.PutStorageItem(p.ID, []byte{palamPrefixLogCounter}, data) +} + +func (p *Palam) getNextLogID(d *dao.Simple) uint64 { + id := p.getLogCounter(d) + 1 + p.setLogCounter(d, id) + return id +} + +func (p *Palam) getAttachmentCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(p.ID, []byte{palamPrefixAttachmentCounter}) + if si == nil || len(si) == 0 { + return 0 + } + return binary.LittleEndian.Uint64(si) +} + +func (p *Palam) setAttachmentCounter(d *dao.Simple, count uint64) { + data := make([]byte, 8) + binary.LittleEndian.PutUint64(data, count) + d.PutStorageItem(p.ID, []byte{palamPrefixAttachmentCounter}, data) +} + +func (p *Palam) getNextAttachmentID(d *dao.Simple) uint64 { + id := p.getAttachmentCounter(d) + 1 + p.setAttachmentCounter(d, id) + return id +} + +// ===== Flow Recording ===== + +func (p *Palam) recordFlow(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tag, err := stackitem.ToString(args[0]) + if err != nil || len(tag) == 0 || len(tag) > 64 { + panic(ErrPalamInvalidTag) + } + + amountBI, err := args[1].TryInteger() + if err != nil { + panic(err) + } + amount := amountBI.Uint64() + + bucket, err := stackitem.ToString(args[2]) + if err != nil || len(bucket) == 0 || len(bucket) > 32 { + panic(ErrPalamInvalidBucket) + } + + participantsArr, ok := args[3].Value().([]stackitem.Item) + if !ok { + panic("invalid participants array") + } + participants := make([]util.Uint160, len(participantsArr)) + for i, p := range participantsArr { + pBytes, err := p.TryBytes() + if err != nil { + panic(err) + } + participants[i], err = util.Uint160DecodeBytesBE(pBytes) + if err != nil { + panic(err) + } + } + + consumerData, err := args[4].TryBytes() + if err != nil { + panic(err) + } + merchantData, err := args[5].TryBytes() + if err != nil { + panic(err) + } + distributorData, err := args[6].TryBytes() + if err != nil { + panic(err) + } + producerData, err := args[7].TryBytes() + if err != nil { + panic(err) + } + ngoData, err := args[8].TryBytes() + if err != nil { + panic(err) + } + auditorData, err := args[9].TryBytes() + if err != nil { + panic(err) + } + + prevFlowBytes, err := args[10].TryBytes() + if err != nil { + panic(err) + } + var previousFlowID util.Uint256 + if len(prevFlowBytes) == 32 { + previousFlowID, err = util.Uint256DecodeBytesBE(prevFlowBytes) + if err != nil { + panic(err) + } + } + + caller := ic.VM.GetCallingScriptHash() + + // Verify caller has Vita + if p.Vita != nil && !p.Vita.TokenExists(ic.DAO, caller) { + panic(ErrPalamNoVita) + } + + // Generate flow ID from content hash + flowData := append([]byte(tag), []byte(bucket)...) + flowData = append(flowData, caller.BytesBE()...) + flowData = append(flowData, consumerData...) + flowData = append(flowData, merchantData...) + blockBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(blockBytes, ic.Block.Index) + flowData = append(flowData, blockBytes...) + flowID := hash.Sha256(flowData) + + // Check if flow already exists + if p.getFlowInternal(ic.DAO, flowID) != nil { + panic(ErrPalamFlowExists) + } + + flow := &state.Flow{ + FlowID: flowID, + Bucket: bucket, + Tag: tag, + Amount: amount, + Timestamp: ic.Block.Index, + Creator: caller, + ConsumerData: consumerData, + MerchantData: merchantData, + DistributorData: distributorData, + ProducerData: producerData, + NGOData: ngoData, + AuditorData: auditorData, + Participants: participants, + PreviousFlowID: previousFlowID, + Status: state.FlowStatusActive, + } + + // Store flow + p.putFlow(ic.DAO, flow) + + // Update counter + count := p.getFlowCounter(ic.DAO) + 1 + p.setFlowCounter(ic.DAO, count) + + // Emit event + ic.AddNotification(p.Hash, FlowRecordedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(flowID.BytesBE()), + stackitem.NewByteArray([]byte(tag)), + stackitem.NewByteArray([]byte(bucket)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(ic.Block.Index))), + })) + + return stackitem.NewByteArray(flowID.BytesBE()) +} + +func (p *Palam) putFlow(d *dao.Simple, flow *state.Flow) { + flowItem := flow.ToStackItem() + flowBytes, err := d.GetItemCtx().Serialize(flowItem, false) + if err != nil { + panic(err) + } + + // Store main record + key := append([]byte{palamPrefixFlow}, flow.FlowID.BytesBE()...) + d.PutStorageItem(p.ID, key, flowBytes) + + // Index by bucket + bucketKey := append([]byte{palamPrefixFlowByBucket}, []byte(flow.Bucket)...) + bucketKey = append(bucketKey, flow.FlowID.BytesBE()...) + d.PutStorageItem(p.ID, bucketKey, []byte{1}) + + // Index by tag + tagKey := append([]byte{palamPrefixFlowByTag}, []byte(flow.Tag)...) + tagKey = append(tagKey, flow.FlowID.BytesBE()...) + d.PutStorageItem(p.ID, tagKey, []byte{1}) + + // Index by participants + for _, participant := range flow.Participants { + partKey := append([]byte{palamPrefixFlowByParticipant}, participant.BytesBE()...) + partKey = append(partKey, flow.FlowID.BytesBE()...) + d.PutStorageItem(p.ID, partKey, []byte{1}) + } +} + +func (p *Palam) getFlowInternal(d *dao.Simple, flowID util.Uint256) *state.Flow { + key := append([]byte{palamPrefixFlow}, flowID.BytesBE()...) + si := d.GetStorageItem(p.ID, key) + if si == nil { + return nil + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return nil + } + + flow := &state.Flow{} + if err := flow.FromStackItem(item); err != nil { + return nil + } + return flow +} + +func (p *Palam) getFlow(ic *interop.Context, args []stackitem.Item) stackitem.Item { + flowIDBytes, err := args[0].TryBytes() + if err != nil { + panic(err) + } + flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) + if err != nil { + panic(err) + } + + flow := p.getFlowInternal(ic.DAO, flowID) + if flow == nil { + return stackitem.Null{} + } + + // Log access if configured + cfg := p.getConfigInternal(ic.DAO) + if cfg.LogAllAccess { + caller := ic.VM.GetCallingScriptHash() + p.logAccess(ic, flowID, caller, state.AccessTypeView, "getFlow") + } + + return flow.ToStackItem() +} + +func (p *Palam) getTotalFlows(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getFlowCounter(ic.DAO))) +} + +// ===== Attachments ===== + +func (p *Palam) attachToFlow(ic *interop.Context, args []stackitem.Item) stackitem.Item { + flowIDBytes, err := args[0].TryBytes() + if err != nil { + panic(err) + } + flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) + if err != nil { + panic(err) + } + + attachmentType, err := stackitem.ToString(args[1]) + if err != nil || len(attachmentType) == 0 || len(attachmentType) > 64 { + panic(ErrPalamInvalidAttachment) + } + + encryptedData, err := args[2].TryBytes() + if err != nil { + panic(err) + } + + caller := ic.VM.GetCallingScriptHash() + + // Verify flow exists + flow := p.getFlowInternal(ic.DAO, flowID) + if flow == nil { + panic(ErrPalamFlowNotFound) + } + + // Verify caller is a participant + isParticipant := false + for _, p := range flow.Participants { + if p.Equals(caller) { + isParticipant = true + break + } + } + if !isParticipant && !flow.Creator.Equals(caller) { + panic(ErrPalamNotParticipant) + } + + attachmentID := p.getNextAttachmentID(ic.DAO) + + attachment := &state.FlowAttachment{ + AttachmentID: attachmentID, + FlowID: flowID, + AttachmentType: attachmentType, + EncryptedData: encryptedData, + Attacher: caller, + AttachedAt: ic.Block.Index, + } + + p.putAttachment(ic.DAO, attachment) + + // Emit event + ic.AddNotification(p.Hash, FlowAttachmentEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(flowID.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(attachmentID)), + stackitem.NewByteArray([]byte(attachmentType)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(attachmentID)) +} + +func (p *Palam) putAttachment(d *dao.Simple, att *state.FlowAttachment) { + attItem := att.ToStackItem() + attBytes, err := d.GetItemCtx().Serialize(attItem, false) + if err != nil { + panic(err) + } + + idBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(idBytes, att.AttachmentID) + + // Store main record + key := append([]byte{palamPrefixAttachment}, idBytes...) + d.PutStorageItem(p.ID, key, attBytes) + + // Index by flow + flowKey := append([]byte{palamPrefixAttachmentByFlow}, att.FlowID.BytesBE()...) + flowKey = append(flowKey, idBytes...) + d.PutStorageItem(p.ID, flowKey, []byte{1}) +} + +func (p *Palam) getAttachmentInternal(d *dao.Simple, attachmentID uint64) *state.FlowAttachment { + idBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(idBytes, attachmentID) + + key := append([]byte{palamPrefixAttachment}, idBytes...) + si := d.GetStorageItem(p.ID, key) + if si == nil { + return nil + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return nil + } + + att := &state.FlowAttachment{} + if err := att.FromStackItem(item); err != nil { + return nil + } + return att +} + +func (p *Palam) getAttachment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + attachmentID, err := args[0].TryInteger() + if err != nil { + panic(err) + } + + att := p.getAttachmentInternal(ic.DAO, attachmentID.Uint64()) + if att == nil { + return stackitem.Null{} + } + + return att.ToStackItem() +} + +func (p *Palam) getTotalAttachments(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getAttachmentCounter(ic.DAO))) +} + +// ===== Declassification ===== + +func (p *Palam) requestDeclassify(ic *interop.Context, args []stackitem.Item) stackitem.Item { + flowIDBytes, err := args[0].TryBytes() + if err != nil { + panic(err) + } + flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) + if err != nil { + panic(err) + } + + caseID, err := stackitem.ToString(args[1]) + if err != nil || len(caseID) == 0 || len(caseID) > 64 { + panic(ErrPalamInvalidCaseID) + } + + reason, err := stackitem.ToString(args[2]) + if err != nil || len(reason) < 50 || len(reason) > 1024 { + panic(ErrPalamInvalidReason) + } + + expirationBlocks, err := args[3].TryInteger() + if err != nil { + panic(err) + } + expBlocks := uint32(expirationBlocks.Uint64()) + + caller := ic.VM.GetCallingScriptHash() + + // Verify flow exists + flow := p.getFlowInternal(ic.DAO, flowID) + if flow == nil { + panic(ErrPalamFlowNotFound) + } + + // Verify caller is an auditor + if p.RoleRegistry != nil { + if !p.RoleRegistry.HasRoleInternal(ic.DAO, caller, RolePalamAuditor, ic.Block.Index) { + panic(ErrPalamNotAuditor) + } + } + + // Get config for defaults + cfg := p.getConfigInternal(ic.DAO) + if expBlocks == 0 { + expBlocks = cfg.DefaultExpiration + } + + requestID := p.getNextRequestID(ic.DAO) + + request := &state.DeclassifyRequest{ + RequestID: requestID, + FlowID: flowID, + CaseID: caseID, + Reason: reason, + Requester: caller, + RequesterRole: state.PalamRoleAuditor, + RequiredApprovals: cfg.MinApprovals, + Approvals: []util.Uint160{}, + ApprovalTimes: []uint32{}, + Status: state.DeclassifyPending, + CreatedAt: ic.Block.Index, + ExpiresAt: ic.Block.Index + expBlocks, + GrantedAt: 0, + } + + p.putDeclassifyRequest(ic.DAO, request) + + // Emit event + ic.AddNotification(p.Hash, DeclassifyRequestedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)), + stackitem.NewByteArray(flowID.BytesBE()), + stackitem.NewByteArray([]byte(caseID)), + stackitem.NewByteArray(caller.BytesBE()), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)) +} + +func (p *Palam) putDeclassifyRequest(d *dao.Simple, req *state.DeclassifyRequest) { + reqItem := req.ToStackItem() + reqBytes, err := d.GetItemCtx().Serialize(reqItem, false) + if err != nil { + panic(err) + } + + idBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(idBytes, req.RequestID) + + // Store main record + key := append([]byte{palamPrefixRequest}, idBytes...) + d.PutStorageItem(p.ID, key, reqBytes) + + // Index by flow + flowKey := append([]byte{palamPrefixRequestByFlow}, req.FlowID.BytesBE()...) + flowKey = append(flowKey, idBytes...) + d.PutStorageItem(p.ID, flowKey, []byte{1}) + + // Index by requester + reqKey := append([]byte{palamPrefixRequestByRequester}, req.Requester.BytesBE()...) + reqKey = append(reqKey, idBytes...) + d.PutStorageItem(p.ID, reqKey, []byte{1}) + + // Track pending request for flow + if req.Status == state.DeclassifyPending { + pendingKey := append([]byte{palamPrefixPendingRequest}, req.FlowID.BytesBE()...) + d.PutStorageItem(p.ID, pendingKey, idBytes) + } +} + +func (p *Palam) getRequestInternal(d *dao.Simple, requestID uint64) *state.DeclassifyRequest { + idBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(idBytes, requestID) + + key := append([]byte{palamPrefixRequest}, idBytes...) + si := d.GetStorageItem(p.ID, key) + if si == nil { + return nil + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return nil + } + + req := &state.DeclassifyRequest{} + if err := req.FromStackItem(item); err != nil { + return nil + } + return req +} + +func (p *Palam) approveDeclassify(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestID, err := args[0].TryInteger() + if err != nil { + panic(err) + } + + caller := ic.VM.GetCallingScriptHash() + + // Verify caller is a judge + if p.RoleRegistry != nil { + if !p.RoleRegistry.HasRoleInternal(ic.DAO, caller, RolePalamJudge, ic.Block.Index) { + panic(ErrPalamNotJudge) + } + } + + req := p.getRequestInternal(ic.DAO, requestID.Uint64()) + if req == nil { + panic(ErrPalamRequestNotFound) + } + + // Check request is pending + if req.Status != state.DeclassifyPending { + panic(ErrPalamRequestNotPending) + } + + // Check not expired + if ic.Block.Index > req.ExpiresAt { + req.Status = state.DeclassifyExpired + p.putDeclassifyRequest(ic.DAO, req) + ic.AddNotification(p.Hash, DeclassifyExpiredEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), + })) + panic(ErrPalamRequestExpired) + } + + // Cannot approve own request + if caller.Equals(req.Requester) { + panic(ErrPalamCannotSelfApprove) + } + + // Check not already approved by this caller + for _, approver := range req.Approvals { + if approver.Equals(caller) { + panic(ErrPalamAlreadyApproved) + } + } + + // Add approval + req.Approvals = append(req.Approvals, caller) + req.ApprovalTimes = append(req.ApprovalTimes, ic.Block.Index) + + // Check if enough approvals + if uint32(len(req.Approvals)) >= req.RequiredApprovals { + req.Status = state.DeclassifyApproved + req.GrantedAt = ic.Block.Index + + // Emit granted event + ic.AddNotification(p.Hash, DeclassifyGrantedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), + stackitem.NewByteArray(req.FlowID.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(req.GrantedAt))), + })) + } + + p.putDeclassifyRequest(ic.DAO, req) + + // Emit approval event + ic.AddNotification(p.Hash, DeclassifyApprovalEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), + stackitem.NewByteArray(caller.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetInt64(int64(len(req.Approvals)))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(req.RequiredApprovals))), + })) + + return stackitem.NewBool(true) +} + +func (p *Palam) denyDeclassify(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestID, err := args[0].TryInteger() + if err != nil { + panic(err) + } + + reason, err := stackitem.ToString(args[1]) + if err != nil { + panic(err) + } + + caller := ic.VM.GetCallingScriptHash() + + // Verify caller is a judge + if p.RoleRegistry != nil { + if !p.RoleRegistry.HasRoleInternal(ic.DAO, caller, RolePalamJudge, ic.Block.Index) { + panic(ErrPalamNotJudge) + } + } + + req := p.getRequestInternal(ic.DAO, requestID.Uint64()) + if req == nil { + panic(ErrPalamRequestNotFound) + } + + if req.Status != state.DeclassifyPending { + panic(ErrPalamRequestNotPending) + } + + req.Status = state.DeclassifyDenied + p.putDeclassifyRequest(ic.DAO, req) + + // Emit denied event + ic.AddNotification(p.Hash, DeclassifyDeniedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(req.RequestID)), + stackitem.NewByteArray(caller.BytesBE()), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +func (p *Palam) getDeclassifyRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestID, err := args[0].TryInteger() + if err != nil { + panic(err) + } + + req := p.getRequestInternal(ic.DAO, requestID.Uint64()) + if req == nil { + return stackitem.Null{} + } + + return req.ToStackItem() +} + +func (p *Palam) getTotalRequests(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getRequestCounter(ic.DAO))) +} + +func (p *Palam) hasDeclassifyGrant(ic *interop.Context, args []stackitem.Item) stackitem.Item { + flowIDBytes, err := args[0].TryBytes() + if err != nil { + panic(err) + } + flowID, err := util.Uint256DecodeBytesBE(flowIDBytes) + if err != nil { + panic(err) + } + + requesterBytes, err := args[1].TryBytes() + if err != nil { + panic(err) + } + requester, err := util.Uint160DecodeBytesBE(requesterBytes) + if err != nil { + panic(err) + } + + // Search for approved request by this requester for this flow + return stackitem.NewBool(p.hasGrantInternal(ic.DAO, flowID, requester)) +} + +func (p *Palam) hasGrantInternal(d *dao.Simple, flowID util.Uint256, requester util.Uint160) bool { + // Iterate through requests for this flow + prefix := append([]byte{palamPrefixRequestByFlow}, flowID.BytesBE()...) + d.Seek(p.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) < 8 { + return true + } + requestID := binary.LittleEndian.Uint64(k[len(k)-8:]) + req := p.getRequestInternal(d, requestID) + if req != nil && req.Status == state.DeclassifyApproved && req.Requester.Equals(requester) { + return false // Found a grant + } + return true + }) + // Note: This is a simplification - in production would track this more efficiently + return false +} + +// ===== Access Logging ===== + +func (p *Palam) logAccess(ic *interop.Context, flowID util.Uint256, accessor util.Uint160, accessType state.AccessType, details string) { + logID := p.getNextLogID(ic.DAO) + + log := &state.AccessLog{ + LogID: logID, + FlowID: flowID, + Accessor: accessor, + AccessType: accessType, + Timestamp: ic.Block.Index, + Details: details, + } + + p.putAccessLog(ic.DAO, log) + + // Emit event + ic.AddNotification(p.Hash, FlowAccessedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(flowID.BytesBE()), + stackitem.NewByteArray(accessor.BytesBE()), + stackitem.NewByteArray([]byte(details)), + })) +} + +func (p *Palam) putAccessLog(d *dao.Simple, log *state.AccessLog) { + logItem := log.ToStackItem() + logBytes, err := d.GetItemCtx().Serialize(logItem, false) + if err != nil { + panic(err) + } + + idBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(idBytes, log.LogID) + + // Store main record + key := append([]byte{palamPrefixAccessLog}, idBytes...) + d.PutStorageItem(p.ID, key, logBytes) + + // Index by flow + flowKey := append([]byte{palamPrefixLogByFlow}, log.FlowID.BytesBE()...) + flowKey = append(flowKey, idBytes...) + d.PutStorageItem(p.ID, flowKey, []byte{1}) + + // Index by accessor + accKey := append([]byte{palamPrefixLogByAccessor}, log.Accessor.BytesBE()...) + accKey = append(accKey, idBytes...) + d.PutStorageItem(p.ID, accKey, []byte{1}) +} + +func (p *Palam) getAccessLogInternal(d *dao.Simple, logID uint64) *state.AccessLog { + idBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(idBytes, logID) + + key := append([]byte{palamPrefixAccessLog}, idBytes...) + si := d.GetStorageItem(p.ID, key) + if si == nil { + return nil + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return nil + } + + log := &state.AccessLog{} + if err := log.FromStackItem(item); err != nil { + return nil + } + return log +} + +func (p *Palam) getAccessLog(ic *interop.Context, args []stackitem.Item) stackitem.Item { + logID, err := args[0].TryInteger() + if err != nil { + panic(err) + } + + log := p.getAccessLogInternal(ic.DAO, logID.Uint64()) + if log == nil { + return stackitem.Null{} + } + + return log.ToStackItem() +} + +func (p *Palam) getTotalLogs(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(p.getLogCounter(ic.DAO))) +} + +// ===== Role Permissions ===== + +func (p *Palam) getRolePermissions(ic *interop.Context, args []stackitem.Item) stackitem.Item { + roleInt, err := args[0].TryInteger() + if err != nil { + panic(err) + } + role := state.PalamRole(roleInt.Uint64()) + + permissions := state.DefaultRolePermissions(role) + return permissions.ToStackItem() +} diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index cf342ba..4a21e1c 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -9,21 +9,21 @@ import ( "math/big" "slices" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) const ( diff --git a/pkg/core/native/policy_test.go b/pkg/core/native/policy_test.go index ac30f0f..d3f2f87 100644 --- a/pkg/core/native/policy_test.go +++ b/pkg/core/native/policy_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/pons.go b/pkg/core/native/pons.go index db650ca..e085a9d 100644 --- a/pkg/core/native/pons.go +++ b/pkg/core/native/pons.go @@ -1,1260 +1,1260 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Pons ("bridge" in Latin) represents the Inter-Government Bridge Protocol -// native contract. It manages: -// - Bilateral agreements between sovereign chains -// - Cross-border verification requests -// - International VTS settlement -// - Education and healthcare credential portability -type Pons struct { - interop.ContractMD - Tutus ITutus - Vita IVita - Federation *Federation - RoleRegistry *RoleRegistry - VTS *VTS - Scire *Scire - Salus *Salus -} - -// Storage key prefixes for Pons. -const ( - ponsPrefixConfig byte = 0x01 // -> PonsConfig - ponsPrefixAgreement byte = 0x10 // agreementID -> BilateralAgreement - ponsPrefixAgreementByChain byte = 0x11 // chainID + agreementID -> exists - ponsPrefixAgreementCounter byte = 0x1F // -> next agreementID - ponsPrefixVerification byte = 0x20 // requestID -> VerificationRequest - ponsPrefixVerifBySubject byte = 0x21 // subject + requestID -> exists - ponsPrefixVerifCounter byte = 0x2F // -> next verificationID - ponsPrefixSettlement byte = 0x30 // settlementID -> SettlementRequest - ponsPrefixSettlByChain byte = 0x31 // chainID + settlementID -> exists - ponsPrefixSettlCounter byte = 0x3F // -> next settlementID - ponsPrefixCredential byte = 0x40 // credentialID -> CredentialShare - ponsPrefixCredByOwner byte = 0x41 // owner + credentialID -> exists - ponsPrefixCredCounter byte = 0x4F // -> next credentialID -) - -// Default configuration values. -const ( - defaultLocalChainID uint32 = 1 - defaultVerificationTimeout uint32 = 8640 // ~1 day at 10s blocks - defaultSettlementTimeout uint32 = 86400 // ~10 days - defaultMaxPendingRequests uint64 = 10000 - defaultCredentialShareExpiry uint32 = 315360 // ~1 year -) - -// Event names for Pons. -const ( - AgreementCreatedEvent = "AgreementCreated" - AgreementUpdatedEvent = "AgreementUpdated" - AgreementTerminatedEvent = "AgreementTerminated" - VerificationRequestedEvent = "VerificationRequested" - VerificationRespondedEvent = "VerificationResponded" - SettlementRequestedEvent = "SettlementRequested" - SettlementCompletedEvent = "SettlementCompleted" - CredentialSharedEvent = "CredentialShared" - CredentialRevokedEvent = "CredentialRevoked" -) - -// Various errors for Pons. -var ( - ErrNoAgreement = errors.New("no active agreement with target chain") - ErrAgreementExists = errors.New("agreement already exists") - ErrAgreementNotFound = errors.New("agreement not found") - ErrInvalidAgreementType = errors.New("invalid agreement type") - ErrAgreementNotActive = errors.New("agreement not active") - ErrVerificationNotFound = errors.New("verification request not found") - ErrVerificationExpired = errors.New("verification request expired") - ErrSettlementNotFound = errors.New("settlement request not found") - ErrSettlementExpired = errors.New("settlement request expired") - ErrCredentialNotFound = errors.New("credential share not found") - ErrCredentialExpired = errors.New("credential share expired") - ErrCredentialRevoked = errors.New("credential share revoked") - ErrMaxRequestsReached = errors.New("maximum pending requests reached") - ErrNotCredentialOwner = errors.New("not credential owner") - ErrAgreementTypeNotAllowed = errors.New("agreement type not allowed for this operation") -) - -var _ interop.Contract = (*Pons)(nil) - -// newPons creates a new Pons native contract. -func newPons() *Pons { - p := &Pons{ - ContractMD: *interop.NewContractMD(nativenames.Pons, nativeids.Pons), - } - defer p.BuildHFSpecificMD(p.ActiveIn()) - - // getConfig method - desc := NewDescriptor("getConfig", smartcontract.ArrayType) - md := NewMethodAndPrice(p.getConfig, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // setLocalChainID method (committee only) - desc = NewDescriptor("setLocalChainID", smartcontract.BoolType, - manifest.NewParameter("chainID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.setLocalChainID, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // --- Agreement Management --- - - // createAgreement method (committee only) - desc = NewDescriptor("createAgreement", smartcontract.IntegerType, - manifest.NewParameter("remoteChainID", smartcontract.IntegerType), - manifest.NewParameter("agreementType", smartcontract.IntegerType), - manifest.NewParameter("termsHash", smartcontract.Hash256Type), - manifest.NewParameter("expirationHeight", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.createAgreement, 1<<17, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // updateAgreementStatus method (committee only) - desc = NewDescriptor("updateAgreementStatus", smartcontract.BoolType, - manifest.NewParameter("agreementID", smartcontract.IntegerType), - manifest.NewParameter("newStatus", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.updateAgreementStatus, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // getAgreement method - desc = NewDescriptor("getAgreement", smartcontract.ArrayType, - manifest.NewParameter("agreementID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getAgreement, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // hasActiveAgreement method - desc = NewDescriptor("hasActiveAgreement", smartcontract.BoolType, - manifest.NewParameter("remoteChainID", smartcontract.IntegerType), - manifest.NewParameter("agreementType", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.hasActiveAgreement, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // --- Verification Requests --- - - // requestVerification method - desc = NewDescriptor("requestVerification", smartcontract.IntegerType, - manifest.NewParameter("targetChainID", smartcontract.IntegerType), - manifest.NewParameter("subject", smartcontract.Hash160Type), - manifest.NewParameter("verificationType", smartcontract.IntegerType), - manifest.NewParameter("dataHash", smartcontract.Hash256Type)) - md = NewMethodAndPrice(p.requestVerification, 1<<17, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // respondVerification method (committee only - represents response from other chain) - desc = NewDescriptor("respondVerification", smartcontract.BoolType, - manifest.NewParameter("requestID", smartcontract.IntegerType), - manifest.NewParameter("approved", smartcontract.BoolType), - manifest.NewParameter("responseHash", smartcontract.Hash256Type)) - md = NewMethodAndPrice(p.respondVerification, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // getVerificationRequest method - desc = NewDescriptor("getVerificationRequest", smartcontract.ArrayType, - manifest.NewParameter("requestID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getVerificationRequest, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // --- Settlement Requests --- - - // requestSettlement method - desc = NewDescriptor("requestSettlement", smartcontract.IntegerType, - manifest.NewParameter("toChainID", smartcontract.IntegerType), - manifest.NewParameter("receiver", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("reference", smartcontract.StringType)) - md = NewMethodAndPrice(p.requestSettlement, 1<<17, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // completeSettlement method (committee only - represents confirmation from other chain) - desc = NewDescriptor("completeSettlement", smartcontract.BoolType, - manifest.NewParameter("settlementID", smartcontract.IntegerType), - manifest.NewParameter("txHash", smartcontract.Hash256Type)) - md = NewMethodAndPrice(p.completeSettlement, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // cancelSettlement method (sender or committee) - desc = NewDescriptor("cancelSettlement", smartcontract.BoolType, - manifest.NewParameter("settlementID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.cancelSettlement, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // getSettlementRequest method - desc = NewDescriptor("getSettlementRequest", smartcontract.ArrayType, - manifest.NewParameter("settlementID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getSettlementRequest, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // --- Credential Sharing --- - - // shareCredential method - desc = NewDescriptor("shareCredential", smartcontract.IntegerType, - manifest.NewParameter("targetChainID", smartcontract.IntegerType), - manifest.NewParameter("credentialType", smartcontract.IntegerType), - manifest.NewParameter("credentialID", smartcontract.IntegerType), - manifest.NewParameter("contentHash", smartcontract.Hash256Type), - manifest.NewParameter("validUntil", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.shareCredential, 1<<17, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // revokeCredentialShare method - desc = NewDescriptor("revokeCredentialShare", smartcontract.BoolType, - manifest.NewParameter("shareID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.revokeCredentialShare, 1<<16, callflag.States|callflag.AllowNotify) - p.AddMethod(md, desc) - - // getCredentialShare method - desc = NewDescriptor("getCredentialShare", smartcontract.ArrayType, - manifest.NewParameter("shareID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.getCredentialShare, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // verifyCredentialShare method - desc = NewDescriptor("verifyCredentialShare", smartcontract.BoolType, - manifest.NewParameter("shareID", smartcontract.IntegerType)) - md = NewMethodAndPrice(p.verifyCredentialShare, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // --- Counter Query Methods --- - - // getAgreementCount method - desc = NewDescriptor("getAgreementCount", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getAgreementCount, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // getVerificationCount method - desc = NewDescriptor("getVerificationCount", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getVerificationCount, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // getSettlementCount method - desc = NewDescriptor("getSettlementCount", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getSettlementCount, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // getCredentialShareCount method - desc = NewDescriptor("getCredentialShareCount", smartcontract.IntegerType) - md = NewMethodAndPrice(p.getCredentialShareCount, 1<<15, callflag.ReadStates) - p.AddMethod(md, desc) - - // --- Events --- - - eDesc := NewEventDescriptor(AgreementCreatedEvent, - manifest.NewParameter("agreementID", smartcontract.IntegerType), - manifest.NewParameter("remoteChainID", smartcontract.IntegerType), - manifest.NewParameter("agreementType", smartcontract.IntegerType)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AgreementUpdatedEvent, - manifest.NewParameter("agreementID", smartcontract.IntegerType), - manifest.NewParameter("oldStatus", smartcontract.IntegerType), - manifest.NewParameter("newStatus", smartcontract.IntegerType)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AgreementTerminatedEvent, - manifest.NewParameter("agreementID", smartcontract.IntegerType), - manifest.NewParameter("remoteChainID", smartcontract.IntegerType)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VerificationRequestedEvent, - manifest.NewParameter("requestID", smartcontract.IntegerType), - manifest.NewParameter("targetChainID", smartcontract.IntegerType), - manifest.NewParameter("subject", smartcontract.Hash160Type), - manifest.NewParameter("verificationType", smartcontract.IntegerType)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VerificationRespondedEvent, - manifest.NewParameter("requestID", smartcontract.IntegerType), - manifest.NewParameter("approved", smartcontract.BoolType)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(SettlementRequestedEvent, - manifest.NewParameter("settlementID", smartcontract.IntegerType), - manifest.NewParameter("toChainID", smartcontract.IntegerType), - manifest.NewParameter("receiver", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(SettlementCompletedEvent, - manifest.NewParameter("settlementID", smartcontract.IntegerType), - manifest.NewParameter("txHash", smartcontract.Hash256Type)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(CredentialSharedEvent, - manifest.NewParameter("shareID", smartcontract.IntegerType), - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("targetChainID", smartcontract.IntegerType), - manifest.NewParameter("credentialType", smartcontract.IntegerType)) - p.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(CredentialRevokedEvent, - manifest.NewParameter("shareID", smartcontract.IntegerType), - manifest.NewParameter("owner", smartcontract.Hash160Type)) - p.AddEvent(NewEvent(eDesc)) - - return p -} - -// Metadata returns contract metadata. -func (p *Pons) Metadata() *interop.ContractMD { - return &p.ContractMD -} - -// Initialize initializes Pons contract at the specified hardfork. -func (p *Pons) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != p.ActiveIn() { - return nil - } - - // Initialize default config - cfg := state.PonsConfig{ - LocalChainID: defaultLocalChainID, - VerificationTimeout: defaultVerificationTimeout, - SettlementTimeout: defaultSettlementTimeout, - MaxPendingRequests: defaultMaxPendingRequests, - CredentialShareExpiry: defaultCredentialShareExpiry, - } - p.setConfigInternal(ic.DAO, &cfg) - - return nil -} - -// InitializeCache fills native Pons cache from DAO on node restart. -func (p *Pons) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - return nil -} - -// OnPersist implements the Contract interface. -func (p *Pons) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (p *Pons) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn returns the hardfork this contract activates in (nil = always active). -func (p *Pons) ActiveIn() *config.Hardfork { - return nil -} - -// ============================================================================ -// Storage Key Helpers -// ============================================================================ - -func makePonsConfigKey() []byte { - return []byte{ponsPrefixConfig} -} - -func makePonsAgreementKey(agreementID uint64) []byte { - key := make([]byte, 9) - key[0] = ponsPrefixAgreement - binary.BigEndian.PutUint64(key[1:], agreementID) - return key -} - -func makePonsAgreementByChainKey(chainID uint32, agreementID uint64) []byte { - key := make([]byte, 13) - key[0] = ponsPrefixAgreementByChain - binary.BigEndian.PutUint32(key[1:], chainID) - binary.BigEndian.PutUint64(key[5:], agreementID) - return key -} - -func makePonsAgreementCounterKey() []byte { - return []byte{ponsPrefixAgreementCounter} -} - -func makePonsVerificationKey(requestID uint64) []byte { - key := make([]byte, 9) - key[0] = ponsPrefixVerification - binary.BigEndian.PutUint64(key[1:], requestID) - return key -} - -func makePonsVerifBySubjectKey(subject util.Uint160, requestID uint64) []byte { - key := make([]byte, 1+util.Uint160Size+8) - key[0] = ponsPrefixVerifBySubject - copy(key[1:], subject.BytesBE()) - binary.BigEndian.PutUint64(key[1+util.Uint160Size:], requestID) - return key -} - -func makePonsVerificationCounterKey() []byte { - return []byte{ponsPrefixVerifCounter} -} - -func makePonsSettlementKey(settlementID uint64) []byte { - key := make([]byte, 9) - key[0] = ponsPrefixSettlement - binary.BigEndian.PutUint64(key[1:], settlementID) - return key -} - -func makePonsSettlByChainKey(chainID uint32, settlementID uint64) []byte { - key := make([]byte, 13) - key[0] = ponsPrefixSettlByChain - binary.BigEndian.PutUint32(key[1:], chainID) - binary.BigEndian.PutUint64(key[5:], settlementID) - return key -} - -func makePonsSettlementCounterKey() []byte { - return []byte{ponsPrefixSettlCounter} -} - -func makePonsCredentialKey(credentialID uint64) []byte { - key := make([]byte, 9) - key[0] = ponsPrefixCredential - binary.BigEndian.PutUint64(key[1:], credentialID) - return key -} - -func makePonsCredByOwnerKey(owner util.Uint160, credentialID uint64) []byte { - key := make([]byte, 1+util.Uint160Size+8) - key[0] = ponsPrefixCredByOwner - copy(key[1:], owner.BytesBE()) - binary.BigEndian.PutUint64(key[1+util.Uint160Size:], credentialID) - return key -} - -func makePonsCredentialCounterKey() []byte { - return []byte{ponsPrefixCredCounter} -} - -// ============================================================================ -// Internal Storage Methods -// ============================================================================ - -func (p *Pons) getConfigInternal(d *dao.Simple) *state.PonsConfig { - si := d.GetStorageItem(p.ID, makePonsConfigKey()) - if si == nil { - return &state.PonsConfig{ - LocalChainID: defaultLocalChainID, - VerificationTimeout: defaultVerificationTimeout, - SettlementTimeout: defaultSettlementTimeout, - MaxPendingRequests: defaultMaxPendingRequests, - CredentialShareExpiry: defaultCredentialShareExpiry, - } - } - // Decode config: chainID(4) + verifTimeout(4) + settlTimeout(4) + maxReq(8) + credExpiry(4) = 24 bytes - if len(si) < 24 { - return &state.PonsConfig{ - LocalChainID: defaultLocalChainID, - VerificationTimeout: defaultVerificationTimeout, - SettlementTimeout: defaultSettlementTimeout, - MaxPendingRequests: defaultMaxPendingRequests, - CredentialShareExpiry: defaultCredentialShareExpiry, - } - } - return &state.PonsConfig{ - LocalChainID: binary.BigEndian.Uint32(si[0:4]), - VerificationTimeout: binary.BigEndian.Uint32(si[4:8]), - SettlementTimeout: binary.BigEndian.Uint32(si[8:12]), - MaxPendingRequests: binary.BigEndian.Uint64(si[12:20]), - CredentialShareExpiry: binary.BigEndian.Uint32(si[20:24]), - } -} - -func (p *Pons) setConfigInternal(d *dao.Simple, cfg *state.PonsConfig) { - buf := make([]byte, 24) - binary.BigEndian.PutUint32(buf[0:4], cfg.LocalChainID) - binary.BigEndian.PutUint32(buf[4:8], cfg.VerificationTimeout) - binary.BigEndian.PutUint32(buf[8:12], cfg.SettlementTimeout) - binary.BigEndian.PutUint64(buf[12:20], cfg.MaxPendingRequests) - binary.BigEndian.PutUint32(buf[20:24], cfg.CredentialShareExpiry) - d.PutStorageItem(p.ID, makePonsConfigKey(), buf) -} - -func (p *Pons) getCounterInternal(d *dao.Simple, key []byte) uint64 { - si := d.GetStorageItem(p.ID, key) - if si == nil || len(si) < 8 { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (p *Pons) incrementCounterInternal(d *dao.Simple, key []byte) uint64 { - current := p.getCounterInternal(d, key) - next := current + 1 - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, next) - d.PutStorageItem(p.ID, key, buf) - return next -} - -// Agreement storage format: -// localChainID(4) + remoteChainID(4) + agreementType(1) + status(1) + terms(32) + -// effectiveDate(4) + expirationDate(4) + createdAt(4) + updatedAt(4) = 58 bytes -func (p *Pons) getAgreementInternal(d *dao.Simple, agreementID uint64) (*state.BilateralAgreement, bool) { - si := d.GetStorageItem(p.ID, makePonsAgreementKey(agreementID)) - if si == nil || len(si) < 58 { - return nil, false - } - - var terms util.Uint256 - copy(terms[:], si[10:42]) - - return &state.BilateralAgreement{ - ID: agreementID, - LocalChainID: binary.BigEndian.Uint32(si[0:4]), - RemoteChainID: binary.BigEndian.Uint32(si[4:8]), - AgreementType: state.AgreementType(si[8]), - Status: state.AgreementStatus(si[9]), - Terms: terms, - EffectiveDate: binary.BigEndian.Uint32(si[42:46]), - ExpirationDate: binary.BigEndian.Uint32(si[46:50]), - CreatedAt: binary.BigEndian.Uint32(si[50:54]), - UpdatedAt: binary.BigEndian.Uint32(si[54:58]), - }, true -} - -func (p *Pons) setAgreementInternal(d *dao.Simple, agr *state.BilateralAgreement) { - buf := make([]byte, 58) - binary.BigEndian.PutUint32(buf[0:4], agr.LocalChainID) - binary.BigEndian.PutUint32(buf[4:8], agr.RemoteChainID) - buf[8] = byte(agr.AgreementType) - buf[9] = byte(agr.Status) - copy(buf[10:42], agr.Terms[:]) - binary.BigEndian.PutUint32(buf[42:46], agr.EffectiveDate) - binary.BigEndian.PutUint32(buf[46:50], agr.ExpirationDate) - binary.BigEndian.PutUint32(buf[50:54], agr.CreatedAt) - binary.BigEndian.PutUint32(buf[54:58], agr.UpdatedAt) - d.PutStorageItem(p.ID, makePonsAgreementKey(agr.ID), buf) - // Index by chain - d.PutStorageItem(p.ID, makePonsAgreementByChainKey(agr.RemoteChainID, agr.ID), []byte{1}) -} - -// Verification storage format: -// requestingChain(4) + targetChain(4) + subject(20) + verificationType(1) + dataHash(32) + -// status(1) + responseHash(32) + requester(20) + createdAt(4) + expiresAt(4) + respondedAt(4) = 126 bytes -func (p *Pons) getVerificationInternal(d *dao.Simple, requestID uint64) (*state.VerificationRequest, bool) { - si := d.GetStorageItem(p.ID, makePonsVerificationKey(requestID)) - if si == nil || len(si) < 126 { - return nil, false - } - - var subject util.Uint160 - copy(subject[:], si[8:28]) - var dataHash util.Uint256 - copy(dataHash[:], si[29:61]) - var responseHash util.Uint256 - copy(responseHash[:], si[62:94]) - var requester util.Uint160 - copy(requester[:], si[94:114]) - - return &state.VerificationRequest{ - ID: requestID, - RequestingChain: binary.BigEndian.Uint32(si[0:4]), - TargetChain: binary.BigEndian.Uint32(si[4:8]), - Subject: subject, - VerificationType: state.VerificationType(si[28]), - DataHash: dataHash, - Status: state.VerificationStatus(si[61]), - ResponseHash: responseHash, - Requester: requester, - CreatedAt: binary.BigEndian.Uint32(si[114:118]), - ExpiresAt: binary.BigEndian.Uint32(si[118:122]), - RespondedAt: binary.BigEndian.Uint32(si[122:126]), - }, true -} - -func (p *Pons) setVerificationInternal(d *dao.Simple, vr *state.VerificationRequest) { - buf := make([]byte, 126) - binary.BigEndian.PutUint32(buf[0:4], vr.RequestingChain) - binary.BigEndian.PutUint32(buf[4:8], vr.TargetChain) - copy(buf[8:28], vr.Subject[:]) - buf[28] = byte(vr.VerificationType) - copy(buf[29:61], vr.DataHash[:]) - buf[61] = byte(vr.Status) - copy(buf[62:94], vr.ResponseHash[:]) - copy(buf[94:114], vr.Requester[:]) - binary.BigEndian.PutUint32(buf[114:118], vr.CreatedAt) - binary.BigEndian.PutUint32(buf[118:122], vr.ExpiresAt) - binary.BigEndian.PutUint32(buf[122:126], vr.RespondedAt) - d.PutStorageItem(p.ID, makePonsVerificationKey(vr.ID), buf) - // Index by subject - d.PutStorageItem(p.ID, makePonsVerifBySubjectKey(vr.Subject, vr.ID), []byte{1}) -} - -// Settlement storage format: -// fromChain(4) + toChain(4) + sender(20) + receiver(20) + amount(8) + status(1) + -// createdAt(4) + settledAt(4) + txHash(32) + refLen(2) + reference(var) = 99 + ref bytes -func (p *Pons) getSettlementInternal(d *dao.Simple, settlementID uint64) (*state.SettlementRequest, bool) { - si := d.GetStorageItem(p.ID, makePonsSettlementKey(settlementID)) - if si == nil || len(si) < 99 { - return nil, false - } - - var sender util.Uint160 - copy(sender[:], si[8:28]) - var receiver util.Uint160 - copy(receiver[:], si[28:48]) - var txHash util.Uint256 - copy(txHash[:], si[65:97]) - - refLen := binary.BigEndian.Uint16(si[97:99]) - var reference string - if len(si) >= 99+int(refLen) { - reference = string(si[99 : 99+refLen]) - } - - return &state.SettlementRequest{ - ID: settlementID, - FromChain: binary.BigEndian.Uint32(si[0:4]), - ToChain: binary.BigEndian.Uint32(si[4:8]), - Sender: sender, - Receiver: receiver, - Amount: binary.BigEndian.Uint64(si[48:56]), - Status: state.SettlementStatus(si[56]), - CreatedAt: binary.BigEndian.Uint32(si[57:61]), - SettledAt: binary.BigEndian.Uint32(si[61:65]), - TxHash: txHash, - Reference: reference, - }, true -} - -func (p *Pons) setSettlementInternal(d *dao.Simple, sr *state.SettlementRequest) { - refBytes := []byte(sr.Reference) - buf := make([]byte, 99+len(refBytes)) - binary.BigEndian.PutUint32(buf[0:4], sr.FromChain) - binary.BigEndian.PutUint32(buf[4:8], sr.ToChain) - copy(buf[8:28], sr.Sender[:]) - copy(buf[28:48], sr.Receiver[:]) - binary.BigEndian.PutUint64(buf[48:56], sr.Amount) - buf[56] = byte(sr.Status) - binary.BigEndian.PutUint32(buf[57:61], sr.CreatedAt) - binary.BigEndian.PutUint32(buf[61:65], sr.SettledAt) - copy(buf[65:97], sr.TxHash[:]) - binary.BigEndian.PutUint16(buf[97:99], uint16(len(refBytes))) - copy(buf[99:], refBytes) - d.PutStorageItem(p.ID, makePonsSettlementKey(sr.ID), buf) - // Index by chain - d.PutStorageItem(p.ID, makePonsSettlByChainKey(sr.ToChain, sr.ID), []byte{1}) -} - -// CredentialShare storage format: -// sourceChain(4) + targetChain(4) + owner(20) + credentialType(1) + credentialID(8) + -// contentHash(32) + validUntil(4) + createdAt(4) + isRevoked(1) = 78 bytes -func (p *Pons) getCredentialShareInternal(d *dao.Simple, shareID uint64) (*state.CredentialShare, bool) { - si := d.GetStorageItem(p.ID, makePonsCredentialKey(shareID)) - if si == nil || len(si) < 78 { - return nil, false - } - - var owner util.Uint160 - copy(owner[:], si[8:28]) - var contentHash util.Uint256 - copy(contentHash[:], si[37:69]) - - return &state.CredentialShare{ - ID: shareID, - SourceChain: binary.BigEndian.Uint32(si[0:4]), - TargetChain: binary.BigEndian.Uint32(si[4:8]), - Owner: owner, - CredentialType: state.VerificationType(si[28]), - CredentialID: binary.BigEndian.Uint64(si[29:37]), - ContentHash: contentHash, - ValidUntil: binary.BigEndian.Uint32(si[69:73]), - CreatedAt: binary.BigEndian.Uint32(si[73:77]), - IsRevoked: si[77] != 0, - }, true -} - -func (p *Pons) setCredentialShareInternal(d *dao.Simple, cs *state.CredentialShare) { - buf := make([]byte, 78) - binary.BigEndian.PutUint32(buf[0:4], cs.SourceChain) - binary.BigEndian.PutUint32(buf[4:8], cs.TargetChain) - copy(buf[8:28], cs.Owner[:]) - buf[28] = byte(cs.CredentialType) - binary.BigEndian.PutUint64(buf[29:37], cs.CredentialID) - copy(buf[37:69], cs.ContentHash[:]) - binary.BigEndian.PutUint32(buf[69:73], cs.ValidUntil) - binary.BigEndian.PutUint32(buf[73:77], cs.CreatedAt) - if cs.IsRevoked { - buf[77] = 1 - } - d.PutStorageItem(p.ID, makePonsCredentialKey(cs.ID), buf) - // Index by owner - d.PutStorageItem(p.ID, makePonsCredByOwnerKey(cs.Owner, cs.ID), []byte{1}) -} - -// hasActiveAgreementInternal checks if there's an active agreement with the target chain -// for the specified agreement type. -func (p *Pons) hasActiveAgreementInternal(d *dao.Simple, remoteChainID uint32, agreementType state.AgreementType, blockHeight uint32) bool { - count := p.getCounterInternal(d, makePonsAgreementCounterKey()) - for i := uint64(1); i <= count; i++ { - agr, exists := p.getAgreementInternal(d, i) - if !exists { - continue - } - if agr.RemoteChainID != remoteChainID { - continue - } - if agr.Status != state.AgreementActive { - continue - } - // Check expiration - if agr.ExpirationDate > 0 && agr.ExpirationDate < blockHeight { - continue - } - // Check if agreement type matches or is comprehensive - if agr.AgreementType == agreementType || agr.AgreementType == state.AgreementTypeComprehensive { - return true - } - } - return false -} - -// ============================================================================ -// Contract Methods -// ============================================================================ - -func (p *Pons) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - cfg := p.getConfigInternal(ic.DAO) - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(cfg.LocalChainID))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.VerificationTimeout))), - stackitem.NewBigInteger(big.NewInt(int64(cfg.SettlementTimeout))), - stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MaxPendingRequests)), - stackitem.NewBigInteger(big.NewInt(int64(cfg.CredentialShareExpiry))), - }) -} - -func (p *Pons) setLocalChainID(ic *interop.Context, args []stackitem.Item) stackitem.Item { - chainID := uint32(toBigInt(args[0]).Int64()) - - if !p.Tutus.CheckCommittee(ic) { - panic("only committee can set chain ID") - } - - cfg := p.getConfigInternal(ic.DAO) - cfg.LocalChainID = chainID - p.setConfigInternal(ic.DAO, cfg) - - return stackitem.NewBool(true) -} - -func (p *Pons) createAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item { - remoteChainID := uint32(toBigInt(args[0]).Int64()) - agreementType := state.AgreementType(toBigInt(args[1]).Int64()) - termsHashBytes, err := args[2].TryBytes() - if err != nil { - panic(err) - } - termsHash, err := util.Uint256DecodeBytesBE(termsHashBytes) - if err != nil { - panic(err) - } - expirationHeight := uint32(toBigInt(args[3]).Int64()) - - if !p.Tutus.CheckCommittee(ic) { - panic("only committee can create agreements") - } - - if agreementType > state.AgreementTypeComprehensive { - panic(ErrInvalidAgreementType) - } - - cfg := p.getConfigInternal(ic.DAO) - - // Create agreement - agreementID := p.incrementCounterInternal(ic.DAO, makePonsAgreementCounterKey()) - agr := &state.BilateralAgreement{ - ID: agreementID, - LocalChainID: cfg.LocalChainID, - RemoteChainID: remoteChainID, - AgreementType: agreementType, - Status: state.AgreementPending, - Terms: termsHash, - EffectiveDate: 0, // Set when activated - ExpirationDate: expirationHeight, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - p.setAgreementInternal(ic.DAO, agr) - - ic.AddNotification(p.Hash, AgreementCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)), - stackitem.NewBigInteger(big.NewInt(int64(remoteChainID))), - stackitem.NewBigInteger(big.NewInt(int64(agreementType))), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)) -} - -func (p *Pons) updateAgreementStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { - agreementID := toBigInt(args[0]).Uint64() - newStatus := state.AgreementStatus(toBigInt(args[1]).Int64()) - - if !p.Tutus.CheckCommittee(ic) { - panic("only committee can update agreement status") - } - - agr, exists := p.getAgreementInternal(ic.DAO, agreementID) - if !exists { - panic(ErrAgreementNotFound) - } - - oldStatus := agr.Status - agr.Status = newStatus - agr.UpdatedAt = ic.Block.Index - - // Set effective date when activating - if newStatus == state.AgreementActive && oldStatus != state.AgreementActive { - agr.EffectiveDate = ic.Block.Index - } - - p.setAgreementInternal(ic.DAO, agr) - - ic.AddNotification(p.Hash, AgreementUpdatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)), - stackitem.NewBigInteger(big.NewInt(int64(oldStatus))), - stackitem.NewBigInteger(big.NewInt(int64(newStatus))), - })) - - return stackitem.NewBool(true) -} - -func (p *Pons) getAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item { - agreementID := toBigInt(args[0]).Uint64() - - agr, exists := p.getAgreementInternal(ic.DAO, agreementID) - if !exists { - return stackitem.Null{} - } - - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(agr.ID)), - stackitem.NewBigInteger(big.NewInt(int64(agr.LocalChainID))), - stackitem.NewBigInteger(big.NewInt(int64(agr.RemoteChainID))), - stackitem.NewBigInteger(big.NewInt(int64(agr.AgreementType))), - stackitem.NewBigInteger(big.NewInt(int64(agr.Status))), - stackitem.NewByteArray(agr.Terms.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(agr.EffectiveDate))), - stackitem.NewBigInteger(big.NewInt(int64(agr.ExpirationDate))), - stackitem.NewBigInteger(big.NewInt(int64(agr.CreatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(agr.UpdatedAt))), - }) -} - -func (p *Pons) hasActiveAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item { - remoteChainID := uint32(toBigInt(args[0]).Int64()) - agreementType := state.AgreementType(toBigInt(args[1]).Int64()) - - has := p.hasActiveAgreementInternal(ic.DAO, remoteChainID, agreementType, ic.Block.Index) - return stackitem.NewBool(has) -} - -func (p *Pons) requestVerification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - targetChainID := uint32(toBigInt(args[0]).Int64()) - subject := toUint160(args[1]) - verificationType := state.VerificationType(toBigInt(args[2]).Int64()) - dataHashBytes, err := args[3].TryBytes() - if err != nil { - panic(err) - } - dataHash, err := util.Uint256DecodeBytesBE(dataHashBytes) - if err != nil { - panic(err) - } - - cfg := p.getConfigInternal(ic.DAO) - - // Check for active agreement - agreementType := state.AgreementTypeIdentity - switch verificationType { - case state.VerificationTypeCredential, state.VerificationTypeCertificate: - agreementType = state.AgreementTypeEducation - case state.VerificationTypeHealth: - agreementType = state.AgreementTypeHealthcare - } - - if !p.hasActiveAgreementInternal(ic.DAO, targetChainID, agreementType, ic.Block.Index) { - panic(ErrNoAgreement) - } - - // Get requester from caller - requester := ic.VM.GetCallingScriptHash() - - // Create verification request - requestID := p.incrementCounterInternal(ic.DAO, makePonsVerificationCounterKey()) - vr := &state.VerificationRequest{ - ID: requestID, - RequestingChain: cfg.LocalChainID, - TargetChain: targetChainID, - Subject: subject, - VerificationType: verificationType, - DataHash: dataHash, - Status: state.VerificationPending, - Requester: requester, - CreatedAt: ic.Block.Index, - ExpiresAt: ic.Block.Index + cfg.VerificationTimeout, - } - p.setVerificationInternal(ic.DAO, vr) - - ic.AddNotification(p.Hash, VerificationRequestedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)), - stackitem.NewBigInteger(big.NewInt(int64(targetChainID))), - stackitem.NewByteArray(subject.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(verificationType))), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)) -} - -func (p *Pons) respondVerification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestID := toBigInt(args[0]).Uint64() - approved := toBool(args[1]) - responseHashBytes, err := args[2].TryBytes() - if err != nil { - panic(err) - } - responseHash, err := util.Uint256DecodeBytesBE(responseHashBytes) - if err != nil { - panic(err) - } - - if !p.Tutus.CheckCommittee(ic) { - panic("only committee can respond to verification requests") - } - - vr, exists := p.getVerificationInternal(ic.DAO, requestID) - if !exists { - panic(ErrVerificationNotFound) - } - - if vr.Status != state.VerificationPending { - panic("verification request already processed") - } - - if ic.Block.Index > vr.ExpiresAt { - vr.Status = state.VerificationExpired - p.setVerificationInternal(ic.DAO, vr) - panic(ErrVerificationExpired) - } - - if approved { - vr.Status = state.VerificationApproved - } else { - vr.Status = state.VerificationRejected - } - vr.ResponseHash = responseHash - vr.RespondedAt = ic.Block.Index - p.setVerificationInternal(ic.DAO, vr) - - ic.AddNotification(p.Hash, VerificationRespondedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)), - stackitem.NewBool(approved), - })) - - return stackitem.NewBool(true) -} - -func (p *Pons) getVerificationRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestID := toBigInt(args[0]).Uint64() - - vr, exists := p.getVerificationInternal(ic.DAO, requestID) - if !exists { - return stackitem.Null{} - } - - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(vr.ID)), - stackitem.NewBigInteger(big.NewInt(int64(vr.RequestingChain))), - stackitem.NewBigInteger(big.NewInt(int64(vr.TargetChain))), - stackitem.NewByteArray(vr.Subject.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(vr.VerificationType))), - stackitem.NewByteArray(vr.DataHash.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(vr.Status))), - stackitem.NewByteArray(vr.ResponseHash.BytesBE()), - stackitem.NewByteArray(vr.Requester.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(vr.CreatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(vr.ExpiresAt))), - stackitem.NewBigInteger(big.NewInt(int64(vr.RespondedAt))), - }) -} - -func (p *Pons) requestSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item { - toChainID := uint32(toBigInt(args[0]).Int64()) - receiver := toUint160(args[1]) - amount := toBigInt(args[2]).Uint64() - reference := toString(args[3]) - - cfg := p.getConfigInternal(ic.DAO) - - // Check for settlement agreement - if !p.hasActiveAgreementInternal(ic.DAO, toChainID, state.AgreementTypeSettlement, ic.Block.Index) { - panic(ErrNoAgreement) - } - - sender := ic.VM.GetCallingScriptHash() - - // Create settlement request - settlementID := p.incrementCounterInternal(ic.DAO, makePonsSettlementCounterKey()) - sr := &state.SettlementRequest{ - ID: settlementID, - FromChain: cfg.LocalChainID, - ToChain: toChainID, - Sender: sender, - Receiver: receiver, - Amount: amount, - Status: state.SettlementPending, - Reference: reference, - CreatedAt: ic.Block.Index, - } - p.setSettlementInternal(ic.DAO, sr) - - ic.AddNotification(p.Hash, SettlementRequestedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)), - stackitem.NewBigInteger(big.NewInt(int64(toChainID))), - stackitem.NewByteArray(receiver.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(amount)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)) -} - -func (p *Pons) completeSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item { - settlementID := toBigInt(args[0]).Uint64() - txHashBytes, err := args[1].TryBytes() - if err != nil { - panic(err) - } - txHash, err := util.Uint256DecodeBytesBE(txHashBytes) - if err != nil { - panic(err) - } - - if !p.Tutus.CheckCommittee(ic) { - panic("only committee can complete settlements") - } - - sr, exists := p.getSettlementInternal(ic.DAO, settlementID) - if !exists { - panic(ErrSettlementNotFound) - } - - if sr.Status != state.SettlementPending { - panic("settlement already processed") - } - - sr.Status = state.SettlementCompleted - sr.SettledAt = ic.Block.Index - sr.TxHash = txHash - p.setSettlementInternal(ic.DAO, sr) - - ic.AddNotification(p.Hash, SettlementCompletedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)), - stackitem.NewByteArray(txHash.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (p *Pons) cancelSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item { - settlementID := toBigInt(args[0]).Uint64() - - sr, exists := p.getSettlementInternal(ic.DAO, settlementID) - if !exists { - panic(ErrSettlementNotFound) - } - - if sr.Status != state.SettlementPending { - panic("settlement already processed") - } - - // Allow sender or committee to cancel - caller := ic.VM.GetCallingScriptHash() - if !caller.Equals(sr.Sender) && !p.Tutus.CheckCommittee(ic) { - panic("only sender or committee can cancel settlement") - } - - sr.Status = state.SettlementCancelled - p.setSettlementInternal(ic.DAO, sr) - - return stackitem.NewBool(true) -} - -func (p *Pons) getSettlementRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { - settlementID := toBigInt(args[0]).Uint64() - - sr, exists := p.getSettlementInternal(ic.DAO, settlementID) - if !exists { - return stackitem.Null{} - } - - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(sr.ID)), - stackitem.NewBigInteger(big.NewInt(int64(sr.FromChain))), - stackitem.NewBigInteger(big.NewInt(int64(sr.ToChain))), - stackitem.NewByteArray(sr.Sender.BytesBE()), - stackitem.NewByteArray(sr.Receiver.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(sr.Amount)), - stackitem.NewBigInteger(big.NewInt(int64(sr.Status))), - stackitem.NewByteArray(sr.TxHash.BytesBE()), - stackitem.NewByteArray([]byte(sr.Reference)), - stackitem.NewBigInteger(big.NewInt(int64(sr.CreatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(sr.SettledAt))), - }) -} - -func (p *Pons) shareCredential(ic *interop.Context, args []stackitem.Item) stackitem.Item { - targetChainID := uint32(toBigInt(args[0]).Int64()) - credentialType := state.VerificationType(toBigInt(args[1]).Int64()) - credentialID := toBigInt(args[2]).Uint64() - contentHashBytes, err := args[3].TryBytes() - if err != nil { - panic(err) - } - contentHash, err := util.Uint256DecodeBytesBE(contentHashBytes) - if err != nil { - panic(err) - } - validUntil := uint32(toBigInt(args[4]).Int64()) - - cfg := p.getConfigInternal(ic.DAO) - - // Determine agreement type needed - agreementType := state.AgreementTypeEducation - if credentialType == state.VerificationTypeHealth { - agreementType = state.AgreementTypeHealthcare - } - - if !p.hasActiveAgreementInternal(ic.DAO, targetChainID, agreementType, ic.Block.Index) { - panic(ErrNoAgreement) - } - - owner := ic.VM.GetCallingScriptHash() - - // Set default validity if not provided - if validUntil == 0 { - validUntil = ic.Block.Index + cfg.CredentialShareExpiry - } - - // Create credential share - shareID := p.incrementCounterInternal(ic.DAO, makePonsCredentialCounterKey()) - cs := &state.CredentialShare{ - ID: shareID, - SourceChain: cfg.LocalChainID, - TargetChain: targetChainID, - Owner: owner, - CredentialType: credentialType, - CredentialID: credentialID, - ContentHash: contentHash, - ValidUntil: validUntil, - CreatedAt: ic.Block.Index, - IsRevoked: false, - } - p.setCredentialShareInternal(ic.DAO, cs) - - ic.AddNotification(p.Hash, CredentialSharedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)), - stackitem.NewByteArray(owner.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(targetChainID))), - stackitem.NewBigInteger(big.NewInt(int64(credentialType))), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)) -} - -func (p *Pons) revokeCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item { - shareID := toBigInt(args[0]).Uint64() - - cs, exists := p.getCredentialShareInternal(ic.DAO, shareID) - if !exists { - panic(ErrCredentialNotFound) - } - - // Allow owner or committee to revoke - caller := ic.VM.GetCallingScriptHash() - if !caller.Equals(cs.Owner) && !p.Tutus.CheckCommittee(ic) { - panic(ErrNotCredentialOwner) - } - - cs.IsRevoked = true - p.setCredentialShareInternal(ic.DAO, cs) - - ic.AddNotification(p.Hash, CredentialRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)), - stackitem.NewByteArray(cs.Owner.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (p *Pons) getCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item { - shareID := toBigInt(args[0]).Uint64() - - cs, exists := p.getCredentialShareInternal(ic.DAO, shareID) - if !exists { - return stackitem.Null{} - } - - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(cs.ID)), - stackitem.NewBigInteger(big.NewInt(int64(cs.SourceChain))), - stackitem.NewBigInteger(big.NewInt(int64(cs.TargetChain))), - stackitem.NewByteArray(cs.Owner.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(cs.CredentialType))), - stackitem.NewBigInteger(new(big.Int).SetUint64(cs.CredentialID)), - stackitem.NewByteArray(cs.ContentHash.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(cs.ValidUntil))), - stackitem.NewBigInteger(big.NewInt(int64(cs.CreatedAt))), - stackitem.NewBool(cs.IsRevoked), - }) -} - -func (p *Pons) verifyCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item { - shareID := toBigInt(args[0]).Uint64() - - cs, exists := p.getCredentialShareInternal(ic.DAO, shareID) - if !exists { - return stackitem.NewBool(false) - } - - if cs.IsRevoked { - return stackitem.NewBool(false) - } - - if ic.Block.Index > cs.ValidUntil { - return stackitem.NewBool(false) - } - - return stackitem.NewBool(true) -} - -func (p *Pons) getAgreementCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - count := p.getCounterInternal(ic.DAO, makePonsAgreementCounterKey()) - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} - -func (p *Pons) getVerificationCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - count := p.getCounterInternal(ic.DAO, makePonsVerificationCounterKey()) - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} - -func (p *Pons) getSettlementCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - count := p.getCounterInternal(ic.DAO, makePonsSettlementCounterKey()) - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} - -func (p *Pons) getCredentialShareCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - count := p.getCounterInternal(ic.DAO, makePonsCredentialCounterKey()) - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Pons ("bridge" in Latin) represents the Inter-Government Bridge Protocol +// native contract. It manages: +// - Bilateral agreements between sovereign chains +// - Cross-border verification requests +// - International VTS settlement +// - Education and healthcare credential portability +type Pons struct { + interop.ContractMD + Tutus ITutus + Vita IVita + Federation *Federation + RoleRegistry *RoleRegistry + VTS *VTS + Scire *Scire + Salus *Salus +} + +// Storage key prefixes for Pons. +const ( + ponsPrefixConfig byte = 0x01 // -> PonsConfig + ponsPrefixAgreement byte = 0x10 // agreementID -> BilateralAgreement + ponsPrefixAgreementByChain byte = 0x11 // chainID + agreementID -> exists + ponsPrefixAgreementCounter byte = 0x1F // -> next agreementID + ponsPrefixVerification byte = 0x20 // requestID -> VerificationRequest + ponsPrefixVerifBySubject byte = 0x21 // subject + requestID -> exists + ponsPrefixVerifCounter byte = 0x2F // -> next verificationID + ponsPrefixSettlement byte = 0x30 // settlementID -> SettlementRequest + ponsPrefixSettlByChain byte = 0x31 // chainID + settlementID -> exists + ponsPrefixSettlCounter byte = 0x3F // -> next settlementID + ponsPrefixCredential byte = 0x40 // credentialID -> CredentialShare + ponsPrefixCredByOwner byte = 0x41 // owner + credentialID -> exists + ponsPrefixCredCounter byte = 0x4F // -> next credentialID +) + +// Default configuration values. +const ( + defaultLocalChainID uint32 = 1 + defaultVerificationTimeout uint32 = 8640 // ~1 day at 10s blocks + defaultSettlementTimeout uint32 = 86400 // ~10 days + defaultMaxPendingRequests uint64 = 10000 + defaultCredentialShareExpiry uint32 = 315360 // ~1 year +) + +// Event names for Pons. +const ( + AgreementCreatedEvent = "AgreementCreated" + AgreementUpdatedEvent = "AgreementUpdated" + AgreementTerminatedEvent = "AgreementTerminated" + VerificationRequestedEvent = "VerificationRequested" + VerificationRespondedEvent = "VerificationResponded" + SettlementRequestedEvent = "SettlementRequested" + SettlementCompletedEvent = "SettlementCompleted" + CredentialSharedEvent = "CredentialShared" + CredentialRevokedEvent = "CredentialRevoked" +) + +// Various errors for Pons. +var ( + ErrNoAgreement = errors.New("no active agreement with target chain") + ErrAgreementExists = errors.New("agreement already exists") + ErrAgreementNotFound = errors.New("agreement not found") + ErrInvalidAgreementType = errors.New("invalid agreement type") + ErrAgreementNotActive = errors.New("agreement not active") + ErrVerificationNotFound = errors.New("verification request not found") + ErrVerificationExpired = errors.New("verification request expired") + ErrSettlementNotFound = errors.New("settlement request not found") + ErrSettlementExpired = errors.New("settlement request expired") + ErrCredentialNotFound = errors.New("credential share not found") + ErrCredentialExpired = errors.New("credential share expired") + ErrCredentialRevoked = errors.New("credential share revoked") + ErrMaxRequestsReached = errors.New("maximum pending requests reached") + ErrNotCredentialOwner = errors.New("not credential owner") + ErrAgreementTypeNotAllowed = errors.New("agreement type not allowed for this operation") +) + +var _ interop.Contract = (*Pons)(nil) + +// newPons creates a new Pons native contract. +func newPons() *Pons { + p := &Pons{ + ContractMD: *interop.NewContractMD(nativenames.Pons, nativeids.Pons), + } + defer p.BuildHFSpecificMD(p.ActiveIn()) + + // getConfig method + desc := NewDescriptor("getConfig", smartcontract.ArrayType) + md := NewMethodAndPrice(p.getConfig, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // setLocalChainID method (committee only) + desc = NewDescriptor("setLocalChainID", smartcontract.BoolType, + manifest.NewParameter("chainID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.setLocalChainID, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // --- Agreement Management --- + + // createAgreement method (committee only) + desc = NewDescriptor("createAgreement", smartcontract.IntegerType, + manifest.NewParameter("remoteChainID", smartcontract.IntegerType), + manifest.NewParameter("agreementType", smartcontract.IntegerType), + manifest.NewParameter("termsHash", smartcontract.Hash256Type), + manifest.NewParameter("expirationHeight", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.createAgreement, 1<<17, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // updateAgreementStatus method (committee only) + desc = NewDescriptor("updateAgreementStatus", smartcontract.BoolType, + manifest.NewParameter("agreementID", smartcontract.IntegerType), + manifest.NewParameter("newStatus", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.updateAgreementStatus, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // getAgreement method + desc = NewDescriptor("getAgreement", smartcontract.ArrayType, + manifest.NewParameter("agreementID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getAgreement, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // hasActiveAgreement method + desc = NewDescriptor("hasActiveAgreement", smartcontract.BoolType, + manifest.NewParameter("remoteChainID", smartcontract.IntegerType), + manifest.NewParameter("agreementType", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.hasActiveAgreement, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // --- Verification Requests --- + + // requestVerification method + desc = NewDescriptor("requestVerification", smartcontract.IntegerType, + manifest.NewParameter("targetChainID", smartcontract.IntegerType), + manifest.NewParameter("subject", smartcontract.Hash160Type), + manifest.NewParameter("verificationType", smartcontract.IntegerType), + manifest.NewParameter("dataHash", smartcontract.Hash256Type)) + md = NewMethodAndPrice(p.requestVerification, 1<<17, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // respondVerification method (committee only - represents response from other chain) + desc = NewDescriptor("respondVerification", smartcontract.BoolType, + manifest.NewParameter("requestID", smartcontract.IntegerType), + manifest.NewParameter("approved", smartcontract.BoolType), + manifest.NewParameter("responseHash", smartcontract.Hash256Type)) + md = NewMethodAndPrice(p.respondVerification, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // getVerificationRequest method + desc = NewDescriptor("getVerificationRequest", smartcontract.ArrayType, + manifest.NewParameter("requestID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getVerificationRequest, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // --- Settlement Requests --- + + // requestSettlement method + desc = NewDescriptor("requestSettlement", smartcontract.IntegerType, + manifest.NewParameter("toChainID", smartcontract.IntegerType), + manifest.NewParameter("receiver", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("reference", smartcontract.StringType)) + md = NewMethodAndPrice(p.requestSettlement, 1<<17, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // completeSettlement method (committee only - represents confirmation from other chain) + desc = NewDescriptor("completeSettlement", smartcontract.BoolType, + manifest.NewParameter("settlementID", smartcontract.IntegerType), + manifest.NewParameter("txHash", smartcontract.Hash256Type)) + md = NewMethodAndPrice(p.completeSettlement, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // cancelSettlement method (sender or committee) + desc = NewDescriptor("cancelSettlement", smartcontract.BoolType, + manifest.NewParameter("settlementID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.cancelSettlement, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // getSettlementRequest method + desc = NewDescriptor("getSettlementRequest", smartcontract.ArrayType, + manifest.NewParameter("settlementID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getSettlementRequest, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // --- Credential Sharing --- + + // shareCredential method + desc = NewDescriptor("shareCredential", smartcontract.IntegerType, + manifest.NewParameter("targetChainID", smartcontract.IntegerType), + manifest.NewParameter("credentialType", smartcontract.IntegerType), + manifest.NewParameter("credentialID", smartcontract.IntegerType), + manifest.NewParameter("contentHash", smartcontract.Hash256Type), + manifest.NewParameter("validUntil", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.shareCredential, 1<<17, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // revokeCredentialShare method + desc = NewDescriptor("revokeCredentialShare", smartcontract.BoolType, + manifest.NewParameter("shareID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.revokeCredentialShare, 1<<16, callflag.States|callflag.AllowNotify) + p.AddMethod(md, desc) + + // getCredentialShare method + desc = NewDescriptor("getCredentialShare", smartcontract.ArrayType, + manifest.NewParameter("shareID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.getCredentialShare, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // verifyCredentialShare method + desc = NewDescriptor("verifyCredentialShare", smartcontract.BoolType, + manifest.NewParameter("shareID", smartcontract.IntegerType)) + md = NewMethodAndPrice(p.verifyCredentialShare, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // --- Counter Query Methods --- + + // getAgreementCount method + desc = NewDescriptor("getAgreementCount", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getAgreementCount, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // getVerificationCount method + desc = NewDescriptor("getVerificationCount", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getVerificationCount, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // getSettlementCount method + desc = NewDescriptor("getSettlementCount", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getSettlementCount, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // getCredentialShareCount method + desc = NewDescriptor("getCredentialShareCount", smartcontract.IntegerType) + md = NewMethodAndPrice(p.getCredentialShareCount, 1<<15, callflag.ReadStates) + p.AddMethod(md, desc) + + // --- Events --- + + eDesc := NewEventDescriptor(AgreementCreatedEvent, + manifest.NewParameter("agreementID", smartcontract.IntegerType), + manifest.NewParameter("remoteChainID", smartcontract.IntegerType), + manifest.NewParameter("agreementType", smartcontract.IntegerType)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AgreementUpdatedEvent, + manifest.NewParameter("agreementID", smartcontract.IntegerType), + manifest.NewParameter("oldStatus", smartcontract.IntegerType), + manifest.NewParameter("newStatus", smartcontract.IntegerType)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AgreementTerminatedEvent, + manifest.NewParameter("agreementID", smartcontract.IntegerType), + manifest.NewParameter("remoteChainID", smartcontract.IntegerType)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VerificationRequestedEvent, + manifest.NewParameter("requestID", smartcontract.IntegerType), + manifest.NewParameter("targetChainID", smartcontract.IntegerType), + manifest.NewParameter("subject", smartcontract.Hash160Type), + manifest.NewParameter("verificationType", smartcontract.IntegerType)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VerificationRespondedEvent, + manifest.NewParameter("requestID", smartcontract.IntegerType), + manifest.NewParameter("approved", smartcontract.BoolType)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(SettlementRequestedEvent, + manifest.NewParameter("settlementID", smartcontract.IntegerType), + manifest.NewParameter("toChainID", smartcontract.IntegerType), + manifest.NewParameter("receiver", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(SettlementCompletedEvent, + manifest.NewParameter("settlementID", smartcontract.IntegerType), + manifest.NewParameter("txHash", smartcontract.Hash256Type)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(CredentialSharedEvent, + manifest.NewParameter("shareID", smartcontract.IntegerType), + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("targetChainID", smartcontract.IntegerType), + manifest.NewParameter("credentialType", smartcontract.IntegerType)) + p.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(CredentialRevokedEvent, + manifest.NewParameter("shareID", smartcontract.IntegerType), + manifest.NewParameter("owner", smartcontract.Hash160Type)) + p.AddEvent(NewEvent(eDesc)) + + return p +} + +// Metadata returns contract metadata. +func (p *Pons) Metadata() *interop.ContractMD { + return &p.ContractMD +} + +// Initialize initializes Pons contract at the specified hardfork. +func (p *Pons) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != p.ActiveIn() { + return nil + } + + // Initialize default config + cfg := state.PonsConfig{ + LocalChainID: defaultLocalChainID, + VerificationTimeout: defaultVerificationTimeout, + SettlementTimeout: defaultSettlementTimeout, + MaxPendingRequests: defaultMaxPendingRequests, + CredentialShareExpiry: defaultCredentialShareExpiry, + } + p.setConfigInternal(ic.DAO, &cfg) + + return nil +} + +// InitializeCache fills native Pons cache from DAO on node restart. +func (p *Pons) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + return nil +} + +// OnPersist implements the Contract interface. +func (p *Pons) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (p *Pons) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn returns the hardfork this contract activates in (nil = always active). +func (p *Pons) ActiveIn() *config.Hardfork { + return nil +} + +// ============================================================================ +// Storage Key Helpers +// ============================================================================ + +func makePonsConfigKey() []byte { + return []byte{ponsPrefixConfig} +} + +func makePonsAgreementKey(agreementID uint64) []byte { + key := make([]byte, 9) + key[0] = ponsPrefixAgreement + binary.BigEndian.PutUint64(key[1:], agreementID) + return key +} + +func makePonsAgreementByChainKey(chainID uint32, agreementID uint64) []byte { + key := make([]byte, 13) + key[0] = ponsPrefixAgreementByChain + binary.BigEndian.PutUint32(key[1:], chainID) + binary.BigEndian.PutUint64(key[5:], agreementID) + return key +} + +func makePonsAgreementCounterKey() []byte { + return []byte{ponsPrefixAgreementCounter} +} + +func makePonsVerificationKey(requestID uint64) []byte { + key := make([]byte, 9) + key[0] = ponsPrefixVerification + binary.BigEndian.PutUint64(key[1:], requestID) + return key +} + +func makePonsVerifBySubjectKey(subject util.Uint160, requestID uint64) []byte { + key := make([]byte, 1+util.Uint160Size+8) + key[0] = ponsPrefixVerifBySubject + copy(key[1:], subject.BytesBE()) + binary.BigEndian.PutUint64(key[1+util.Uint160Size:], requestID) + return key +} + +func makePonsVerificationCounterKey() []byte { + return []byte{ponsPrefixVerifCounter} +} + +func makePonsSettlementKey(settlementID uint64) []byte { + key := make([]byte, 9) + key[0] = ponsPrefixSettlement + binary.BigEndian.PutUint64(key[1:], settlementID) + return key +} + +func makePonsSettlByChainKey(chainID uint32, settlementID uint64) []byte { + key := make([]byte, 13) + key[0] = ponsPrefixSettlByChain + binary.BigEndian.PutUint32(key[1:], chainID) + binary.BigEndian.PutUint64(key[5:], settlementID) + return key +} + +func makePonsSettlementCounterKey() []byte { + return []byte{ponsPrefixSettlCounter} +} + +func makePonsCredentialKey(credentialID uint64) []byte { + key := make([]byte, 9) + key[0] = ponsPrefixCredential + binary.BigEndian.PutUint64(key[1:], credentialID) + return key +} + +func makePonsCredByOwnerKey(owner util.Uint160, credentialID uint64) []byte { + key := make([]byte, 1+util.Uint160Size+8) + key[0] = ponsPrefixCredByOwner + copy(key[1:], owner.BytesBE()) + binary.BigEndian.PutUint64(key[1+util.Uint160Size:], credentialID) + return key +} + +func makePonsCredentialCounterKey() []byte { + return []byte{ponsPrefixCredCounter} +} + +// ============================================================================ +// Internal Storage Methods +// ============================================================================ + +func (p *Pons) getConfigInternal(d *dao.Simple) *state.PonsConfig { + si := d.GetStorageItem(p.ID, makePonsConfigKey()) + if si == nil { + return &state.PonsConfig{ + LocalChainID: defaultLocalChainID, + VerificationTimeout: defaultVerificationTimeout, + SettlementTimeout: defaultSettlementTimeout, + MaxPendingRequests: defaultMaxPendingRequests, + CredentialShareExpiry: defaultCredentialShareExpiry, + } + } + // Decode config: chainID(4) + verifTimeout(4) + settlTimeout(4) + maxReq(8) + credExpiry(4) = 24 bytes + if len(si) < 24 { + return &state.PonsConfig{ + LocalChainID: defaultLocalChainID, + VerificationTimeout: defaultVerificationTimeout, + SettlementTimeout: defaultSettlementTimeout, + MaxPendingRequests: defaultMaxPendingRequests, + CredentialShareExpiry: defaultCredentialShareExpiry, + } + } + return &state.PonsConfig{ + LocalChainID: binary.BigEndian.Uint32(si[0:4]), + VerificationTimeout: binary.BigEndian.Uint32(si[4:8]), + SettlementTimeout: binary.BigEndian.Uint32(si[8:12]), + MaxPendingRequests: binary.BigEndian.Uint64(si[12:20]), + CredentialShareExpiry: binary.BigEndian.Uint32(si[20:24]), + } +} + +func (p *Pons) setConfigInternal(d *dao.Simple, cfg *state.PonsConfig) { + buf := make([]byte, 24) + binary.BigEndian.PutUint32(buf[0:4], cfg.LocalChainID) + binary.BigEndian.PutUint32(buf[4:8], cfg.VerificationTimeout) + binary.BigEndian.PutUint32(buf[8:12], cfg.SettlementTimeout) + binary.BigEndian.PutUint64(buf[12:20], cfg.MaxPendingRequests) + binary.BigEndian.PutUint32(buf[20:24], cfg.CredentialShareExpiry) + d.PutStorageItem(p.ID, makePonsConfigKey(), buf) +} + +func (p *Pons) getCounterInternal(d *dao.Simple, key []byte) uint64 { + si := d.GetStorageItem(p.ID, key) + if si == nil || len(si) < 8 { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (p *Pons) incrementCounterInternal(d *dao.Simple, key []byte) uint64 { + current := p.getCounterInternal(d, key) + next := current + 1 + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, next) + d.PutStorageItem(p.ID, key, buf) + return next +} + +// Agreement storage format: +// localChainID(4) + remoteChainID(4) + agreementType(1) + status(1) + terms(32) + +// effectiveDate(4) + expirationDate(4) + createdAt(4) + updatedAt(4) = 58 bytes +func (p *Pons) getAgreementInternal(d *dao.Simple, agreementID uint64) (*state.BilateralAgreement, bool) { + si := d.GetStorageItem(p.ID, makePonsAgreementKey(agreementID)) + if si == nil || len(si) < 58 { + return nil, false + } + + var terms util.Uint256 + copy(terms[:], si[10:42]) + + return &state.BilateralAgreement{ + ID: agreementID, + LocalChainID: binary.BigEndian.Uint32(si[0:4]), + RemoteChainID: binary.BigEndian.Uint32(si[4:8]), + AgreementType: state.AgreementType(si[8]), + Status: state.AgreementStatus(si[9]), + Terms: terms, + EffectiveDate: binary.BigEndian.Uint32(si[42:46]), + ExpirationDate: binary.BigEndian.Uint32(si[46:50]), + CreatedAt: binary.BigEndian.Uint32(si[50:54]), + UpdatedAt: binary.BigEndian.Uint32(si[54:58]), + }, true +} + +func (p *Pons) setAgreementInternal(d *dao.Simple, agr *state.BilateralAgreement) { + buf := make([]byte, 58) + binary.BigEndian.PutUint32(buf[0:4], agr.LocalChainID) + binary.BigEndian.PutUint32(buf[4:8], agr.RemoteChainID) + buf[8] = byte(agr.AgreementType) + buf[9] = byte(agr.Status) + copy(buf[10:42], agr.Terms[:]) + binary.BigEndian.PutUint32(buf[42:46], agr.EffectiveDate) + binary.BigEndian.PutUint32(buf[46:50], agr.ExpirationDate) + binary.BigEndian.PutUint32(buf[50:54], agr.CreatedAt) + binary.BigEndian.PutUint32(buf[54:58], agr.UpdatedAt) + d.PutStorageItem(p.ID, makePonsAgreementKey(agr.ID), buf) + // Index by chain + d.PutStorageItem(p.ID, makePonsAgreementByChainKey(agr.RemoteChainID, agr.ID), []byte{1}) +} + +// Verification storage format: +// requestingChain(4) + targetChain(4) + subject(20) + verificationType(1) + dataHash(32) + +// status(1) + responseHash(32) + requester(20) + createdAt(4) + expiresAt(4) + respondedAt(4) = 126 bytes +func (p *Pons) getVerificationInternal(d *dao.Simple, requestID uint64) (*state.VerificationRequest, bool) { + si := d.GetStorageItem(p.ID, makePonsVerificationKey(requestID)) + if si == nil || len(si) < 126 { + return nil, false + } + + var subject util.Uint160 + copy(subject[:], si[8:28]) + var dataHash util.Uint256 + copy(dataHash[:], si[29:61]) + var responseHash util.Uint256 + copy(responseHash[:], si[62:94]) + var requester util.Uint160 + copy(requester[:], si[94:114]) + + return &state.VerificationRequest{ + ID: requestID, + RequestingChain: binary.BigEndian.Uint32(si[0:4]), + TargetChain: binary.BigEndian.Uint32(si[4:8]), + Subject: subject, + VerificationType: state.VerificationType(si[28]), + DataHash: dataHash, + Status: state.VerificationStatus(si[61]), + ResponseHash: responseHash, + Requester: requester, + CreatedAt: binary.BigEndian.Uint32(si[114:118]), + ExpiresAt: binary.BigEndian.Uint32(si[118:122]), + RespondedAt: binary.BigEndian.Uint32(si[122:126]), + }, true +} + +func (p *Pons) setVerificationInternal(d *dao.Simple, vr *state.VerificationRequest) { + buf := make([]byte, 126) + binary.BigEndian.PutUint32(buf[0:4], vr.RequestingChain) + binary.BigEndian.PutUint32(buf[4:8], vr.TargetChain) + copy(buf[8:28], vr.Subject[:]) + buf[28] = byte(vr.VerificationType) + copy(buf[29:61], vr.DataHash[:]) + buf[61] = byte(vr.Status) + copy(buf[62:94], vr.ResponseHash[:]) + copy(buf[94:114], vr.Requester[:]) + binary.BigEndian.PutUint32(buf[114:118], vr.CreatedAt) + binary.BigEndian.PutUint32(buf[118:122], vr.ExpiresAt) + binary.BigEndian.PutUint32(buf[122:126], vr.RespondedAt) + d.PutStorageItem(p.ID, makePonsVerificationKey(vr.ID), buf) + // Index by subject + d.PutStorageItem(p.ID, makePonsVerifBySubjectKey(vr.Subject, vr.ID), []byte{1}) +} + +// Settlement storage format: +// fromChain(4) + toChain(4) + sender(20) + receiver(20) + amount(8) + status(1) + +// createdAt(4) + settledAt(4) + txHash(32) + refLen(2) + reference(var) = 99 + ref bytes +func (p *Pons) getSettlementInternal(d *dao.Simple, settlementID uint64) (*state.SettlementRequest, bool) { + si := d.GetStorageItem(p.ID, makePonsSettlementKey(settlementID)) + if si == nil || len(si) < 99 { + return nil, false + } + + var sender util.Uint160 + copy(sender[:], si[8:28]) + var receiver util.Uint160 + copy(receiver[:], si[28:48]) + var txHash util.Uint256 + copy(txHash[:], si[65:97]) + + refLen := binary.BigEndian.Uint16(si[97:99]) + var reference string + if len(si) >= 99+int(refLen) { + reference = string(si[99 : 99+refLen]) + } + + return &state.SettlementRequest{ + ID: settlementID, + FromChain: binary.BigEndian.Uint32(si[0:4]), + ToChain: binary.BigEndian.Uint32(si[4:8]), + Sender: sender, + Receiver: receiver, + Amount: binary.BigEndian.Uint64(si[48:56]), + Status: state.SettlementStatus(si[56]), + CreatedAt: binary.BigEndian.Uint32(si[57:61]), + SettledAt: binary.BigEndian.Uint32(si[61:65]), + TxHash: txHash, + Reference: reference, + }, true +} + +func (p *Pons) setSettlementInternal(d *dao.Simple, sr *state.SettlementRequest) { + refBytes := []byte(sr.Reference) + buf := make([]byte, 99+len(refBytes)) + binary.BigEndian.PutUint32(buf[0:4], sr.FromChain) + binary.BigEndian.PutUint32(buf[4:8], sr.ToChain) + copy(buf[8:28], sr.Sender[:]) + copy(buf[28:48], sr.Receiver[:]) + binary.BigEndian.PutUint64(buf[48:56], sr.Amount) + buf[56] = byte(sr.Status) + binary.BigEndian.PutUint32(buf[57:61], sr.CreatedAt) + binary.BigEndian.PutUint32(buf[61:65], sr.SettledAt) + copy(buf[65:97], sr.TxHash[:]) + binary.BigEndian.PutUint16(buf[97:99], uint16(len(refBytes))) + copy(buf[99:], refBytes) + d.PutStorageItem(p.ID, makePonsSettlementKey(sr.ID), buf) + // Index by chain + d.PutStorageItem(p.ID, makePonsSettlByChainKey(sr.ToChain, sr.ID), []byte{1}) +} + +// CredentialShare storage format: +// sourceChain(4) + targetChain(4) + owner(20) + credentialType(1) + credentialID(8) + +// contentHash(32) + validUntil(4) + createdAt(4) + isRevoked(1) = 78 bytes +func (p *Pons) getCredentialShareInternal(d *dao.Simple, shareID uint64) (*state.CredentialShare, bool) { + si := d.GetStorageItem(p.ID, makePonsCredentialKey(shareID)) + if si == nil || len(si) < 78 { + return nil, false + } + + var owner util.Uint160 + copy(owner[:], si[8:28]) + var contentHash util.Uint256 + copy(contentHash[:], si[37:69]) + + return &state.CredentialShare{ + ID: shareID, + SourceChain: binary.BigEndian.Uint32(si[0:4]), + TargetChain: binary.BigEndian.Uint32(si[4:8]), + Owner: owner, + CredentialType: state.VerificationType(si[28]), + CredentialID: binary.BigEndian.Uint64(si[29:37]), + ContentHash: contentHash, + ValidUntil: binary.BigEndian.Uint32(si[69:73]), + CreatedAt: binary.BigEndian.Uint32(si[73:77]), + IsRevoked: si[77] != 0, + }, true +} + +func (p *Pons) setCredentialShareInternal(d *dao.Simple, cs *state.CredentialShare) { + buf := make([]byte, 78) + binary.BigEndian.PutUint32(buf[0:4], cs.SourceChain) + binary.BigEndian.PutUint32(buf[4:8], cs.TargetChain) + copy(buf[8:28], cs.Owner[:]) + buf[28] = byte(cs.CredentialType) + binary.BigEndian.PutUint64(buf[29:37], cs.CredentialID) + copy(buf[37:69], cs.ContentHash[:]) + binary.BigEndian.PutUint32(buf[69:73], cs.ValidUntil) + binary.BigEndian.PutUint32(buf[73:77], cs.CreatedAt) + if cs.IsRevoked { + buf[77] = 1 + } + d.PutStorageItem(p.ID, makePonsCredentialKey(cs.ID), buf) + // Index by owner + d.PutStorageItem(p.ID, makePonsCredByOwnerKey(cs.Owner, cs.ID), []byte{1}) +} + +// hasActiveAgreementInternal checks if there's an active agreement with the target chain +// for the specified agreement type. +func (p *Pons) hasActiveAgreementInternal(d *dao.Simple, remoteChainID uint32, agreementType state.AgreementType, blockHeight uint32) bool { + count := p.getCounterInternal(d, makePonsAgreementCounterKey()) + for i := uint64(1); i <= count; i++ { + agr, exists := p.getAgreementInternal(d, i) + if !exists { + continue + } + if agr.RemoteChainID != remoteChainID { + continue + } + if agr.Status != state.AgreementActive { + continue + } + // Check expiration + if agr.ExpirationDate > 0 && agr.ExpirationDate < blockHeight { + continue + } + // Check if agreement type matches or is comprehensive + if agr.AgreementType == agreementType || agr.AgreementType == state.AgreementTypeComprehensive { + return true + } + } + return false +} + +// ============================================================================ +// Contract Methods +// ============================================================================ + +func (p *Pons) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + cfg := p.getConfigInternal(ic.DAO) + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(cfg.LocalChainID))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.VerificationTimeout))), + stackitem.NewBigInteger(big.NewInt(int64(cfg.SettlementTimeout))), + stackitem.NewBigInteger(new(big.Int).SetUint64(cfg.MaxPendingRequests)), + stackitem.NewBigInteger(big.NewInt(int64(cfg.CredentialShareExpiry))), + }) +} + +func (p *Pons) setLocalChainID(ic *interop.Context, args []stackitem.Item) stackitem.Item { + chainID := uint32(toBigInt(args[0]).Int64()) + + if !p.Tutus.CheckCommittee(ic) { + panic("only committee can set chain ID") + } + + cfg := p.getConfigInternal(ic.DAO) + cfg.LocalChainID = chainID + p.setConfigInternal(ic.DAO, cfg) + + return stackitem.NewBool(true) +} + +func (p *Pons) createAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item { + remoteChainID := uint32(toBigInt(args[0]).Int64()) + agreementType := state.AgreementType(toBigInt(args[1]).Int64()) + termsHashBytes, err := args[2].TryBytes() + if err != nil { + panic(err) + } + termsHash, err := util.Uint256DecodeBytesBE(termsHashBytes) + if err != nil { + panic(err) + } + expirationHeight := uint32(toBigInt(args[3]).Int64()) + + if !p.Tutus.CheckCommittee(ic) { + panic("only committee can create agreements") + } + + if agreementType > state.AgreementTypeComprehensive { + panic(ErrInvalidAgreementType) + } + + cfg := p.getConfigInternal(ic.DAO) + + // Create agreement + agreementID := p.incrementCounterInternal(ic.DAO, makePonsAgreementCounterKey()) + agr := &state.BilateralAgreement{ + ID: agreementID, + LocalChainID: cfg.LocalChainID, + RemoteChainID: remoteChainID, + AgreementType: agreementType, + Status: state.AgreementPending, + Terms: termsHash, + EffectiveDate: 0, // Set when activated + ExpirationDate: expirationHeight, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + p.setAgreementInternal(ic.DAO, agr) + + ic.AddNotification(p.Hash, AgreementCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)), + stackitem.NewBigInteger(big.NewInt(int64(remoteChainID))), + stackitem.NewBigInteger(big.NewInt(int64(agreementType))), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)) +} + +func (p *Pons) updateAgreementStatus(ic *interop.Context, args []stackitem.Item) stackitem.Item { + agreementID := toBigInt(args[0]).Uint64() + newStatus := state.AgreementStatus(toBigInt(args[1]).Int64()) + + if !p.Tutus.CheckCommittee(ic) { + panic("only committee can update agreement status") + } + + agr, exists := p.getAgreementInternal(ic.DAO, agreementID) + if !exists { + panic(ErrAgreementNotFound) + } + + oldStatus := agr.Status + agr.Status = newStatus + agr.UpdatedAt = ic.Block.Index + + // Set effective date when activating + if newStatus == state.AgreementActive && oldStatus != state.AgreementActive { + agr.EffectiveDate = ic.Block.Index + } + + p.setAgreementInternal(ic.DAO, agr) + + ic.AddNotification(p.Hash, AgreementUpdatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(agreementID)), + stackitem.NewBigInteger(big.NewInt(int64(oldStatus))), + stackitem.NewBigInteger(big.NewInt(int64(newStatus))), + })) + + return stackitem.NewBool(true) +} + +func (p *Pons) getAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item { + agreementID := toBigInt(args[0]).Uint64() + + agr, exists := p.getAgreementInternal(ic.DAO, agreementID) + if !exists { + return stackitem.Null{} + } + + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(agr.ID)), + stackitem.NewBigInteger(big.NewInt(int64(agr.LocalChainID))), + stackitem.NewBigInteger(big.NewInt(int64(agr.RemoteChainID))), + stackitem.NewBigInteger(big.NewInt(int64(agr.AgreementType))), + stackitem.NewBigInteger(big.NewInt(int64(agr.Status))), + stackitem.NewByteArray(agr.Terms.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(agr.EffectiveDate))), + stackitem.NewBigInteger(big.NewInt(int64(agr.ExpirationDate))), + stackitem.NewBigInteger(big.NewInt(int64(agr.CreatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(agr.UpdatedAt))), + }) +} + +func (p *Pons) hasActiveAgreement(ic *interop.Context, args []stackitem.Item) stackitem.Item { + remoteChainID := uint32(toBigInt(args[0]).Int64()) + agreementType := state.AgreementType(toBigInt(args[1]).Int64()) + + has := p.hasActiveAgreementInternal(ic.DAO, remoteChainID, agreementType, ic.Block.Index) + return stackitem.NewBool(has) +} + +func (p *Pons) requestVerification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + targetChainID := uint32(toBigInt(args[0]).Int64()) + subject := toUint160(args[1]) + verificationType := state.VerificationType(toBigInt(args[2]).Int64()) + dataHashBytes, err := args[3].TryBytes() + if err != nil { + panic(err) + } + dataHash, err := util.Uint256DecodeBytesBE(dataHashBytes) + if err != nil { + panic(err) + } + + cfg := p.getConfigInternal(ic.DAO) + + // Check for active agreement + agreementType := state.AgreementTypeIdentity + switch verificationType { + case state.VerificationTypeCredential, state.VerificationTypeCertificate: + agreementType = state.AgreementTypeEducation + case state.VerificationTypeHealth: + agreementType = state.AgreementTypeHealthcare + } + + if !p.hasActiveAgreementInternal(ic.DAO, targetChainID, agreementType, ic.Block.Index) { + panic(ErrNoAgreement) + } + + // Get requester from caller + requester := ic.VM.GetCallingScriptHash() + + // Create verification request + requestID := p.incrementCounterInternal(ic.DAO, makePonsVerificationCounterKey()) + vr := &state.VerificationRequest{ + ID: requestID, + RequestingChain: cfg.LocalChainID, + TargetChain: targetChainID, + Subject: subject, + VerificationType: verificationType, + DataHash: dataHash, + Status: state.VerificationPending, + Requester: requester, + CreatedAt: ic.Block.Index, + ExpiresAt: ic.Block.Index + cfg.VerificationTimeout, + } + p.setVerificationInternal(ic.DAO, vr) + + ic.AddNotification(p.Hash, VerificationRequestedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)), + stackitem.NewBigInteger(big.NewInt(int64(targetChainID))), + stackitem.NewByteArray(subject.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(verificationType))), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)) +} + +func (p *Pons) respondVerification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestID := toBigInt(args[0]).Uint64() + approved := toBool(args[1]) + responseHashBytes, err := args[2].TryBytes() + if err != nil { + panic(err) + } + responseHash, err := util.Uint256DecodeBytesBE(responseHashBytes) + if err != nil { + panic(err) + } + + if !p.Tutus.CheckCommittee(ic) { + panic("only committee can respond to verification requests") + } + + vr, exists := p.getVerificationInternal(ic.DAO, requestID) + if !exists { + panic(ErrVerificationNotFound) + } + + if vr.Status != state.VerificationPending { + panic("verification request already processed") + } + + if ic.Block.Index > vr.ExpiresAt { + vr.Status = state.VerificationExpired + p.setVerificationInternal(ic.DAO, vr) + panic(ErrVerificationExpired) + } + + if approved { + vr.Status = state.VerificationApproved + } else { + vr.Status = state.VerificationRejected + } + vr.ResponseHash = responseHash + vr.RespondedAt = ic.Block.Index + p.setVerificationInternal(ic.DAO, vr) + + ic.AddNotification(p.Hash, VerificationRespondedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(requestID)), + stackitem.NewBool(approved), + })) + + return stackitem.NewBool(true) +} + +func (p *Pons) getVerificationRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestID := toBigInt(args[0]).Uint64() + + vr, exists := p.getVerificationInternal(ic.DAO, requestID) + if !exists { + return stackitem.Null{} + } + + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(vr.ID)), + stackitem.NewBigInteger(big.NewInt(int64(vr.RequestingChain))), + stackitem.NewBigInteger(big.NewInt(int64(vr.TargetChain))), + stackitem.NewByteArray(vr.Subject.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(vr.VerificationType))), + stackitem.NewByteArray(vr.DataHash.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(vr.Status))), + stackitem.NewByteArray(vr.ResponseHash.BytesBE()), + stackitem.NewByteArray(vr.Requester.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(vr.CreatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(vr.ExpiresAt))), + stackitem.NewBigInteger(big.NewInt(int64(vr.RespondedAt))), + }) +} + +func (p *Pons) requestSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item { + toChainID := uint32(toBigInt(args[0]).Int64()) + receiver := toUint160(args[1]) + amount := toBigInt(args[2]).Uint64() + reference := toString(args[3]) + + cfg := p.getConfigInternal(ic.DAO) + + // Check for settlement agreement + if !p.hasActiveAgreementInternal(ic.DAO, toChainID, state.AgreementTypeSettlement, ic.Block.Index) { + panic(ErrNoAgreement) + } + + sender := ic.VM.GetCallingScriptHash() + + // Create settlement request + settlementID := p.incrementCounterInternal(ic.DAO, makePonsSettlementCounterKey()) + sr := &state.SettlementRequest{ + ID: settlementID, + FromChain: cfg.LocalChainID, + ToChain: toChainID, + Sender: sender, + Receiver: receiver, + Amount: amount, + Status: state.SettlementPending, + Reference: reference, + CreatedAt: ic.Block.Index, + } + p.setSettlementInternal(ic.DAO, sr) + + ic.AddNotification(p.Hash, SettlementRequestedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)), + stackitem.NewBigInteger(big.NewInt(int64(toChainID))), + stackitem.NewByteArray(receiver.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(amount)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)) +} + +func (p *Pons) completeSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item { + settlementID := toBigInt(args[0]).Uint64() + txHashBytes, err := args[1].TryBytes() + if err != nil { + panic(err) + } + txHash, err := util.Uint256DecodeBytesBE(txHashBytes) + if err != nil { + panic(err) + } + + if !p.Tutus.CheckCommittee(ic) { + panic("only committee can complete settlements") + } + + sr, exists := p.getSettlementInternal(ic.DAO, settlementID) + if !exists { + panic(ErrSettlementNotFound) + } + + if sr.Status != state.SettlementPending { + panic("settlement already processed") + } + + sr.Status = state.SettlementCompleted + sr.SettledAt = ic.Block.Index + sr.TxHash = txHash + p.setSettlementInternal(ic.DAO, sr) + + ic.AddNotification(p.Hash, SettlementCompletedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(settlementID)), + stackitem.NewByteArray(txHash.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (p *Pons) cancelSettlement(ic *interop.Context, args []stackitem.Item) stackitem.Item { + settlementID := toBigInt(args[0]).Uint64() + + sr, exists := p.getSettlementInternal(ic.DAO, settlementID) + if !exists { + panic(ErrSettlementNotFound) + } + + if sr.Status != state.SettlementPending { + panic("settlement already processed") + } + + // Allow sender or committee to cancel + caller := ic.VM.GetCallingScriptHash() + if !caller.Equals(sr.Sender) && !p.Tutus.CheckCommittee(ic) { + panic("only sender or committee can cancel settlement") + } + + sr.Status = state.SettlementCancelled + p.setSettlementInternal(ic.DAO, sr) + + return stackitem.NewBool(true) +} + +func (p *Pons) getSettlementRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { + settlementID := toBigInt(args[0]).Uint64() + + sr, exists := p.getSettlementInternal(ic.DAO, settlementID) + if !exists { + return stackitem.Null{} + } + + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(sr.ID)), + stackitem.NewBigInteger(big.NewInt(int64(sr.FromChain))), + stackitem.NewBigInteger(big.NewInt(int64(sr.ToChain))), + stackitem.NewByteArray(sr.Sender.BytesBE()), + stackitem.NewByteArray(sr.Receiver.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(sr.Amount)), + stackitem.NewBigInteger(big.NewInt(int64(sr.Status))), + stackitem.NewByteArray(sr.TxHash.BytesBE()), + stackitem.NewByteArray([]byte(sr.Reference)), + stackitem.NewBigInteger(big.NewInt(int64(sr.CreatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(sr.SettledAt))), + }) +} + +func (p *Pons) shareCredential(ic *interop.Context, args []stackitem.Item) stackitem.Item { + targetChainID := uint32(toBigInt(args[0]).Int64()) + credentialType := state.VerificationType(toBigInt(args[1]).Int64()) + credentialID := toBigInt(args[2]).Uint64() + contentHashBytes, err := args[3].TryBytes() + if err != nil { + panic(err) + } + contentHash, err := util.Uint256DecodeBytesBE(contentHashBytes) + if err != nil { + panic(err) + } + validUntil := uint32(toBigInt(args[4]).Int64()) + + cfg := p.getConfigInternal(ic.DAO) + + // Determine agreement type needed + agreementType := state.AgreementTypeEducation + if credentialType == state.VerificationTypeHealth { + agreementType = state.AgreementTypeHealthcare + } + + if !p.hasActiveAgreementInternal(ic.DAO, targetChainID, agreementType, ic.Block.Index) { + panic(ErrNoAgreement) + } + + owner := ic.VM.GetCallingScriptHash() + + // Set default validity if not provided + if validUntil == 0 { + validUntil = ic.Block.Index + cfg.CredentialShareExpiry + } + + // Create credential share + shareID := p.incrementCounterInternal(ic.DAO, makePonsCredentialCounterKey()) + cs := &state.CredentialShare{ + ID: shareID, + SourceChain: cfg.LocalChainID, + TargetChain: targetChainID, + Owner: owner, + CredentialType: credentialType, + CredentialID: credentialID, + ContentHash: contentHash, + ValidUntil: validUntil, + CreatedAt: ic.Block.Index, + IsRevoked: false, + } + p.setCredentialShareInternal(ic.DAO, cs) + + ic.AddNotification(p.Hash, CredentialSharedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)), + stackitem.NewByteArray(owner.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(targetChainID))), + stackitem.NewBigInteger(big.NewInt(int64(credentialType))), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)) +} + +func (p *Pons) revokeCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item { + shareID := toBigInt(args[0]).Uint64() + + cs, exists := p.getCredentialShareInternal(ic.DAO, shareID) + if !exists { + panic(ErrCredentialNotFound) + } + + // Allow owner or committee to revoke + caller := ic.VM.GetCallingScriptHash() + if !caller.Equals(cs.Owner) && !p.Tutus.CheckCommittee(ic) { + panic(ErrNotCredentialOwner) + } + + cs.IsRevoked = true + p.setCredentialShareInternal(ic.DAO, cs) + + ic.AddNotification(p.Hash, CredentialRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(shareID)), + stackitem.NewByteArray(cs.Owner.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (p *Pons) getCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item { + shareID := toBigInt(args[0]).Uint64() + + cs, exists := p.getCredentialShareInternal(ic.DAO, shareID) + if !exists { + return stackitem.Null{} + } + + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(cs.ID)), + stackitem.NewBigInteger(big.NewInt(int64(cs.SourceChain))), + stackitem.NewBigInteger(big.NewInt(int64(cs.TargetChain))), + stackitem.NewByteArray(cs.Owner.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(cs.CredentialType))), + stackitem.NewBigInteger(new(big.Int).SetUint64(cs.CredentialID)), + stackitem.NewByteArray(cs.ContentHash.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(cs.ValidUntil))), + stackitem.NewBigInteger(big.NewInt(int64(cs.CreatedAt))), + stackitem.NewBool(cs.IsRevoked), + }) +} + +func (p *Pons) verifyCredentialShare(ic *interop.Context, args []stackitem.Item) stackitem.Item { + shareID := toBigInt(args[0]).Uint64() + + cs, exists := p.getCredentialShareInternal(ic.DAO, shareID) + if !exists { + return stackitem.NewBool(false) + } + + if cs.IsRevoked { + return stackitem.NewBool(false) + } + + if ic.Block.Index > cs.ValidUntil { + return stackitem.NewBool(false) + } + + return stackitem.NewBool(true) +} + +func (p *Pons) getAgreementCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + count := p.getCounterInternal(ic.DAO, makePonsAgreementCounterKey()) + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} + +func (p *Pons) getVerificationCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + count := p.getCounterInternal(ic.DAO, makePonsVerificationCounterKey()) + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} + +func (p *Pons) getSettlementCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + count := p.getCounterInternal(ic.DAO, makePonsSettlementCounterKey()) + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} + +func (p *Pons) getCredentialShareCount(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + count := p.getCounterInternal(ic.DAO, makePonsCredentialCounterKey()) + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} diff --git a/pkg/core/native/role_registry.go b/pkg/core/native/role_registry.go index 357ecd7..a08b7cf 100644 --- a/pkg/core/native/role_registry.go +++ b/pkg/core/native/role_registry.go @@ -1,1061 +1,1061 @@ -package native - -import ( - "encoding/binary" - "errors" - "fmt" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// RoleRegistry is a native contract for hierarchical role-based access control. -// It replaces NEO.CheckCommittee() with democratic governance based on Vita. -type RoleRegistry struct { - interop.ContractMD - - // Tutus is used for fallback committee checks when TutusCommittee is not set. - Tutus ITutus - - // tutusCommittee contains initial committee member addresses from config. - tutusCommittee []util.Uint160 -} - -// RoleRegistryCache contains cached role registry data. -type RoleRegistryCache struct { - roleCount uint64 -} - -// Built-in role IDs. -const ( - // RoleCommittee is the Tutus Committee role (designated officials). - RoleCommittee uint64 = 1 - // RoleRegistrar can register new Vitas. - RoleRegistrar uint64 = 2 - // RoleAttestor can attest identity attributes. - RoleAttestor uint64 = 3 - // RoleOperator is a system operator (node management). - RoleOperator uint64 = 4 -) - -// Storage key prefixes for RoleRegistry. -const ( - rrPrefixRole byte = 0x01 // roleID -> Role - rrPrefixRoleName byte = 0x02 // name -> roleID - rrPrefixRoleAssignment byte = 0x03 // tokenID + roleID -> RoleAssignment - rrPrefixTokenRoles byte = 0x04 // tokenID -> []roleID - rrPrefixRoleMembers byte = 0x05 // roleID -> []tokenID - rrPrefixPermission byte = 0x06 // roleID + resource + action -> PermissionGrant - rrPrefixRolePermissions byte = 0x07 // roleID -> []permission keys - rrPrefixCounter byte = 0x10 // "counter" -> next roleID - rrPrefixConfig byte = 0x11 // "config" -> config - rrPrefixAddressAssignment byte = 0x12 // address + roleID -> AddressRoleAssignment - rrPrefixAddressRoles byte = 0x13 // address -> []roleID -) - -// Max lengths for validation. -const ( - maxRoleNameLength = 64 - maxRoleDescriptionLength = 256 - maxResourceLength = 128 - maxActionLength = 64 -) - -// Errors. -var ( - ErrRoleNotFound = errors.New("role not found") - ErrRoleNameExists = errors.New("role name already exists") - ErrRoleNameTooLong = errors.New("role name too long") - ErrRoleDescTooLong = errors.New("role description too long") - ErrRoleNotActive = errors.New("role is not active") - ErrAssignmentNotFound = errors.New("role assignment not found") - ErrAssignmentExists = errors.New("role assignment already exists") - ErrAssignmentExpired = errors.New("role assignment has expired") - ErrPermissionNotFound = errors.New("permission not found") - ErrPermissionExists = errors.New("permission already exists") - ErrResourceTooLong = errors.New("resource identifier too long") - ErrActionTooLong = errors.New("action identifier too long") - ErrInvalidScope = errors.New("invalid scope") - ErrRoleRegistryNotCommittee = errors.New("caller is not a committee member") - ErrBuiltinRole = errors.New("cannot modify built-in role") - ErrInvalidParentRole = errors.New("invalid parent role") - ErrCircularHierarchy = errors.New("circular role hierarchy detected") -) - -// Event names. -const ( - RoleCreatedEvent = "RoleCreated" - RoleDeletedEvent = "RoleDeleted" - RoleGrantedEvent = "RoleGranted" - RoleRevokedEvent = "RoleRevoked" - PermissionAssignedEvent = "PermissionAssigned" - PermissionRemovedEvent = "PermissionRemoved" -) - -var ( - _ interop.Contract = (*RoleRegistry)(nil) - _ dao.NativeContractCache = (*RoleRegistryCache)(nil) -) - -// Copy implements NativeContractCache interface. -func (c *RoleRegistryCache) Copy() dao.NativeContractCache { - return &RoleRegistryCache{roleCount: c.roleCount} -} - -// newRoleRegistry creates a new RoleRegistry contract. -func newRoleRegistry(tutusCommittee []util.Uint160) *RoleRegistry { - r := &RoleRegistry{ - ContractMD: *interop.NewContractMD(nativenames.RoleRegistry, nativeids.RoleRegistry), - tutusCommittee: tutusCommittee, - } - defer r.BuildHFSpecificMD(r.ActiveIn()) - - // Query methods - desc := NewDescriptor("totalRoles", smartcontract.IntegerType) - md := NewMethodAndPrice(r.totalRoles, 1<<15, callflag.ReadStates) - r.AddMethod(md, desc) - - desc = NewDescriptor("getRole", smartcontract.ArrayType, - manifest.NewParameter("roleID", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.getRole, 1<<15, callflag.ReadStates) - r.AddMethod(md, desc) - - desc = NewDescriptor("getRoleByName", smartcontract.ArrayType, - manifest.NewParameter("name", smartcontract.StringType)) - md = NewMethodAndPrice(r.getRoleByName, 1<<15, callflag.ReadStates) - r.AddMethod(md, desc) - - desc = NewDescriptor("hasRole", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("roleID", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.hasRole, 1<<15, callflag.ReadStates) - r.AddMethod(md, desc) - - desc = NewDescriptor("getRolesForAddress", smartcontract.ArrayType, - manifest.NewParameter("address", smartcontract.Hash160Type)) - md = NewMethodAndPrice(r.getRolesForAddress, 1<<15, callflag.ReadStates) - r.AddMethod(md, desc) - - desc = NewDescriptor("getPermissions", smartcontract.ArrayType, - manifest.NewParameter("roleID", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.getPermissions, 1<<15, callflag.ReadStates) - r.AddMethod(md, desc) - - desc = NewDescriptor("hasPermission", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("resource", smartcontract.StringType), - manifest.NewParameter("action", smartcontract.StringType), - manifest.NewParameter("scope", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.hasPermission, 1<<15, callflag.ReadStates) - r.AddMethod(md, desc) - - // Admin methods (committee-only) - desc = NewDescriptor("createRole", smartcontract.IntegerType, - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("description", smartcontract.StringType), - manifest.NewParameter("parentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.createRole, 1<<15, callflag.States|callflag.AllowNotify) - r.AddMethod(md, desc) - - desc = NewDescriptor("deleteRole", smartcontract.BoolType, - manifest.NewParameter("roleID", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.deleteRole, 1<<15, callflag.States|callflag.AllowNotify) - r.AddMethod(md, desc) - - desc = NewDescriptor("grantRole", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("expiresAt", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.grantRole, 1<<15, callflag.States|callflag.AllowNotify) - r.AddMethod(md, desc) - - desc = NewDescriptor("revokeRole", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("roleID", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.revokeRole, 1<<15, callflag.States|callflag.AllowNotify) - r.AddMethod(md, desc) - - desc = NewDescriptor("assignPermission", smartcontract.BoolType, - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("resource", smartcontract.StringType), - manifest.NewParameter("action", smartcontract.StringType), - manifest.NewParameter("scope", smartcontract.IntegerType)) - md = NewMethodAndPrice(r.assignPermission, 1<<15, callflag.States|callflag.AllowNotify) - r.AddMethod(md, desc) - - desc = NewDescriptor("removePermission", smartcontract.BoolType, - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("resource", smartcontract.StringType), - manifest.NewParameter("action", smartcontract.StringType)) - md = NewMethodAndPrice(r.removePermission, 1<<15, callflag.States|callflag.AllowNotify) - r.AddMethod(md, desc) - - // Events - eDesc := NewEventDescriptor(RoleCreatedEvent, - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("parentID", smartcontract.IntegerType), - manifest.NewParameter("createdBy", smartcontract.Hash160Type)) - eMD := NewEvent(eDesc) - r.AddEvent(eMD) - - eDesc = NewEventDescriptor(RoleDeletedEvent, - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("deletedBy", smartcontract.Hash160Type)) - eMD = NewEvent(eDesc) - r.AddEvent(eMD) - - eDesc = NewEventDescriptor(RoleGrantedEvent, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("expiresAt", smartcontract.IntegerType), - manifest.NewParameter("grantedBy", smartcontract.Hash160Type)) - eMD = NewEvent(eDesc) - r.AddEvent(eMD) - - eDesc = NewEventDescriptor(RoleRevokedEvent, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("revokedBy", smartcontract.Hash160Type)) - eMD = NewEvent(eDesc) - r.AddEvent(eMD) - - eDesc = NewEventDescriptor(PermissionAssignedEvent, - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("resource", smartcontract.StringType), - manifest.NewParameter("action", smartcontract.StringType), - manifest.NewParameter("scope", smartcontract.IntegerType)) - eMD = NewEvent(eDesc) - r.AddEvent(eMD) - - eDesc = NewEventDescriptor(PermissionRemovedEvent, - manifest.NewParameter("roleID", smartcontract.IntegerType), - manifest.NewParameter("resource", smartcontract.StringType), - manifest.NewParameter("action", smartcontract.StringType)) - eMD = NewEvent(eDesc) - r.AddEvent(eMD) - - return r -} - -// Initialize initializes RoleRegistry contract at genesis. -func (r *RoleRegistry) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != r.ActiveIn() { - return nil - } - - cache := &RoleRegistryCache{roleCount: 0} - ic.DAO.SetCache(r.ID, cache) - - // Initialize role counter starting at the first custom role ID (after built-ins) - r.putRoleCounter(ic.DAO, 5) // Built-in roles are 1-4, custom roles start at 5 - - // Create built-in roles - builtinRoles := []struct { - id uint64 - name string - description string - }{ - {RoleCommittee, "COMMITTEE", "Tutus Committee member (designated officials)"}, - {RoleRegistrar, "REGISTRAR", "Can register new Vitas"}, - {RoleAttestor, "ATTESTOR", "Can attest identity attributes"}, - {RoleOperator, "OPERATOR", "System operator (node management)"}, - } - - for _, br := range builtinRoles { - role := &state.Role{ - ID: br.id, - Name: br.name, - Description: br.description, - ParentID: 0, - CreatedAt: 0, - CreatedBy: util.Uint160{}, - Active: true, - } - err := r.putRole(ic.DAO, role) - if err != nil { - return fmt.Errorf("failed to create built-in role %s: %w", br.name, err) - } - r.putRoleNameIndex(ic.DAO, br.name, br.id) - cache.roleCount++ - } - - // Grant COMMITTEE role to initial committee members from config - for _, addr := range r.tutusCommittee { - assignment := &state.AddressRoleAssignment{ - Address: addr, - RoleID: RoleCommittee, - GrantedAt: 0, - GrantedBy: util.Uint160{}, - ExpiresAt: 0, // Never expires - Active: true, - } - err := r.putAddressRoleAssignment(ic.DAO, assignment) - if err != nil { - return fmt.Errorf("failed to grant COMMITTEE role to %s: %w", addr.StringLE(), err) - } - } - - return nil -} - -// InitializeCache initializes RoleRegistry cache from DAO. -func (r *RoleRegistry) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cache := &RoleRegistryCache{} - cache.roleCount = r.getRoleCounter(d) - d.SetCache(r.ID, cache) - return nil -} - -// OnPersist implements the Contract interface. -func (r *RoleRegistry) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (r *RoleRegistry) PostPersist(ic *interop.Context) error { - return nil -} - -// Metadata returns contract metadata. -func (r *RoleRegistry) Metadata() *interop.ContractMD { - return &r.ContractMD -} - -// ActiveIn implements the Contract interface. -func (r *RoleRegistry) ActiveIn() *config.Hardfork { - return nil -} - -// CheckCommittee returns true if the caller has the COMMITTEE role. -// This replaces NEO.CheckCommittee() throughout Tutus for democratic governance. -// Falls back to NEO.CheckCommittee() for backwards compatibility with StandbyCommittee. -func (r *RoleRegistry) CheckCommittee(ic *interop.Context) bool { - caller := ic.VM.GetCallingScriptHash() - - // Check if caller has COMMITTEE role in RoleRegistry - if r.HasRoleInternal(ic.DAO, caller, RoleCommittee, ic.Block.Index) { - return true - } - - // Check if caller is in the tutusCommittee config - for _, addr := range r.tutusCommittee { - if addr.Equals(caller) { - return true - } - } - - // Fallback to Tutus.CheckCommittee for backwards compatibility - // This allows StandbyCommittee to work when TutusCommittee is not configured - if r.Tutus != nil && r.Tutus.CheckCommittee(ic) { - return true - } - - return false -} - -// CheckCommitteeWitness checks if the caller has committee signature. -func (r *RoleRegistry) checkCommitteeWitness(ic *interop.Context) error { - if !r.CheckCommittee(ic) { - // Fall back to checking if any TutusCommittee member signed the transaction - for _, addr := range r.tutusCommittee { - if ok, _ := runtime.CheckHashedWitness(ic, addr); ok { - return nil - } - } - // Check addresses that have been granted COMMITTEE role - committeeAddrs := r.getAddressesWithRole(ic.DAO, RoleCommittee) - for _, addr := range committeeAddrs { - if ok, _ := runtime.CheckHashedWitness(ic, addr); ok { - return nil - } - } - // Fallback to Tutus.CheckCommittee for backwards compatibility - // This allows StandbyCommittee to work when TutusCommittee is not configured - if r.Tutus != nil && r.Tutus.CheckCommittee(ic) { - return nil - } - return ErrRoleRegistryNotCommittee - } - return nil -} - -// HasRoleInternal checks if an address has a specific role (including hierarchy). -// A user has a role if they have it directly, OR if they have a child role that inherits from it. -func (r *RoleRegistry) HasRoleInternal(d *dao.Simple, address util.Uint160, roleID uint64, blockHeight uint32) bool { - // Get all roles directly assigned to this address - assignedRoleIDs := r.getAddressRoleIDs(d, address) - - for _, assignedRoleID := range assignedRoleIDs { - // Check if assignment is valid - assignment := r.getAddressRoleAssignment(d, address, assignedRoleID) - if assignment == nil || !assignment.Active { - continue - } - if assignment.ExpiresAt != 0 && assignment.ExpiresAt <= blockHeight { - continue - } - - // Check if this assigned role equals the queried role, or inherits from it - if r.roleInheritsFrom(d, assignedRoleID, roleID) { - return true - } - } - - return false -} - -// roleInheritsFrom checks if roleID equals targetRoleID, or if roleID has targetRoleID as an ancestor. -func (r *RoleRegistry) roleInheritsFrom(d *dao.Simple, roleID, targetRoleID uint64) bool { - // Direct match - if roleID == targetRoleID { - return true - } - - // Traverse up the hierarchy - role := r.getRoleInternal(d, roleID) - if role != nil && role.ParentID != 0 { - return r.roleInheritsFrom(d, role.ParentID, targetRoleID) - } - - return false -} - -// HasPermissionInternal checks if an address has a specific permission via roles. -func (r *RoleRegistry) HasPermissionInternal(d *dao.Simple, address util.Uint160, resource, action string, scope state.Scope, blockHeight uint32) bool { - // Get all roles for this address - roleIDs := r.getAddressRoleIDs(d, address) - - for _, roleID := range roleIDs { - // Check if assignment is valid - assignment := r.getAddressRoleAssignment(d, address, roleID) - if assignment == nil || !assignment.Active { - continue - } - if assignment.ExpiresAt != 0 && assignment.ExpiresAt <= blockHeight { - continue - } - - // Check if role has the permission - if r.roleHasPermission(d, roleID, resource, action, scope) { - return true - } - } - - return false -} - -// roleHasPermission checks if a role (or its parents) has a specific permission. -func (r *RoleRegistry) roleHasPermission(d *dao.Simple, roleID uint64, resource, action string, scope state.Scope) bool { - // COMMITTEE role has all permissions - if roleID == RoleCommittee { - return true - } - - // Check direct permission - perm := r.getPermission(d, roleID, resource, action) - if perm != nil && perm.Scope <= scope { - return true - } - - // Check parent roles - role := r.getRoleInternal(d, roleID) - if role != nil && role.ParentID != 0 { - return r.roleHasPermission(d, role.ParentID, resource, action, scope) - } - - return false -} - -// Storage helpers - -func (r *RoleRegistry) makeRoleKey(roleID uint64) []byte { - key := make([]byte, 9) - key[0] = rrPrefixRole - binary.BigEndian.PutUint64(key[1:], roleID) - return key -} - -func (r *RoleRegistry) makeRoleNameKey(name string) []byte { - key := make([]byte, 1+len(name)) - key[0] = rrPrefixRoleName - copy(key[1:], []byte(name)) - return key -} - -func (r *RoleRegistry) makeAddressRoleKey(address util.Uint160, roleID uint64) []byte { - key := make([]byte, 1+20+8) - key[0] = rrPrefixAddressAssignment - copy(key[1:21], address.BytesBE()) - binary.BigEndian.PutUint64(key[21:], roleID) - return key -} - -func (r *RoleRegistry) makeAddressRolesKey(address util.Uint160) []byte { - key := make([]byte, 1+20) - key[0] = rrPrefixAddressRoles - copy(key[1:], address.BytesBE()) - return key -} - -func (r *RoleRegistry) makePermissionKey(roleID uint64, resource, action string) []byte { - key := make([]byte, 1+8+len(resource)+1+len(action)) - key[0] = rrPrefixPermission - binary.BigEndian.PutUint64(key[1:9], roleID) - copy(key[9:9+len(resource)], resource) - key[9+len(resource)] = 0 // separator - copy(key[10+len(resource):], action) - return key -} - -func (r *RoleRegistry) putRole(d *dao.Simple, role *state.Role) error { - key := r.makeRoleKey(role.ID) - return putConvertibleToDAO(r.ID, d, key, role) -} - -func (r *RoleRegistry) getRoleInternal(d *dao.Simple, roleID uint64) *state.Role { - key := r.makeRoleKey(roleID) - si := d.GetStorageItem(r.ID, key) - if si == nil { - return nil - } - role := new(state.Role) - err := stackitem.DeserializeConvertible(si, role) - if err != nil { - return nil - } - return role -} - -func (r *RoleRegistry) putRoleNameIndex(d *dao.Simple, name string, roleID uint64) { - key := r.makeRoleNameKey(name) - data := make([]byte, 8) - binary.BigEndian.PutUint64(data, roleID) - d.PutStorageItem(r.ID, key, data) -} - -func (r *RoleRegistry) getRoleIDByName(d *dao.Simple, name string) (uint64, bool) { - key := r.makeRoleNameKey(name) - si := d.GetStorageItem(r.ID, key) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint64(si), true -} - -func (r *RoleRegistry) putAddressRoleAssignment(d *dao.Simple, assignment *state.AddressRoleAssignment) error { - key := r.makeAddressRoleKey(assignment.Address, assignment.RoleID) - err := putConvertibleToDAO(r.ID, d, key, assignment) - if err != nil { - return err - } - // Update address roles index - r.addAddressRoleIndex(d, assignment.Address, assignment.RoleID) - return nil -} - -func (r *RoleRegistry) getAddressRoleAssignment(d *dao.Simple, address util.Uint160, roleID uint64) *state.AddressRoleAssignment { - key := r.makeAddressRoleKey(address, roleID) - si := d.GetStorageItem(r.ID, key) - if si == nil { - return nil - } - assignment := new(state.AddressRoleAssignment) - err := stackitem.DeserializeConvertible(si, assignment) - if err != nil { - return nil - } - return assignment -} - -func (r *RoleRegistry) deleteAddressRoleAssignment(d *dao.Simple, address util.Uint160, roleID uint64) { - key := r.makeAddressRoleKey(address, roleID) - d.DeleteStorageItem(r.ID, key) - r.removeAddressRoleIndex(d, address, roleID) -} - -func (r *RoleRegistry) addAddressRoleIndex(d *dao.Simple, address util.Uint160, roleID uint64) { - key := r.makeAddressRolesKey(address) - si := d.GetStorageItem(r.ID, key) - var roleIDs []uint64 - if si != nil { - roleIDs = r.decodeRoleIDs(si) - } - // Check if already exists - for _, id := range roleIDs { - if id == roleID { - return - } - } - roleIDs = append(roleIDs, roleID) - d.PutStorageItem(r.ID, key, r.encodeRoleIDs(roleIDs)) -} - -func (r *RoleRegistry) removeAddressRoleIndex(d *dao.Simple, address util.Uint160, roleID uint64) { - key := r.makeAddressRolesKey(address) - si := d.GetStorageItem(r.ID, key) - if si == nil { - return - } - roleIDs := r.decodeRoleIDs(si) - newIDs := make([]uint64, 0, len(roleIDs)) - for _, id := range roleIDs { - if id != roleID { - newIDs = append(newIDs, id) - } - } - if len(newIDs) == 0 { - d.DeleteStorageItem(r.ID, key) - } else { - d.PutStorageItem(r.ID, key, r.encodeRoleIDs(newIDs)) - } -} - -func (r *RoleRegistry) getAddressRoleIDs(d *dao.Simple, address util.Uint160) []uint64 { - key := r.makeAddressRolesKey(address) - si := d.GetStorageItem(r.ID, key) - if si == nil { - return nil - } - return r.decodeRoleIDs(si) -} - -func (r *RoleRegistry) getAddressesWithRole(d *dao.Simple, roleID uint64) []util.Uint160 { - var addresses []util.Uint160 - prefix := []byte{rrPrefixAddressAssignment} - d.Seek(r.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 28 { - storedRoleID := binary.BigEndian.Uint64(k[20:28]) - if storedRoleID == roleID { - addr, err := util.Uint160DecodeBytesBE(k[:20]) - if err == nil { - assignment := new(state.AddressRoleAssignment) - if err := stackitem.DeserializeConvertible(v, assignment); err == nil && assignment.Active { - addresses = append(addresses, addr) - } - } - } - } - return true - }) - return addresses -} - -func (r *RoleRegistry) encodeRoleIDs(ids []uint64) []byte { - data := make([]byte, 8*len(ids)) - for i, id := range ids { - binary.BigEndian.PutUint64(data[i*8:], id) - } - return data -} - -func (r *RoleRegistry) decodeRoleIDs(data []byte) []uint64 { - count := len(data) / 8 - ids := make([]uint64, count) - for i := 0; i < count; i++ { - ids[i] = binary.BigEndian.Uint64(data[i*8:]) - } - return ids -} - -func (r *RoleRegistry) putPermission(d *dao.Simple, perm *state.PermissionGrant) error { - key := r.makePermissionKey(perm.RoleID, perm.Resource, perm.Action) - return putConvertibleToDAO(r.ID, d, key, perm) -} - -func (r *RoleRegistry) getPermission(d *dao.Simple, roleID uint64, resource, action string) *state.PermissionGrant { - key := r.makePermissionKey(roleID, resource, action) - si := d.GetStorageItem(r.ID, key) - if si == nil { - return nil - } - perm := new(state.PermissionGrant) - err := stackitem.DeserializeConvertible(si, perm) - if err != nil { - return nil - } - return perm -} - -func (r *RoleRegistry) deletePermission(d *dao.Simple, roleID uint64, resource, action string) { - key := r.makePermissionKey(roleID, resource, action) - d.DeleteStorageItem(r.ID, key) -} - -func (r *RoleRegistry) getRoleCounter(d *dao.Simple) uint64 { - key := []byte{rrPrefixCounter} - si := d.GetStorageItem(r.ID, key) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (r *RoleRegistry) putRoleCounter(d *dao.Simple, count uint64) { - key := []byte{rrPrefixCounter} - data := make([]byte, 8) - binary.BigEndian.PutUint64(data, count) - d.PutStorageItem(r.ID, key, data) -} - -func (r *RoleRegistry) nextRoleID(d *dao.Simple, cache *RoleRegistryCache) uint64 { - id := r.getRoleCounter(d) - r.putRoleCounter(d, id+1) - cache.roleCount++ - return id -} - -// Contract methods - -func (r *RoleRegistry) totalRoles(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(r.ID).(*RoleRegistryCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.roleCount))) -} - -func (r *RoleRegistry) getRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { - roleID := toBigInt(args[0]).Uint64() - role := r.getRoleInternal(ic.DAO, roleID) - if role == nil { - return stackitem.Null{} - } - item, _ := role.ToStackItem() - return item -} - -func (r *RoleRegistry) getRoleByName(ic *interop.Context, args []stackitem.Item) stackitem.Item { - name := toString(args[0]) - roleID, found := r.getRoleIDByName(ic.DAO, name) - if !found { - return stackitem.Null{} - } - role := r.getRoleInternal(ic.DAO, roleID) - if role == nil { - return stackitem.Null{} - } - item, _ := role.ToStackItem() - return item -} - -func (r *RoleRegistry) hasRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - roleID := toBigInt(args[1]).Uint64() - return stackitem.NewBool(r.HasRoleInternal(ic.DAO, address, roleID, ic.Block.Index)) -} - -func (r *RoleRegistry) getRolesForAddress(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - roleIDs := r.getAddressRoleIDs(ic.DAO, address) - items := make([]stackitem.Item, len(roleIDs)) - for i, id := range roleIDs { - items[i] = stackitem.NewBigInteger(big.NewInt(int64(id))) - } - return stackitem.NewArray(items) -} - -func (r *RoleRegistry) getPermissions(ic *interop.Context, args []stackitem.Item) stackitem.Item { - roleID := toBigInt(args[0]).Uint64() - var perms []stackitem.Item - prefix := make([]byte, 9) - prefix[0] = rrPrefixPermission - binary.BigEndian.PutUint64(prefix[1:], roleID) - ic.DAO.Seek(r.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - perm := new(state.PermissionGrant) - if err := stackitem.DeserializeConvertible(v, perm); err == nil { - item, _ := perm.ToStackItem() - perms = append(perms, item) - } - return true - }) - return stackitem.NewArray(perms) -} - -func (r *RoleRegistry) hasPermission(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - resource := toString(args[1]) - action := toString(args[2]) - scopeInt := toBigInt(args[3]).Int64() - scope := state.Scope(scopeInt) - return stackitem.NewBool(r.HasPermissionInternal(ic.DAO, address, resource, action, scope, ic.Block.Index)) -} - -func (r *RoleRegistry) createRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if err := r.checkCommitteeWitness(ic); err != nil { - panic(err) - } - - name := toString(args[0]) - description := toString(args[1]) - parentID := toBigInt(args[2]).Uint64() - - // Validation - if len(name) > maxRoleNameLength { - panic(ErrRoleNameTooLong) - } - if len(description) > maxRoleDescriptionLength { - panic(ErrRoleDescTooLong) - } - if _, exists := r.getRoleIDByName(ic.DAO, name); exists { - panic(ErrRoleNameExists) - } - if parentID != 0 { - parent := r.getRoleInternal(ic.DAO, parentID) - if parent == nil { - panic(ErrInvalidParentRole) - } - if !parent.Active { - panic(ErrRoleNotActive) - } - } - - cache := ic.DAO.GetRWCache(r.ID).(*RoleRegistryCache) - roleID := r.nextRoleID(ic.DAO, cache) - - caller := ic.VM.GetCallingScriptHash() - role := &state.Role{ - ID: roleID, - Name: name, - Description: description, - ParentID: parentID, - CreatedAt: ic.Block.Index, - CreatedBy: caller, - Active: true, - } - - if err := r.putRole(ic.DAO, role); err != nil { - panic(err) - } - r.putRoleNameIndex(ic.DAO, name, roleID) - - err := ic.AddNotification(r.Hash, RoleCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(roleID))), - stackitem.NewByteArray([]byte(name)), - stackitem.NewBigInteger(big.NewInt(int64(parentID))), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBigInteger(big.NewInt(int64(roleID))) -} - -func (r *RoleRegistry) deleteRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if err := r.checkCommitteeWitness(ic); err != nil { - panic(err) - } - - roleID := toBigInt(args[0]).Uint64() - - // Cannot delete built-in roles - if roleID <= RoleOperator { - panic(ErrBuiltinRole) - } - - role := r.getRoleInternal(ic.DAO, roleID) - if role == nil { - panic(ErrRoleNotFound) - } - - role.Active = false - if err := r.putRole(ic.DAO, role); err != nil { - panic(err) - } - - caller := ic.VM.GetCallingScriptHash() - err := ic.AddNotification(r.Hash, RoleDeletedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(roleID))), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (r *RoleRegistry) grantRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if err := r.checkCommitteeWitness(ic); err != nil { - panic(err) - } - - address := toUint160(args[0]) - roleID := toBigInt(args[1]).Uint64() - expiresAt := uint32(toBigInt(args[2]).Uint64()) - - // Verify role exists and is active - role := r.getRoleInternal(ic.DAO, roleID) - if role == nil { - panic(ErrRoleNotFound) - } - if !role.Active { - panic(ErrRoleNotActive) - } - - // Check if already assigned - existing := r.getAddressRoleAssignment(ic.DAO, address, roleID) - if existing != nil && existing.Active { - panic(ErrAssignmentExists) - } - - caller := ic.VM.GetCallingScriptHash() - assignment := &state.AddressRoleAssignment{ - Address: address, - RoleID: roleID, - GrantedAt: ic.Block.Index, - GrantedBy: caller, - ExpiresAt: expiresAt, - Active: true, - } - - if err := r.putAddressRoleAssignment(ic.DAO, assignment); err != nil { - panic(err) - } - - err := ic.AddNotification(r.Hash, RoleGrantedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(address.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(roleID))), - stackitem.NewBigInteger(big.NewInt(int64(expiresAt))), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (r *RoleRegistry) revokeRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if err := r.checkCommitteeWitness(ic); err != nil { - panic(err) - } - - address := toUint160(args[0]) - roleID := toBigInt(args[1]).Uint64() - - assignment := r.getAddressRoleAssignment(ic.DAO, address, roleID) - if assignment == nil || !assignment.Active { - panic(ErrAssignmentNotFound) - } - - assignment.Active = false - if err := r.putAddressRoleAssignment(ic.DAO, assignment); err != nil { - panic(err) - } - - caller := ic.VM.GetCallingScriptHash() - err := ic.AddNotification(r.Hash, RoleRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(address.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(roleID))), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (r *RoleRegistry) assignPermission(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if err := r.checkCommitteeWitness(ic); err != nil { - panic(err) - } - - roleID := toBigInt(args[0]).Uint64() - resource := toString(args[1]) - action := toString(args[2]) - scopeInt := toBigInt(args[3]).Int64() - scope := state.Scope(scopeInt) - - // Validation - if len(resource) > maxResourceLength { - panic(ErrResourceTooLong) - } - if len(action) > maxActionLength { - panic(ErrActionTooLong) - } - if scope > state.ScopeDelegated { - panic(ErrInvalidScope) - } - - // Verify role exists and is active - role := r.getRoleInternal(ic.DAO, roleID) - if role == nil { - panic(ErrRoleNotFound) - } - if !role.Active { - panic(ErrRoleNotActive) - } - - // Check if already exists - existing := r.getPermission(ic.DAO, roleID, resource, action) - if existing != nil { - panic(ErrPermissionExists) - } - - caller := ic.VM.GetCallingScriptHash() - perm := &state.PermissionGrant{ - RoleID: roleID, - Resource: resource, - Action: action, - Scope: scope, - GrantedAt: ic.Block.Index, - GrantedBy: caller, - } - - if err := r.putPermission(ic.DAO, perm); err != nil { - panic(err) - } - - err := ic.AddNotification(r.Hash, PermissionAssignedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(roleID))), - stackitem.NewByteArray([]byte(resource)), - stackitem.NewByteArray([]byte(action)), - stackitem.NewBigInteger(big.NewInt(int64(scope))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -func (r *RoleRegistry) removePermission(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if err := r.checkCommitteeWitness(ic); err != nil { - panic(err) - } - - roleID := toBigInt(args[0]).Uint64() - resource := toString(args[1]) - action := toString(args[2]) - - // Verify permission exists - existing := r.getPermission(ic.DAO, roleID, resource, action) - if existing == nil { - panic(ErrPermissionNotFound) - } - - r.deletePermission(ic.DAO, roleID, resource, action) - - err := ic.AddNotification(r.Hash, PermissionRemovedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(roleID))), - stackitem.NewByteArray([]byte(resource)), - stackitem.NewByteArray([]byte(action)), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} +package native + +import ( + "encoding/binary" + "errors" + "fmt" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// RoleRegistry is a native contract for hierarchical role-based access control. +// It replaces NEO.CheckCommittee() with democratic governance based on Vita. +type RoleRegistry struct { + interop.ContractMD + + // Tutus is used for fallback committee checks when TutusCommittee is not set. + Tutus ITutus + + // tutusCommittee contains initial committee member addresses from config. + tutusCommittee []util.Uint160 +} + +// RoleRegistryCache contains cached role registry data. +type RoleRegistryCache struct { + roleCount uint64 +} + +// Built-in role IDs. +const ( + // RoleCommittee is the Tutus Committee role (designated officials). + RoleCommittee uint64 = 1 + // RoleRegistrar can register new Vitas. + RoleRegistrar uint64 = 2 + // RoleAttestor can attest identity attributes. + RoleAttestor uint64 = 3 + // RoleOperator is a system operator (node management). + RoleOperator uint64 = 4 +) + +// Storage key prefixes for RoleRegistry. +const ( + rrPrefixRole byte = 0x01 // roleID -> Role + rrPrefixRoleName byte = 0x02 // name -> roleID + rrPrefixRoleAssignment byte = 0x03 // tokenID + roleID -> RoleAssignment + rrPrefixTokenRoles byte = 0x04 // tokenID -> []roleID + rrPrefixRoleMembers byte = 0x05 // roleID -> []tokenID + rrPrefixPermission byte = 0x06 // roleID + resource + action -> PermissionGrant + rrPrefixRolePermissions byte = 0x07 // roleID -> []permission keys + rrPrefixCounter byte = 0x10 // "counter" -> next roleID + rrPrefixConfig byte = 0x11 // "config" -> config + rrPrefixAddressAssignment byte = 0x12 // address + roleID -> AddressRoleAssignment + rrPrefixAddressRoles byte = 0x13 // address -> []roleID +) + +// Max lengths for validation. +const ( + maxRoleNameLength = 64 + maxRoleDescriptionLength = 256 + maxResourceLength = 128 + maxActionLength = 64 +) + +// Errors. +var ( + ErrRoleNotFound = errors.New("role not found") + ErrRoleNameExists = errors.New("role name already exists") + ErrRoleNameTooLong = errors.New("role name too long") + ErrRoleDescTooLong = errors.New("role description too long") + ErrRoleNotActive = errors.New("role is not active") + ErrAssignmentNotFound = errors.New("role assignment not found") + ErrAssignmentExists = errors.New("role assignment already exists") + ErrAssignmentExpired = errors.New("role assignment has expired") + ErrPermissionNotFound = errors.New("permission not found") + ErrPermissionExists = errors.New("permission already exists") + ErrResourceTooLong = errors.New("resource identifier too long") + ErrActionTooLong = errors.New("action identifier too long") + ErrInvalidScope = errors.New("invalid scope") + ErrRoleRegistryNotCommittee = errors.New("caller is not a committee member") + ErrBuiltinRole = errors.New("cannot modify built-in role") + ErrInvalidParentRole = errors.New("invalid parent role") + ErrCircularHierarchy = errors.New("circular role hierarchy detected") +) + +// Event names. +const ( + RoleCreatedEvent = "RoleCreated" + RoleDeletedEvent = "RoleDeleted" + RoleGrantedEvent = "RoleGranted" + RoleRevokedEvent = "RoleRevoked" + PermissionAssignedEvent = "PermissionAssigned" + PermissionRemovedEvent = "PermissionRemoved" +) + +var ( + _ interop.Contract = (*RoleRegistry)(nil) + _ dao.NativeContractCache = (*RoleRegistryCache)(nil) +) + +// Copy implements NativeContractCache interface. +func (c *RoleRegistryCache) Copy() dao.NativeContractCache { + return &RoleRegistryCache{roleCount: c.roleCount} +} + +// newRoleRegistry creates a new RoleRegistry contract. +func newRoleRegistry(tutusCommittee []util.Uint160) *RoleRegistry { + r := &RoleRegistry{ + ContractMD: *interop.NewContractMD(nativenames.RoleRegistry, nativeids.RoleRegistry), + tutusCommittee: tutusCommittee, + } + defer r.BuildHFSpecificMD(r.ActiveIn()) + + // Query methods + desc := NewDescriptor("totalRoles", smartcontract.IntegerType) + md := NewMethodAndPrice(r.totalRoles, 1<<15, callflag.ReadStates) + r.AddMethod(md, desc) + + desc = NewDescriptor("getRole", smartcontract.ArrayType, + manifest.NewParameter("roleID", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.getRole, 1<<15, callflag.ReadStates) + r.AddMethod(md, desc) + + desc = NewDescriptor("getRoleByName", smartcontract.ArrayType, + manifest.NewParameter("name", smartcontract.StringType)) + md = NewMethodAndPrice(r.getRoleByName, 1<<15, callflag.ReadStates) + r.AddMethod(md, desc) + + desc = NewDescriptor("hasRole", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("roleID", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.hasRole, 1<<15, callflag.ReadStates) + r.AddMethod(md, desc) + + desc = NewDescriptor("getRolesForAddress", smartcontract.ArrayType, + manifest.NewParameter("address", smartcontract.Hash160Type)) + md = NewMethodAndPrice(r.getRolesForAddress, 1<<15, callflag.ReadStates) + r.AddMethod(md, desc) + + desc = NewDescriptor("getPermissions", smartcontract.ArrayType, + manifest.NewParameter("roleID", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.getPermissions, 1<<15, callflag.ReadStates) + r.AddMethod(md, desc) + + desc = NewDescriptor("hasPermission", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("resource", smartcontract.StringType), + manifest.NewParameter("action", smartcontract.StringType), + manifest.NewParameter("scope", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.hasPermission, 1<<15, callflag.ReadStates) + r.AddMethod(md, desc) + + // Admin methods (committee-only) + desc = NewDescriptor("createRole", smartcontract.IntegerType, + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("description", smartcontract.StringType), + manifest.NewParameter("parentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.createRole, 1<<15, callflag.States|callflag.AllowNotify) + r.AddMethod(md, desc) + + desc = NewDescriptor("deleteRole", smartcontract.BoolType, + manifest.NewParameter("roleID", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.deleteRole, 1<<15, callflag.States|callflag.AllowNotify) + r.AddMethod(md, desc) + + desc = NewDescriptor("grantRole", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("expiresAt", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.grantRole, 1<<15, callflag.States|callflag.AllowNotify) + r.AddMethod(md, desc) + + desc = NewDescriptor("revokeRole", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("roleID", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.revokeRole, 1<<15, callflag.States|callflag.AllowNotify) + r.AddMethod(md, desc) + + desc = NewDescriptor("assignPermission", smartcontract.BoolType, + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("resource", smartcontract.StringType), + manifest.NewParameter("action", smartcontract.StringType), + manifest.NewParameter("scope", smartcontract.IntegerType)) + md = NewMethodAndPrice(r.assignPermission, 1<<15, callflag.States|callflag.AllowNotify) + r.AddMethod(md, desc) + + desc = NewDescriptor("removePermission", smartcontract.BoolType, + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("resource", smartcontract.StringType), + manifest.NewParameter("action", smartcontract.StringType)) + md = NewMethodAndPrice(r.removePermission, 1<<15, callflag.States|callflag.AllowNotify) + r.AddMethod(md, desc) + + // Events + eDesc := NewEventDescriptor(RoleCreatedEvent, + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("parentID", smartcontract.IntegerType), + manifest.NewParameter("createdBy", smartcontract.Hash160Type)) + eMD := NewEvent(eDesc) + r.AddEvent(eMD) + + eDesc = NewEventDescriptor(RoleDeletedEvent, + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("deletedBy", smartcontract.Hash160Type)) + eMD = NewEvent(eDesc) + r.AddEvent(eMD) + + eDesc = NewEventDescriptor(RoleGrantedEvent, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("expiresAt", smartcontract.IntegerType), + manifest.NewParameter("grantedBy", smartcontract.Hash160Type)) + eMD = NewEvent(eDesc) + r.AddEvent(eMD) + + eDesc = NewEventDescriptor(RoleRevokedEvent, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("revokedBy", smartcontract.Hash160Type)) + eMD = NewEvent(eDesc) + r.AddEvent(eMD) + + eDesc = NewEventDescriptor(PermissionAssignedEvent, + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("resource", smartcontract.StringType), + manifest.NewParameter("action", smartcontract.StringType), + manifest.NewParameter("scope", smartcontract.IntegerType)) + eMD = NewEvent(eDesc) + r.AddEvent(eMD) + + eDesc = NewEventDescriptor(PermissionRemovedEvent, + manifest.NewParameter("roleID", smartcontract.IntegerType), + manifest.NewParameter("resource", smartcontract.StringType), + manifest.NewParameter("action", smartcontract.StringType)) + eMD = NewEvent(eDesc) + r.AddEvent(eMD) + + return r +} + +// Initialize initializes RoleRegistry contract at genesis. +func (r *RoleRegistry) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != r.ActiveIn() { + return nil + } + + cache := &RoleRegistryCache{roleCount: 0} + ic.DAO.SetCache(r.ID, cache) + + // Initialize role counter starting at the first custom role ID (after built-ins) + r.putRoleCounter(ic.DAO, 5) // Built-in roles are 1-4, custom roles start at 5 + + // Create built-in roles + builtinRoles := []struct { + id uint64 + name string + description string + }{ + {RoleCommittee, "COMMITTEE", "Tutus Committee member (designated officials)"}, + {RoleRegistrar, "REGISTRAR", "Can register new Vitas"}, + {RoleAttestor, "ATTESTOR", "Can attest identity attributes"}, + {RoleOperator, "OPERATOR", "System operator (node management)"}, + } + + for _, br := range builtinRoles { + role := &state.Role{ + ID: br.id, + Name: br.name, + Description: br.description, + ParentID: 0, + CreatedAt: 0, + CreatedBy: util.Uint160{}, + Active: true, + } + err := r.putRole(ic.DAO, role) + if err != nil { + return fmt.Errorf("failed to create built-in role %s: %w", br.name, err) + } + r.putRoleNameIndex(ic.DAO, br.name, br.id) + cache.roleCount++ + } + + // Grant COMMITTEE role to initial committee members from config + for _, addr := range r.tutusCommittee { + assignment := &state.AddressRoleAssignment{ + Address: addr, + RoleID: RoleCommittee, + GrantedAt: 0, + GrantedBy: util.Uint160{}, + ExpiresAt: 0, // Never expires + Active: true, + } + err := r.putAddressRoleAssignment(ic.DAO, assignment) + if err != nil { + return fmt.Errorf("failed to grant COMMITTEE role to %s: %w", addr.StringLE(), err) + } + } + + return nil +} + +// InitializeCache initializes RoleRegistry cache from DAO. +func (r *RoleRegistry) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cache := &RoleRegistryCache{} + cache.roleCount = r.getRoleCounter(d) + d.SetCache(r.ID, cache) + return nil +} + +// OnPersist implements the Contract interface. +func (r *RoleRegistry) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (r *RoleRegistry) PostPersist(ic *interop.Context) error { + return nil +} + +// Metadata returns contract metadata. +func (r *RoleRegistry) Metadata() *interop.ContractMD { + return &r.ContractMD +} + +// ActiveIn implements the Contract interface. +func (r *RoleRegistry) ActiveIn() *config.Hardfork { + return nil +} + +// CheckCommittee returns true if the caller has the COMMITTEE role. +// This replaces NEO.CheckCommittee() throughout Tutus for democratic governance. +// Falls back to NEO.CheckCommittee() for backwards compatibility with StandbyCommittee. +func (r *RoleRegistry) CheckCommittee(ic *interop.Context) bool { + caller := ic.VM.GetCallingScriptHash() + + // Check if caller has COMMITTEE role in RoleRegistry + if r.HasRoleInternal(ic.DAO, caller, RoleCommittee, ic.Block.Index) { + return true + } + + // Check if caller is in the tutusCommittee config + for _, addr := range r.tutusCommittee { + if addr.Equals(caller) { + return true + } + } + + // Fallback to Tutus.CheckCommittee for backwards compatibility + // This allows StandbyCommittee to work when TutusCommittee is not configured + if r.Tutus != nil && r.Tutus.CheckCommittee(ic) { + return true + } + + return false +} + +// CheckCommitteeWitness checks if the caller has committee signature. +func (r *RoleRegistry) checkCommitteeWitness(ic *interop.Context) error { + if !r.CheckCommittee(ic) { + // Fall back to checking if any TutusCommittee member signed the transaction + for _, addr := range r.tutusCommittee { + if ok, _ := runtime.CheckHashedWitness(ic, addr); ok { + return nil + } + } + // Check addresses that have been granted COMMITTEE role + committeeAddrs := r.getAddressesWithRole(ic.DAO, RoleCommittee) + for _, addr := range committeeAddrs { + if ok, _ := runtime.CheckHashedWitness(ic, addr); ok { + return nil + } + } + // Fallback to Tutus.CheckCommittee for backwards compatibility + // This allows StandbyCommittee to work when TutusCommittee is not configured + if r.Tutus != nil && r.Tutus.CheckCommittee(ic) { + return nil + } + return ErrRoleRegistryNotCommittee + } + return nil +} + +// HasRoleInternal checks if an address has a specific role (including hierarchy). +// A user has a role if they have it directly, OR if they have a child role that inherits from it. +func (r *RoleRegistry) HasRoleInternal(d *dao.Simple, address util.Uint160, roleID uint64, blockHeight uint32) bool { + // Get all roles directly assigned to this address + assignedRoleIDs := r.getAddressRoleIDs(d, address) + + for _, assignedRoleID := range assignedRoleIDs { + // Check if assignment is valid + assignment := r.getAddressRoleAssignment(d, address, assignedRoleID) + if assignment == nil || !assignment.Active { + continue + } + if assignment.ExpiresAt != 0 && assignment.ExpiresAt <= blockHeight { + continue + } + + // Check if this assigned role equals the queried role, or inherits from it + if r.roleInheritsFrom(d, assignedRoleID, roleID) { + return true + } + } + + return false +} + +// roleInheritsFrom checks if roleID equals targetRoleID, or if roleID has targetRoleID as an ancestor. +func (r *RoleRegistry) roleInheritsFrom(d *dao.Simple, roleID, targetRoleID uint64) bool { + // Direct match + if roleID == targetRoleID { + return true + } + + // Traverse up the hierarchy + role := r.getRoleInternal(d, roleID) + if role != nil && role.ParentID != 0 { + return r.roleInheritsFrom(d, role.ParentID, targetRoleID) + } + + return false +} + +// HasPermissionInternal checks if an address has a specific permission via roles. +func (r *RoleRegistry) HasPermissionInternal(d *dao.Simple, address util.Uint160, resource, action string, scope state.Scope, blockHeight uint32) bool { + // Get all roles for this address + roleIDs := r.getAddressRoleIDs(d, address) + + for _, roleID := range roleIDs { + // Check if assignment is valid + assignment := r.getAddressRoleAssignment(d, address, roleID) + if assignment == nil || !assignment.Active { + continue + } + if assignment.ExpiresAt != 0 && assignment.ExpiresAt <= blockHeight { + continue + } + + // Check if role has the permission + if r.roleHasPermission(d, roleID, resource, action, scope) { + return true + } + } + + return false +} + +// roleHasPermission checks if a role (or its parents) has a specific permission. +func (r *RoleRegistry) roleHasPermission(d *dao.Simple, roleID uint64, resource, action string, scope state.Scope) bool { + // COMMITTEE role has all permissions + if roleID == RoleCommittee { + return true + } + + // Check direct permission + perm := r.getPermission(d, roleID, resource, action) + if perm != nil && perm.Scope <= scope { + return true + } + + // Check parent roles + role := r.getRoleInternal(d, roleID) + if role != nil && role.ParentID != 0 { + return r.roleHasPermission(d, role.ParentID, resource, action, scope) + } + + return false +} + +// Storage helpers + +func (r *RoleRegistry) makeRoleKey(roleID uint64) []byte { + key := make([]byte, 9) + key[0] = rrPrefixRole + binary.BigEndian.PutUint64(key[1:], roleID) + return key +} + +func (r *RoleRegistry) makeRoleNameKey(name string) []byte { + key := make([]byte, 1+len(name)) + key[0] = rrPrefixRoleName + copy(key[1:], []byte(name)) + return key +} + +func (r *RoleRegistry) makeAddressRoleKey(address util.Uint160, roleID uint64) []byte { + key := make([]byte, 1+20+8) + key[0] = rrPrefixAddressAssignment + copy(key[1:21], address.BytesBE()) + binary.BigEndian.PutUint64(key[21:], roleID) + return key +} + +func (r *RoleRegistry) makeAddressRolesKey(address util.Uint160) []byte { + key := make([]byte, 1+20) + key[0] = rrPrefixAddressRoles + copy(key[1:], address.BytesBE()) + return key +} + +func (r *RoleRegistry) makePermissionKey(roleID uint64, resource, action string) []byte { + key := make([]byte, 1+8+len(resource)+1+len(action)) + key[0] = rrPrefixPermission + binary.BigEndian.PutUint64(key[1:9], roleID) + copy(key[9:9+len(resource)], resource) + key[9+len(resource)] = 0 // separator + copy(key[10+len(resource):], action) + return key +} + +func (r *RoleRegistry) putRole(d *dao.Simple, role *state.Role) error { + key := r.makeRoleKey(role.ID) + return putConvertibleToDAO(r.ID, d, key, role) +} + +func (r *RoleRegistry) getRoleInternal(d *dao.Simple, roleID uint64) *state.Role { + key := r.makeRoleKey(roleID) + si := d.GetStorageItem(r.ID, key) + if si == nil { + return nil + } + role := new(state.Role) + err := stackitem.DeserializeConvertible(si, role) + if err != nil { + return nil + } + return role +} + +func (r *RoleRegistry) putRoleNameIndex(d *dao.Simple, name string, roleID uint64) { + key := r.makeRoleNameKey(name) + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, roleID) + d.PutStorageItem(r.ID, key, data) +} + +func (r *RoleRegistry) getRoleIDByName(d *dao.Simple, name string) (uint64, bool) { + key := r.makeRoleNameKey(name) + si := d.GetStorageItem(r.ID, key) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint64(si), true +} + +func (r *RoleRegistry) putAddressRoleAssignment(d *dao.Simple, assignment *state.AddressRoleAssignment) error { + key := r.makeAddressRoleKey(assignment.Address, assignment.RoleID) + err := putConvertibleToDAO(r.ID, d, key, assignment) + if err != nil { + return err + } + // Update address roles index + r.addAddressRoleIndex(d, assignment.Address, assignment.RoleID) + return nil +} + +func (r *RoleRegistry) getAddressRoleAssignment(d *dao.Simple, address util.Uint160, roleID uint64) *state.AddressRoleAssignment { + key := r.makeAddressRoleKey(address, roleID) + si := d.GetStorageItem(r.ID, key) + if si == nil { + return nil + } + assignment := new(state.AddressRoleAssignment) + err := stackitem.DeserializeConvertible(si, assignment) + if err != nil { + return nil + } + return assignment +} + +func (r *RoleRegistry) deleteAddressRoleAssignment(d *dao.Simple, address util.Uint160, roleID uint64) { + key := r.makeAddressRoleKey(address, roleID) + d.DeleteStorageItem(r.ID, key) + r.removeAddressRoleIndex(d, address, roleID) +} + +func (r *RoleRegistry) addAddressRoleIndex(d *dao.Simple, address util.Uint160, roleID uint64) { + key := r.makeAddressRolesKey(address) + si := d.GetStorageItem(r.ID, key) + var roleIDs []uint64 + if si != nil { + roleIDs = r.decodeRoleIDs(si) + } + // Check if already exists + for _, id := range roleIDs { + if id == roleID { + return + } + } + roleIDs = append(roleIDs, roleID) + d.PutStorageItem(r.ID, key, r.encodeRoleIDs(roleIDs)) +} + +func (r *RoleRegistry) removeAddressRoleIndex(d *dao.Simple, address util.Uint160, roleID uint64) { + key := r.makeAddressRolesKey(address) + si := d.GetStorageItem(r.ID, key) + if si == nil { + return + } + roleIDs := r.decodeRoleIDs(si) + newIDs := make([]uint64, 0, len(roleIDs)) + for _, id := range roleIDs { + if id != roleID { + newIDs = append(newIDs, id) + } + } + if len(newIDs) == 0 { + d.DeleteStorageItem(r.ID, key) + } else { + d.PutStorageItem(r.ID, key, r.encodeRoleIDs(newIDs)) + } +} + +func (r *RoleRegistry) getAddressRoleIDs(d *dao.Simple, address util.Uint160) []uint64 { + key := r.makeAddressRolesKey(address) + si := d.GetStorageItem(r.ID, key) + if si == nil { + return nil + } + return r.decodeRoleIDs(si) +} + +func (r *RoleRegistry) getAddressesWithRole(d *dao.Simple, roleID uint64) []util.Uint160 { + var addresses []util.Uint160 + prefix := []byte{rrPrefixAddressAssignment} + d.Seek(r.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 28 { + storedRoleID := binary.BigEndian.Uint64(k[20:28]) + if storedRoleID == roleID { + addr, err := util.Uint160DecodeBytesBE(k[:20]) + if err == nil { + assignment := new(state.AddressRoleAssignment) + if err := stackitem.DeserializeConvertible(v, assignment); err == nil && assignment.Active { + addresses = append(addresses, addr) + } + } + } + } + return true + }) + return addresses +} + +func (r *RoleRegistry) encodeRoleIDs(ids []uint64) []byte { + data := make([]byte, 8*len(ids)) + for i, id := range ids { + binary.BigEndian.PutUint64(data[i*8:], id) + } + return data +} + +func (r *RoleRegistry) decodeRoleIDs(data []byte) []uint64 { + count := len(data) / 8 + ids := make([]uint64, count) + for i := 0; i < count; i++ { + ids[i] = binary.BigEndian.Uint64(data[i*8:]) + } + return ids +} + +func (r *RoleRegistry) putPermission(d *dao.Simple, perm *state.PermissionGrant) error { + key := r.makePermissionKey(perm.RoleID, perm.Resource, perm.Action) + return putConvertibleToDAO(r.ID, d, key, perm) +} + +func (r *RoleRegistry) getPermission(d *dao.Simple, roleID uint64, resource, action string) *state.PermissionGrant { + key := r.makePermissionKey(roleID, resource, action) + si := d.GetStorageItem(r.ID, key) + if si == nil { + return nil + } + perm := new(state.PermissionGrant) + err := stackitem.DeserializeConvertible(si, perm) + if err != nil { + return nil + } + return perm +} + +func (r *RoleRegistry) deletePermission(d *dao.Simple, roleID uint64, resource, action string) { + key := r.makePermissionKey(roleID, resource, action) + d.DeleteStorageItem(r.ID, key) +} + +func (r *RoleRegistry) getRoleCounter(d *dao.Simple) uint64 { + key := []byte{rrPrefixCounter} + si := d.GetStorageItem(r.ID, key) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (r *RoleRegistry) putRoleCounter(d *dao.Simple, count uint64) { + key := []byte{rrPrefixCounter} + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, count) + d.PutStorageItem(r.ID, key, data) +} + +func (r *RoleRegistry) nextRoleID(d *dao.Simple, cache *RoleRegistryCache) uint64 { + id := r.getRoleCounter(d) + r.putRoleCounter(d, id+1) + cache.roleCount++ + return id +} + +// Contract methods + +func (r *RoleRegistry) totalRoles(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(r.ID).(*RoleRegistryCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.roleCount))) +} + +func (r *RoleRegistry) getRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { + roleID := toBigInt(args[0]).Uint64() + role := r.getRoleInternal(ic.DAO, roleID) + if role == nil { + return stackitem.Null{} + } + item, _ := role.ToStackItem() + return item +} + +func (r *RoleRegistry) getRoleByName(ic *interop.Context, args []stackitem.Item) stackitem.Item { + name := toString(args[0]) + roleID, found := r.getRoleIDByName(ic.DAO, name) + if !found { + return stackitem.Null{} + } + role := r.getRoleInternal(ic.DAO, roleID) + if role == nil { + return stackitem.Null{} + } + item, _ := role.ToStackItem() + return item +} + +func (r *RoleRegistry) hasRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + roleID := toBigInt(args[1]).Uint64() + return stackitem.NewBool(r.HasRoleInternal(ic.DAO, address, roleID, ic.Block.Index)) +} + +func (r *RoleRegistry) getRolesForAddress(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + roleIDs := r.getAddressRoleIDs(ic.DAO, address) + items := make([]stackitem.Item, len(roleIDs)) + for i, id := range roleIDs { + items[i] = stackitem.NewBigInteger(big.NewInt(int64(id))) + } + return stackitem.NewArray(items) +} + +func (r *RoleRegistry) getPermissions(ic *interop.Context, args []stackitem.Item) stackitem.Item { + roleID := toBigInt(args[0]).Uint64() + var perms []stackitem.Item + prefix := make([]byte, 9) + prefix[0] = rrPrefixPermission + binary.BigEndian.PutUint64(prefix[1:], roleID) + ic.DAO.Seek(r.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + perm := new(state.PermissionGrant) + if err := stackitem.DeserializeConvertible(v, perm); err == nil { + item, _ := perm.ToStackItem() + perms = append(perms, item) + } + return true + }) + return stackitem.NewArray(perms) +} + +func (r *RoleRegistry) hasPermission(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + resource := toString(args[1]) + action := toString(args[2]) + scopeInt := toBigInt(args[3]).Int64() + scope := state.Scope(scopeInt) + return stackitem.NewBool(r.HasPermissionInternal(ic.DAO, address, resource, action, scope, ic.Block.Index)) +} + +func (r *RoleRegistry) createRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if err := r.checkCommitteeWitness(ic); err != nil { + panic(err) + } + + name := toString(args[0]) + description := toString(args[1]) + parentID := toBigInt(args[2]).Uint64() + + // Validation + if len(name) > maxRoleNameLength { + panic(ErrRoleNameTooLong) + } + if len(description) > maxRoleDescriptionLength { + panic(ErrRoleDescTooLong) + } + if _, exists := r.getRoleIDByName(ic.DAO, name); exists { + panic(ErrRoleNameExists) + } + if parentID != 0 { + parent := r.getRoleInternal(ic.DAO, parentID) + if parent == nil { + panic(ErrInvalidParentRole) + } + if !parent.Active { + panic(ErrRoleNotActive) + } + } + + cache := ic.DAO.GetRWCache(r.ID).(*RoleRegistryCache) + roleID := r.nextRoleID(ic.DAO, cache) + + caller := ic.VM.GetCallingScriptHash() + role := &state.Role{ + ID: roleID, + Name: name, + Description: description, + ParentID: parentID, + CreatedAt: ic.Block.Index, + CreatedBy: caller, + Active: true, + } + + if err := r.putRole(ic.DAO, role); err != nil { + panic(err) + } + r.putRoleNameIndex(ic.DAO, name, roleID) + + err := ic.AddNotification(r.Hash, RoleCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(roleID))), + stackitem.NewByteArray([]byte(name)), + stackitem.NewBigInteger(big.NewInt(int64(parentID))), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBigInteger(big.NewInt(int64(roleID))) +} + +func (r *RoleRegistry) deleteRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if err := r.checkCommitteeWitness(ic); err != nil { + panic(err) + } + + roleID := toBigInt(args[0]).Uint64() + + // Cannot delete built-in roles + if roleID <= RoleOperator { + panic(ErrBuiltinRole) + } + + role := r.getRoleInternal(ic.DAO, roleID) + if role == nil { + panic(ErrRoleNotFound) + } + + role.Active = false + if err := r.putRole(ic.DAO, role); err != nil { + panic(err) + } + + caller := ic.VM.GetCallingScriptHash() + err := ic.AddNotification(r.Hash, RoleDeletedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(roleID))), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (r *RoleRegistry) grantRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if err := r.checkCommitteeWitness(ic); err != nil { + panic(err) + } + + address := toUint160(args[0]) + roleID := toBigInt(args[1]).Uint64() + expiresAt := uint32(toBigInt(args[2]).Uint64()) + + // Verify role exists and is active + role := r.getRoleInternal(ic.DAO, roleID) + if role == nil { + panic(ErrRoleNotFound) + } + if !role.Active { + panic(ErrRoleNotActive) + } + + // Check if already assigned + existing := r.getAddressRoleAssignment(ic.DAO, address, roleID) + if existing != nil && existing.Active { + panic(ErrAssignmentExists) + } + + caller := ic.VM.GetCallingScriptHash() + assignment := &state.AddressRoleAssignment{ + Address: address, + RoleID: roleID, + GrantedAt: ic.Block.Index, + GrantedBy: caller, + ExpiresAt: expiresAt, + Active: true, + } + + if err := r.putAddressRoleAssignment(ic.DAO, assignment); err != nil { + panic(err) + } + + err := ic.AddNotification(r.Hash, RoleGrantedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(address.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(roleID))), + stackitem.NewBigInteger(big.NewInt(int64(expiresAt))), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (r *RoleRegistry) revokeRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if err := r.checkCommitteeWitness(ic); err != nil { + panic(err) + } + + address := toUint160(args[0]) + roleID := toBigInt(args[1]).Uint64() + + assignment := r.getAddressRoleAssignment(ic.DAO, address, roleID) + if assignment == nil || !assignment.Active { + panic(ErrAssignmentNotFound) + } + + assignment.Active = false + if err := r.putAddressRoleAssignment(ic.DAO, assignment); err != nil { + panic(err) + } + + caller := ic.VM.GetCallingScriptHash() + err := ic.AddNotification(r.Hash, RoleRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(address.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(roleID))), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (r *RoleRegistry) assignPermission(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if err := r.checkCommitteeWitness(ic); err != nil { + panic(err) + } + + roleID := toBigInt(args[0]).Uint64() + resource := toString(args[1]) + action := toString(args[2]) + scopeInt := toBigInt(args[3]).Int64() + scope := state.Scope(scopeInt) + + // Validation + if len(resource) > maxResourceLength { + panic(ErrResourceTooLong) + } + if len(action) > maxActionLength { + panic(ErrActionTooLong) + } + if scope > state.ScopeDelegated { + panic(ErrInvalidScope) + } + + // Verify role exists and is active + role := r.getRoleInternal(ic.DAO, roleID) + if role == nil { + panic(ErrRoleNotFound) + } + if !role.Active { + panic(ErrRoleNotActive) + } + + // Check if already exists + existing := r.getPermission(ic.DAO, roleID, resource, action) + if existing != nil { + panic(ErrPermissionExists) + } + + caller := ic.VM.GetCallingScriptHash() + perm := &state.PermissionGrant{ + RoleID: roleID, + Resource: resource, + Action: action, + Scope: scope, + GrantedAt: ic.Block.Index, + GrantedBy: caller, + } + + if err := r.putPermission(ic.DAO, perm); err != nil { + panic(err) + } + + err := ic.AddNotification(r.Hash, PermissionAssignedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(roleID))), + stackitem.NewByteArray([]byte(resource)), + stackitem.NewByteArray([]byte(action)), + stackitem.NewBigInteger(big.NewInt(int64(scope))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +func (r *RoleRegistry) removePermission(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if err := r.checkCommitteeWitness(ic); err != nil { + panic(err) + } + + roleID := toBigInt(args[0]).Uint64() + resource := toString(args[1]) + action := toString(args[2]) + + // Verify permission exists + existing := r.getPermission(ic.DAO, roleID, resource, action) + if existing == nil { + panic(ErrPermissionNotFound) + } + + r.deletePermission(ic.DAO, roleID, resource, action) + + err := ic.AddNotification(r.Hash, PermissionRemovedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(roleID))), + stackitem.NewByteArray([]byte(resource)), + stackitem.NewByteArray([]byte(action)), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} diff --git a/pkg/core/native/role_registry_domain.go b/pkg/core/native/role_registry_domain.go index 5174920..c4dace8 100644 --- a/pkg/core/native/role_registry_domain.go +++ b/pkg/core/native/role_registry_domain.go @@ -1,8 +1,8 @@ package native import ( - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // CRIT-002: Domain-specific committee roles for reduced single point of failure. diff --git a/pkg/core/native/salus.go b/pkg/core/native/salus.go index aa37876..bee1549 100644 --- a/pkg/core/native/salus.go +++ b/pkg/core/native/salus.go @@ -1,1516 +1,1516 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Salus represents the universal healthcare native contract. -type Salus struct { - interop.ContractMD - Tutus ITutus - Vita IVita - RoleRegistry IRoleRegistry - Lex ILex -} - -// SalusCache represents the cached state for Salus contract. -type SalusCache struct { - accountCount uint64 - recordCount uint64 - providerCount uint64 - authorizationCount uint64 - emergencyCount uint64 -} - -// Storage key prefixes for Salus. -const ( - salusPrefixAccount byte = 0x01 // vitaID -> HealthcareAccount - salusPrefixAccountByOwner byte = 0x02 // owner -> vitaID - salusPrefixRecord byte = 0x10 // recordID -> MedicalRecord - salusPrefixRecordByPatient byte = 0x11 // vitaID + recordID -> exists - salusPrefixRecordByProvider byte = 0x12 // provider + recordID -> exists - salusPrefixProvider byte = 0x20 // providerID -> HealthcareProvider - salusPrefixProviderByAddress byte = 0x21 // provider address -> providerID - salusPrefixAuthorization byte = 0x30 // authID -> ProviderAuthorization - salusPrefixAuthByPatient byte = 0x31 // vitaID + authID -> exists - salusPrefixAuthByProvider byte = 0x32 // provider + authID -> exists - salusPrefixActiveAuth byte = 0x33 // vitaID + provider -> authID - salusPrefixEmergencyAccess byte = 0x40 // emergencyID -> EmergencyAccess - salusPrefixEmergencyByPatient byte = 0x41 // vitaID + emergencyID -> exists - salusPrefixAccountCounter byte = 0xF0 // -> uint64 - salusPrefixRecordCounter byte = 0xF1 // -> next record ID - salusPrefixProviderCounter byte = 0xF2 // -> next provider ID - salusPrefixAuthCounter byte = 0xF3 // -> next authorization ID - salusPrefixEmergencyCounter byte = 0xF4 // -> next emergency access ID - salusPrefixConfig byte = 0xFF // -> SalusConfig -) - -// Event names for Salus. -const ( - HealthcareActivatedEvent = "HealthcareActivated" - CreditsAllocatedEventSalus = "CreditsAllocated" - MedicalRecordCreatedEvent = "MedicalRecordCreated" - ProviderRegisteredEvent = "ProviderRegistered" - ProviderSuspendedEvent = "ProviderSuspended" - AuthorizationGrantedEvent = "AuthorizationGranted" - AuthorizationRevokedEvent = "AuthorizationRevoked" - EmergencyAccessGrantedEvent = "EmergencyAccessGranted" - EmergencyAccessReviewedEvent = "EmergencyAccessReviewed" -) - -// Role constants for healthcare providers. -const ( - RoleHealthcare uint64 = 21 // Can record medical events and access authorized records -) - -// Various errors for Salus. -var ( - ErrSalusAccountNotFound = errors.New("healthcare account not found") - ErrSalusAccountExists = errors.New("healthcare account already exists") - ErrSalusAccountSuspended = errors.New("healthcare account is suspended") - ErrSalusAccountClosed = errors.New("healthcare account is closed") - ErrSalusNoVita = errors.New("owner must have an active Vita") - ErrSalusInsufficientCredits = errors.New("insufficient healthcare credits") - ErrSalusInvalidCredits = errors.New("invalid credit amount") - ErrSalusRecordNotFound = errors.New("medical record not found") - ErrSalusProviderNotFound = errors.New("healthcare provider not found") - ErrSalusProviderExists = errors.New("healthcare provider already registered") - ErrSalusProviderSuspended = errors.New("healthcare provider is suspended") - ErrSalusProviderRevoked = errors.New("healthcare provider is revoked") - ErrSalusNotProvider = errors.New("caller is not an authorized healthcare provider") - ErrSalusNotCommittee = errors.New("invalid committee signature") - ErrSalusInvalidOwner = errors.New("invalid owner address") - ErrSalusInvalidProvider = errors.New("invalid provider address") - ErrSalusAuthorizationNotFound = errors.New("authorization not found") - ErrSalusAuthorizationExpired = errors.New("authorization has expired") - ErrSalusAuthorizationExists = errors.New("authorization already exists") - ErrSalusNotPatient = errors.New("caller is not the patient") - ErrSalusHealthcareRestricted = errors.New("healthcare right is restricted") - ErrSalusEmergencyNotFound = errors.New("emergency access not found") - ErrSalusInvalidReason = errors.New("invalid reason") - ErrSalusInvalidName = errors.New("invalid name") - ErrSalusInvalidSpecialty = errors.New("invalid specialty") - ErrSalusNoAccess = errors.New("no access to patient records") - ErrSalusExceedsMaxDuration = errors.New("exceeds maximum authorization duration") -) - -var ( - _ interop.Contract = (*Salus)(nil) - _ dao.NativeContractCache = (*SalusCache)(nil) -) - -// Copy implements NativeContractCache interface. -func (c *SalusCache) Copy() dao.NativeContractCache { - return &SalusCache{ - accountCount: c.accountCount, - recordCount: c.recordCount, - providerCount: c.providerCount, - authorizationCount: c.authorizationCount, - emergencyCount: c.emergencyCount, - } -} - -// checkCommittee checks if the caller has committee authority. -func (s *Salus) checkCommittee(ic *interop.Context) bool { - if s.RoleRegistry != nil { - return s.RoleRegistry.CheckCommittee(ic) - } - return s.Tutus.CheckCommittee(ic) -} - -// checkHealthcareProvider checks if the caller has healthcare provider authority. -func (s *Salus) checkHealthcareProvider(ic *interop.Context) bool { - caller := ic.VM.GetCallingScriptHash() - if s.RoleRegistry != nil { - if s.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleHealthcare, ic.Block.Index) { - return true - } - } - // Committee members can also act as healthcare providers - return s.checkCommittee(ic) -} - -// checkHealthcareRight checks if subject has healthcare rights via Lex. -func (s *Salus) checkHealthcareRight(ic *interop.Context, subject util.Uint160) bool { - if s.Lex == nil { - return true // Allow if Lex not available - } - return s.Lex.HasRightInternal(ic.DAO, subject, state.RightHealthcare, ic.Block.Index) -} - -// newSalus creates a new Salus native contract. -func newSalus() *Salus { - s := &Salus{ - ContractMD: *interop.NewContractMD(nativenames.Salus, nativeids.Salus), - } - defer s.BuildHFSpecificMD(s.ActiveIn()) - - // ===== Account Management ===== - - // activateHealthcare - Activate healthcare account for a Vita holder - desc := NewDescriptor("activateHealthcare", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md := NewMethodAndPrice(s.activateHealthcare, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getAccount - Get healthcare account by owner - desc = NewDescriptor("getAccount", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(s.getAccount, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getAccountByVitaID - Get account by Vita ID - desc = NewDescriptor("getAccountByVitaID", smartcontract.ArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getAccountByVitaID, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // allocateCredits - Allocate healthcare credits (committee only) - desc = NewDescriptor("allocateCredits", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(s.allocateCredits, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getCredits - Get available credits - desc = NewDescriptor("getCredits", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(s.getCredits, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Medical Records ===== - - // recordMedicalEvent - Record a medical event (provider only) - desc = NewDescriptor("recordMedicalEvent", smartcontract.IntegerType, - manifest.NewParameter("patient", smartcontract.Hash160Type), - manifest.NewParameter("recordType", smartcontract.IntegerType), - manifest.NewParameter("contentHash", smartcontract.Hash256Type), - manifest.NewParameter("credits", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.recordMedicalEvent, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getMedicalRecord - Get medical record by ID - desc = NewDescriptor("getMedicalRecord", smartcontract.ArrayType, - manifest.NewParameter("recordID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getMedicalRecord, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Provider Management ===== - - // registerProvider - Register a healthcare provider (committee only) - desc = NewDescriptor("registerProvider", smartcontract.IntegerType, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("specialty", smartcontract.StringType), - manifest.NewParameter("licenseHash", smartcontract.Hash256Type)) - md = NewMethodAndPrice(s.registerProvider, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // suspendProvider - Suspend a healthcare provider (committee only) - desc = NewDescriptor("suspendProvider", smartcontract.BoolType, - manifest.NewParameter("providerID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(s.suspendProvider, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getProvider - Get provider details - desc = NewDescriptor("getProvider", smartcontract.ArrayType, - manifest.NewParameter("providerID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getProvider, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getProviderByAddress - Get provider by address - desc = NewDescriptor("getProviderByAddress", smartcontract.ArrayType, - manifest.NewParameter("address", smartcontract.Hash160Type)) - md = NewMethodAndPrice(s.getProviderByAddress, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Authorization Management ===== - - // authorizeAccess - Grant provider access to patient records - desc = NewDescriptor("authorizeAccess", smartcontract.IntegerType, - manifest.NewParameter("patient", smartcontract.Hash160Type), - manifest.NewParameter("provider", smartcontract.Hash160Type), - manifest.NewParameter("accessLevel", smartcontract.IntegerType), - manifest.NewParameter("duration", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.authorizeAccess, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // revokeAccess - Revoke provider access - desc = NewDescriptor("revokeAccess", smartcontract.BoolType, - manifest.NewParameter("authID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.revokeAccess, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getAuthorization - Get authorization details - desc = NewDescriptor("getAuthorization", smartcontract.ArrayType, - manifest.NewParameter("authID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getAuthorization, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // hasAccess - Check if provider has access to patient - desc = NewDescriptor("hasAccess", smartcontract.BoolType, - manifest.NewParameter("patient", smartcontract.Hash160Type), - manifest.NewParameter("provider", smartcontract.Hash160Type)) - md = NewMethodAndPrice(s.hasAccess, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Emergency Access ===== - - // emergencyAccess - Request emergency access (provider only) - desc = NewDescriptor("emergencyAccess", smartcontract.IntegerType, - manifest.NewParameter("patient", smartcontract.Hash160Type), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(s.emergencyAccess, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // reviewEmergencyAccess - Review emergency access (committee only) - desc = NewDescriptor("reviewEmergencyAccess", smartcontract.BoolType, - manifest.NewParameter("emergencyID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.reviewEmergencyAccess, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getEmergencyAccess - Get emergency access details - desc = NewDescriptor("getEmergencyAccess", smartcontract.ArrayType, - manifest.NewParameter("emergencyID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getEmergencyAccess, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Query Methods ===== - - // getConfig - Get Salus configuration - desc = NewDescriptor("getConfig", smartcontract.ArrayType) - md = NewMethodAndPrice(s.getConfig, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getTotalAccounts - Get total healthcare accounts - desc = NewDescriptor("getTotalAccounts", smartcontract.IntegerType) - md = NewMethodAndPrice(s.getTotalAccounts, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getTotalRecords - Get total medical records - desc = NewDescriptor("getTotalRecords", smartcontract.IntegerType) - md = NewMethodAndPrice(s.getTotalRecords, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getTotalProviders - Get total healthcare providers - desc = NewDescriptor("getTotalProviders", smartcontract.IntegerType) - md = NewMethodAndPrice(s.getTotalProviders, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Events ===== - - // HealthcareActivated event - eDesc := NewEventDescriptor(HealthcareActivatedEvent, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("owner", smartcontract.Hash160Type)) - s.AddEvent(NewEvent(eDesc)) - - // CreditsAllocated event - eDesc = NewEventDescriptor(CreditsAllocatedEventSalus, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("total", smartcontract.IntegerType)) - s.AddEvent(NewEvent(eDesc)) - - // MedicalRecordCreated event - eDesc = NewEventDescriptor(MedicalRecordCreatedEvent, - manifest.NewParameter("recordID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("recordType", smartcontract.IntegerType)) - s.AddEvent(NewEvent(eDesc)) - - // ProviderRegistered event - eDesc = NewEventDescriptor(ProviderRegisteredEvent, - manifest.NewParameter("providerID", smartcontract.IntegerType), - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("specialty", smartcontract.StringType)) - s.AddEvent(NewEvent(eDesc)) - - // ProviderSuspended event - eDesc = NewEventDescriptor(ProviderSuspendedEvent, - manifest.NewParameter("providerID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - s.AddEvent(NewEvent(eDesc)) - - // AuthorizationGranted event - eDesc = NewEventDescriptor(AuthorizationGrantedEvent, - manifest.NewParameter("authID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("provider", smartcontract.Hash160Type)) - s.AddEvent(NewEvent(eDesc)) - - // AuthorizationRevoked event - eDesc = NewEventDescriptor(AuthorizationRevokedEvent, - manifest.NewParameter("authID", smartcontract.IntegerType)) - s.AddEvent(NewEvent(eDesc)) - - // EmergencyAccessGranted event - eDesc = NewEventDescriptor(EmergencyAccessGrantedEvent, - manifest.NewParameter("emergencyID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("provider", smartcontract.Hash160Type)) - s.AddEvent(NewEvent(eDesc)) - - // EmergencyAccessReviewed event - eDesc = NewEventDescriptor(EmergencyAccessReviewedEvent, - manifest.NewParameter("emergencyID", smartcontract.IntegerType)) - s.AddEvent(NewEvent(eDesc)) - - return s -} - -// Metadata returns contract metadata. -func (s *Salus) Metadata() *interop.ContractMD { - return &s.ContractMD -} - -// Initialize initializes the Salus contract. -func (s *Salus) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != s.ActiveIn() { - return nil - } - - // Initialize counters - s.setAccountCounter(ic.DAO, 0) - s.setRecordCounter(ic.DAO, 0) - s.setProviderCounter(ic.DAO, 0) - s.setAuthCounter(ic.DAO, 0) - s.setEmergencyCounter(ic.DAO, 0) - - // Initialize config with defaults - cfg := &state.SalusConfig{ - DefaultAnnualCredits: 10000, // 10000 healthcare credits per year - EmergencyAccessDuration: 86400, // ~24 hours (1-second blocks) - PreventiveCareBonus: 500, // Bonus for preventive care visits - MaxAuthorizationDuration: 2592000, // ~30 days (1-second blocks) - } - s.setConfig(ic.DAO, cfg) - - // Initialize cache - cache := &SalusCache{ - accountCount: 0, - recordCount: 0, - providerCount: 0, - authorizationCount: 0, - emergencyCount: 0, - } - ic.DAO.SetCache(s.ID, cache) - - return nil -} - -// InitializeCache initializes the cache from storage. -func (s *Salus) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cache := &SalusCache{ - accountCount: s.getAccountCounter(d), - recordCount: s.getRecordCounter(d), - providerCount: s.getProviderCounter(d), - authorizationCount: s.getAuthCounter(d), - emergencyCount: s.getEmergencyCounter(d), - } - d.SetCache(s.ID, cache) - return nil -} - -// OnPersist is called before block is committed. -func (s *Salus) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist is called after block is committed. -func (s *Salus) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn returns the hardfork at which this contract is activated. -func (s *Salus) ActiveIn() *config.Hardfork { - return nil // Always active -} - -// ===== Storage Helpers ===== - -func (s *Salus) makeAccountKey(vitaID uint64) []byte { - key := make([]byte, 9) - key[0] = salusPrefixAccount - binary.BigEndian.PutUint64(key[1:], vitaID) - return key -} - -func (s *Salus) makeAccountByOwnerKey(owner util.Uint160) []byte { - key := make([]byte, 21) - key[0] = salusPrefixAccountByOwner - copy(key[1:], owner.BytesBE()) - return key -} - -func (s *Salus) makeRecordKey(recordID uint64) []byte { - key := make([]byte, 9) - key[0] = salusPrefixRecord - binary.BigEndian.PutUint64(key[1:], recordID) - return key -} - -func (s *Salus) makeRecordByPatientKey(vitaID, recordID uint64) []byte { - key := make([]byte, 17) - key[0] = salusPrefixRecordByPatient - binary.BigEndian.PutUint64(key[1:9], vitaID) - binary.BigEndian.PutUint64(key[9:], recordID) - return key -} - -func (s *Salus) makeProviderKey(providerID uint64) []byte { - key := make([]byte, 9) - key[0] = salusPrefixProvider - binary.BigEndian.PutUint64(key[1:], providerID) - return key -} - -func (s *Salus) makeProviderByAddressKey(address util.Uint160) []byte { - key := make([]byte, 21) - key[0] = salusPrefixProviderByAddress - copy(key[1:], address.BytesBE()) - return key -} - -func (s *Salus) makeAuthorizationKey(authID uint64) []byte { - key := make([]byte, 9) - key[0] = salusPrefixAuthorization - binary.BigEndian.PutUint64(key[1:], authID) - return key -} - -func (s *Salus) makeActiveAuthKey(vitaID uint64, provider util.Uint160) []byte { - key := make([]byte, 29) - key[0] = salusPrefixActiveAuth - binary.BigEndian.PutUint64(key[1:9], vitaID) - copy(key[9:], provider.BytesBE()) - return key -} - -func (s *Salus) makeEmergencyKey(emergencyID uint64) []byte { - key := make([]byte, 9) - key[0] = salusPrefixEmergencyAccess - binary.BigEndian.PutUint64(key[1:], emergencyID) - return key -} - -// Counter getters/setters -func (s *Salus) getAccountCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{salusPrefixAccountCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Salus) setAccountCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{salusPrefixAccountCounter}, buf) -} - -func (s *Salus) getRecordCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{salusPrefixRecordCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Salus) setRecordCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{salusPrefixRecordCounter}, buf) -} - -func (s *Salus) getProviderCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{salusPrefixProviderCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Salus) setProviderCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{salusPrefixProviderCounter}, buf) -} - -func (s *Salus) getAuthCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{salusPrefixAuthCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Salus) setAuthCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{salusPrefixAuthCounter}, buf) -} - -func (s *Salus) getEmergencyCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{salusPrefixEmergencyCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Salus) setEmergencyCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{salusPrefixEmergencyCounter}, buf) -} - -// Config getter/setter -func (s *Salus) getConfigInternal(d *dao.Simple) *state.SalusConfig { - si := d.GetStorageItem(s.ID, []byte{salusPrefixConfig}) - if si == nil { - return &state.SalusConfig{ - DefaultAnnualCredits: 10000, - EmergencyAccessDuration: 86400, - PreventiveCareBonus: 500, - MaxAuthorizationDuration: 2592000, - } - } - cfg := new(state.SalusConfig) - item, _ := stackitem.Deserialize(si) - cfg.FromStackItem(item) - return cfg -} - -func (s *Salus) setConfig(d *dao.Simple, cfg *state.SalusConfig) { - item, _ := cfg.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, []byte{salusPrefixConfig}, data) -} - -// Account storage -func (s *Salus) getAccountInternal(d *dao.Simple, vitaID uint64) *state.HealthcareAccount { - si := d.GetStorageItem(s.ID, s.makeAccountKey(vitaID)) - if si == nil { - return nil - } - acc := new(state.HealthcareAccount) - item, _ := stackitem.Deserialize(si) - acc.FromStackItem(item) - return acc -} - -func (s *Salus) putAccount(d *dao.Simple, acc *state.HealthcareAccount) { - item, _ := acc.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeAccountKey(acc.VitaID), data) -} - -func (s *Salus) getVitaIDByOwner(d *dao.Simple, owner util.Uint160) (uint64, bool) { - si := d.GetStorageItem(s.ID, s.makeAccountByOwnerKey(owner)) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint64(si), true -} - -func (s *Salus) setOwnerToVitaID(d *dao.Simple, owner util.Uint160, vitaID uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, vitaID) - d.PutStorageItem(s.ID, s.makeAccountByOwnerKey(owner), buf) -} - -// Record storage -func (s *Salus) getRecordInternal(d *dao.Simple, recordID uint64) *state.MedicalRecord { - si := d.GetStorageItem(s.ID, s.makeRecordKey(recordID)) - if si == nil { - return nil - } - record := new(state.MedicalRecord) - item, _ := stackitem.Deserialize(si) - record.FromStackItem(item) - return record -} - -func (s *Salus) putRecord(d *dao.Simple, record *state.MedicalRecord) { - item, _ := record.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeRecordKey(record.ID), data) -} - -func (s *Salus) setRecordByPatient(d *dao.Simple, vitaID, recordID uint64) { - d.PutStorageItem(s.ID, s.makeRecordByPatientKey(vitaID, recordID), []byte{1}) -} - -// Provider storage -func (s *Salus) getProviderInternal(d *dao.Simple, providerID uint64) *state.HealthcareProvider { - si := d.GetStorageItem(s.ID, s.makeProviderKey(providerID)) - if si == nil { - return nil - } - provider := new(state.HealthcareProvider) - item, _ := stackitem.Deserialize(si) - provider.FromStackItem(item) - return provider -} - -func (s *Salus) putProvider(d *dao.Simple, provider *state.HealthcareProvider) { - item, _ := provider.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeProviderKey(provider.ProviderID), data) -} - -func (s *Salus) getProviderIDByAddress(d *dao.Simple, address util.Uint160) (uint64, bool) { - si := d.GetStorageItem(s.ID, s.makeProviderByAddressKey(address)) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint64(si), true -} - -func (s *Salus) setProviderByAddress(d *dao.Simple, address util.Uint160, providerID uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, providerID) - d.PutStorageItem(s.ID, s.makeProviderByAddressKey(address), buf) -} - -// Authorization storage -func (s *Salus) getAuthInternal(d *dao.Simple, authID uint64) *state.ProviderAuthorization { - si := d.GetStorageItem(s.ID, s.makeAuthorizationKey(authID)) - if si == nil { - return nil - } - auth := new(state.ProviderAuthorization) - item, _ := stackitem.Deserialize(si) - auth.FromStackItem(item) - return auth -} - -func (s *Salus) putAuth(d *dao.Simple, auth *state.ProviderAuthorization) { - item, _ := auth.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeAuthorizationKey(auth.ID), data) -} - -func (s *Salus) getActiveAuthID(d *dao.Simple, vitaID uint64, provider util.Uint160) (uint64, bool) { - si := d.GetStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider)) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint64(si), true -} - -func (s *Salus) setActiveAuthID(d *dao.Simple, vitaID uint64, provider util.Uint160, authID uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, authID) - d.PutStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider), buf) -} - -func (s *Salus) clearActiveAuth(d *dao.Simple, vitaID uint64, provider util.Uint160) { - d.DeleteStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider)) -} - -// Emergency access storage -func (s *Salus) getEmergencyInternal(d *dao.Simple, emergencyID uint64) *state.EmergencyAccess { - si := d.GetStorageItem(s.ID, s.makeEmergencyKey(emergencyID)) - if si == nil { - return nil - } - emergency := new(state.EmergencyAccess) - item, _ := stackitem.Deserialize(si) - emergency.FromStackItem(item) - return emergency -} - -func (s *Salus) putEmergency(d *dao.Simple, emergency *state.EmergencyAccess) { - item, _ := emergency.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeEmergencyKey(emergency.ID), data) -} - -// ===== Contract Methods ===== - -// activateHealthcare activates healthcare account for a Vita holder. -func (s *Salus) activateHealthcare(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - // Check owner has active Vita - if s.Vita == nil { - panic(ErrSalusNoVita) - } - vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) - if err != nil || vita == nil { - panic(ErrSalusNoVita) - } - if vita.Status != state.TokenStatusActive { - panic(ErrSalusNoVita) - } - - // Check if account already exists - existing := s.getAccountInternal(ic.DAO, vita.TokenID) - if existing != nil { - panic(ErrSalusAccountExists) - } - - // Check healthcare rights - if !s.checkHealthcareRight(ic, owner) { - // Log but allow (EnforcementLogging) - } - - // Get cache and increment counter - cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) - cache.accountCount++ - s.setAccountCounter(ic.DAO, cache.accountCount) - - // Get default credits from config - cfg := s.getConfigInternal(ic.DAO) - - // Create account - acc := &state.HealthcareAccount{ - VitaID: vita.TokenID, - Owner: owner, - AnnualAllocation: cfg.DefaultAnnualCredits, - CreditsUsed: 0, - CreditsAvailable: cfg.DefaultAnnualCredits, - BiologicalAge: 0, - LastCheckup: 0, - Status: state.HealthcareAccountActive, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - - // Store account - s.putAccount(ic.DAO, acc) - s.setOwnerToVitaID(ic.DAO, owner, vita.TokenID) - - // Emit event - ic.AddNotification(s.Hash, HealthcareActivatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), - stackitem.NewByteArray(owner.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -// getAccount returns healthcare account by owner. -func (s *Salus) getAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.Null{} - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - return stackitem.Null{} - } - - item, _ := acc.ToStackItem() - return item -} - -// getAccountByVitaID returns healthcare account by Vita ID. -func (s *Salus) getAccountByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - return stackitem.Null{} - } - - item, _ := acc.ToStackItem() - return item -} - -// allocateCredits allocates healthcare credits to an account (committee only). -func (s *Salus) allocateCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - amount := toUint64(args[1]) - // reason := toString(args[2]) // for logging - - // Committee only - if !s.checkCommittee(ic) { - panic(ErrSalusNotCommittee) - } - - if amount == 0 { - panic(ErrSalusInvalidCredits) - } - - // Get or create account - vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) - if !found { - // Auto-create account if Vita exists - if s.Vita == nil { - panic(ErrSalusNoVita) - } - vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) - if err != nil || vita == nil || vita.Status != state.TokenStatusActive { - panic(ErrSalusNoVita) - } - vitaID = vita.TokenID - - cfg := s.getConfigInternal(ic.DAO) - - // Create account - cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) - cache.accountCount++ - s.setAccountCounter(ic.DAO, cache.accountCount) - - acc := &state.HealthcareAccount{ - VitaID: vitaID, - Owner: owner, - AnnualAllocation: cfg.DefaultAnnualCredits, - CreditsUsed: 0, - CreditsAvailable: amount, - BiologicalAge: 0, - LastCheckup: 0, - Status: state.HealthcareAccountActive, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - s.putAccount(ic.DAO, acc) - s.setOwnerToVitaID(ic.DAO, owner, vitaID) - - // Emit events - ic.AddNotification(s.Hash, HealthcareActivatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewByteArray(owner.BytesBE()), - })) - ic.AddNotification(s.Hash, CreditsAllocatedEventSalus, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(amount))), - stackitem.NewBigInteger(big.NewInt(int64(amount))), - })) - - return stackitem.NewBool(true) - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - panic(ErrSalusAccountNotFound) - } - if acc.Status != state.HealthcareAccountActive { - panic(ErrSalusAccountSuspended) - } - - // Add credits - acc.CreditsAvailable += amount - acc.UpdatedAt = ic.Block.Index - - s.putAccount(ic.DAO, acc) - - // Emit event - ic.AddNotification(s.Hash, CreditsAllocatedEventSalus, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(amount))), - stackitem.NewBigInteger(big.NewInt(int64(acc.CreditsAvailable))), - })) - - return stackitem.NewBool(true) -} - -// getCredits returns available credits for an owner. -func (s *Salus) getCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - return stackitem.NewBigInteger(big.NewInt(int64(acc.CreditsAvailable))) -} - -// recordMedicalEvent records a medical event (provider only). -func (s *Salus) recordMedicalEvent(ic *interop.Context, args []stackitem.Item) stackitem.Item { - patient := toUint160(args[0]) - recordType := state.MedicalRecordType(toUint64(args[1])) - contentHashBytes := toBytes(args[2]) - credits := toUint64(args[3]) - - // Convert bytes to Uint256 - var contentHash util.Uint256 - if len(contentHashBytes) == 32 { - copy(contentHash[:], contentHashBytes) - } - - // Check provider authority - if !s.checkHealthcareProvider(ic) { - panic(ErrSalusNotProvider) - } - - // Get provider address - provider := ic.VM.GetCallingScriptHash() - - // Get patient's Vita - if s.Vita == nil { - panic(ErrSalusNoVita) - } - vita, err := s.Vita.GetTokenByOwner(ic.DAO, patient) - if err != nil || vita == nil || vita.Status != state.TokenStatusActive { - panic(ErrSalusNoVita) - } - - // Check provider has access (unless emergency or authorized) - vitaID, found := s.getVitaIDByOwner(ic.DAO, patient) - if found { - authID, hasAuth := s.getActiveAuthID(ic.DAO, vitaID, provider) - if hasAuth { - auth := s.getAuthInternal(ic.DAO, authID) - if auth == nil || !auth.IsValid(ic.Block.Index) { - panic(ErrSalusNoAccess) - } - } - // If no auth, still allow (emergency can be logged separately) - } - - // Get or auto-create account - if !found { - // Auto-create - cfg := s.getConfigInternal(ic.DAO) - cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) - cache.accountCount++ - s.setAccountCounter(ic.DAO, cache.accountCount) - - acc := &state.HealthcareAccount{ - VitaID: vita.TokenID, - Owner: patient, - AnnualAllocation: cfg.DefaultAnnualCredits, - CreditsUsed: 0, - CreditsAvailable: cfg.DefaultAnnualCredits, - BiologicalAge: 0, - LastCheckup: 0, - Status: state.HealthcareAccountActive, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - s.putAccount(ic.DAO, acc) - s.setOwnerToVitaID(ic.DAO, patient, vita.TokenID) - vitaID = vita.TokenID - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - panic(ErrSalusAccountNotFound) - } - if acc.Status != state.HealthcareAccountActive { - panic(ErrSalusAccountSuspended) - } - - // Check sufficient credits - if acc.CreditsAvailable < credits { - panic(ErrSalusInsufficientCredits) - } - - // Get next record ID - cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) - recordID := cache.recordCount - cache.recordCount++ - s.setRecordCounter(ic.DAO, cache.recordCount) - - // Deduct credits - acc.CreditsUsed += credits - acc.CreditsAvailable -= credits - acc.UpdatedAt = ic.Block.Index - - // Update last checkup if appropriate - if recordType == state.RecordTypeCheckup || recordType == state.RecordTypePreventive { - acc.LastCheckup = ic.Block.Index - } - - s.putAccount(ic.DAO, acc) - - // Create record - record := &state.MedicalRecord{ - ID: recordID, - VitaID: vitaID, - Patient: patient, - Provider: provider, - RecordType: recordType, - ContentHash: contentHash, - CreditsUsed: credits, - CreatedAt: ic.Block.Index, - IsActive: true, - } - - s.putRecord(ic.DAO, record) - s.setRecordByPatient(ic.DAO, vitaID, recordID) - - // Emit event - ic.AddNotification(s.Hash, MedicalRecordCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(recordID))), - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(recordType))), - })) - - return stackitem.NewBigInteger(big.NewInt(int64(recordID))) -} - -// getMedicalRecord returns medical record by ID. -func (s *Salus) getMedicalRecord(ic *interop.Context, args []stackitem.Item) stackitem.Item { - recordID := toUint64(args[0]) - - record := s.getRecordInternal(ic.DAO, recordID) - if record == nil { - return stackitem.Null{} - } - - item, _ := record.ToStackItem() - return item -} - -// registerProvider registers a healthcare provider (committee only). -func (s *Salus) registerProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - name := toString(args[1]) - specialty := toString(args[2]) - licenseHashBytes := toBytes(args[3]) - - // Convert bytes to Uint256 - var licenseHash util.Uint256 - if len(licenseHashBytes) == 32 { - copy(licenseHash[:], licenseHashBytes) - } - - // Committee only - if !s.checkCommittee(ic) { - panic(ErrSalusNotCommittee) - } - - // Validate inputs - if len(name) == 0 || len(name) > 128 { - panic(ErrSalusInvalidName) - } - if len(specialty) == 0 || len(specialty) > 64 { - panic(ErrSalusInvalidSpecialty) - } - - // Check if provider already exists - _, exists := s.getProviderIDByAddress(ic.DAO, address) - if exists { - panic(ErrSalusProviderExists) - } - - // Get next provider ID - cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) - providerID := cache.providerCount - cache.providerCount++ - s.setProviderCounter(ic.DAO, cache.providerCount) - - // Create provider - provider := &state.HealthcareProvider{ - Address: address, - Name: name, - ProviderID: providerID, - Specialty: specialty, - LicenseHash: licenseHash, - Status: state.ProviderStatusActive, - RegisteredAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - - s.putProvider(ic.DAO, provider) - s.setProviderByAddress(ic.DAO, address, providerID) - - // Emit event - ic.AddNotification(s.Hash, ProviderRegisteredEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(providerID))), - stackitem.NewByteArray(address.BytesBE()), - stackitem.NewByteArray([]byte(specialty)), - })) - - return stackitem.NewBigInteger(big.NewInt(int64(providerID))) -} - -// suspendProvider suspends a healthcare provider (committee only). -func (s *Salus) suspendProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { - providerID := toUint64(args[0]) - reason := toString(args[1]) - - // Committee only - if !s.checkCommittee(ic) { - panic(ErrSalusNotCommittee) - } - - provider := s.getProviderInternal(ic.DAO, providerID) - if provider == nil { - panic(ErrSalusProviderNotFound) - } - if provider.Status == state.ProviderStatusRevoked { - panic(ErrSalusProviderRevoked) - } - - // Suspend - provider.Status = state.ProviderStatusSuspended - provider.UpdatedAt = ic.Block.Index - s.putProvider(ic.DAO, provider) - - // Emit event - ic.AddNotification(s.Hash, ProviderSuspendedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(providerID))), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -// getProvider returns provider details. -func (s *Salus) getProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { - providerID := toUint64(args[0]) - - provider := s.getProviderInternal(ic.DAO, providerID) - if provider == nil { - return stackitem.Null{} - } - - item, _ := provider.ToStackItem() - return item -} - -// getProviderByAddress returns provider by address. -func (s *Salus) getProviderByAddress(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - - providerID, found := s.getProviderIDByAddress(ic.DAO, address) - if !found { - return stackitem.Null{} - } - - provider := s.getProviderInternal(ic.DAO, providerID) - if provider == nil { - return stackitem.Null{} - } - - item, _ := provider.ToStackItem() - return item -} - -// authorizeAccess grants provider access to patient records. -func (s *Salus) authorizeAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { - patient := toUint160(args[0]) - provider := toUint160(args[1]) - accessLevel := state.AccessLevel(toUint64(args[2])) - duration := toUint32(args[3]) - - // Get patient's Vita and account - vitaID, found := s.getVitaIDByOwner(ic.DAO, patient) - if !found { - panic(ErrSalusAccountNotFound) - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - panic(ErrSalusAccountNotFound) - } - if acc.Status != state.HealthcareAccountActive { - panic(ErrSalusAccountSuspended) - } - - // Check caller is patient (self-authorization) - caller := ic.VM.GetCallingScriptHash() - if caller != patient && !s.checkCommittee(ic) { - panic(ErrSalusNotPatient) - } - - // Check max duration - cfg := s.getConfigInternal(ic.DAO) - if duration > cfg.MaxAuthorizationDuration { - panic(ErrSalusExceedsMaxDuration) - } - - // Check if authorization already exists - existingAuthID, exists := s.getActiveAuthID(ic.DAO, vitaID, provider) - if exists { - existingAuth := s.getAuthInternal(ic.DAO, existingAuthID) - if existingAuth != nil && existingAuth.IsValid(ic.Block.Index) { - panic(ErrSalusAuthorizationExists) - } - } - - // Get next auth ID - cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) - authID := cache.authorizationCount - cache.authorizationCount++ - s.setAuthCounter(ic.DAO, cache.authorizationCount) - - // Calculate expiry - expiresAt := uint32(0) - if duration > 0 { - expiresAt = ic.Block.Index + duration - } - - // Create authorization - auth := &state.ProviderAuthorization{ - ID: authID, - VitaID: vitaID, - Patient: patient, - Provider: provider, - AccessLevel: accessLevel, - StartsAt: ic.Block.Index, - ExpiresAt: expiresAt, - IsActive: true, - GrantedAt: ic.Block.Index, - } - - s.putAuth(ic.DAO, auth) - s.setActiveAuthID(ic.DAO, vitaID, provider, authID) - - // Emit event - ic.AddNotification(s.Hash, AuthorizationGrantedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(authID))), - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewByteArray(provider.BytesBE()), - })) - - return stackitem.NewBigInteger(big.NewInt(int64(authID))) -} - -// revokeAccess revokes provider access. -func (s *Salus) revokeAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { - authID := toUint64(args[0]) - - auth := s.getAuthInternal(ic.DAO, authID) - if auth == nil { - panic(ErrSalusAuthorizationNotFound) - } - - // Check caller is patient or committee - caller := ic.VM.GetCallingScriptHash() - if caller != auth.Patient && !s.checkCommittee(ic) { - panic(ErrSalusNotPatient) - } - - // Revoke - auth.IsActive = false - s.putAuth(ic.DAO, auth) - s.clearActiveAuth(ic.DAO, auth.VitaID, auth.Provider) - - // Emit event - ic.AddNotification(s.Hash, AuthorizationRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(authID))), - })) - - return stackitem.NewBool(true) -} - -// getAuthorization returns authorization details. -func (s *Salus) getAuthorization(ic *interop.Context, args []stackitem.Item) stackitem.Item { - authID := toUint64(args[0]) - - auth := s.getAuthInternal(ic.DAO, authID) - if auth == nil { - return stackitem.Null{} - } - - item, _ := auth.ToStackItem() - return item -} - -// hasAccess checks if provider has access to patient. -func (s *Salus) hasAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { - patient := toUint160(args[0]) - provider := toUint160(args[1]) - - vitaID, found := s.getVitaIDByOwner(ic.DAO, patient) - if !found { - return stackitem.NewBool(false) - } - - authID, exists := s.getActiveAuthID(ic.DAO, vitaID, provider) - if !exists { - return stackitem.NewBool(false) - } - - auth := s.getAuthInternal(ic.DAO, authID) - if auth == nil { - return stackitem.NewBool(false) - } - - return stackitem.NewBool(auth.IsValid(ic.Block.Index)) -} - -// emergencyAccess requests emergency access to patient records. -func (s *Salus) emergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { - patient := toUint160(args[0]) - reason := toString(args[1]) - - // Check provider authority - if !s.checkHealthcareProvider(ic) { - panic(ErrSalusNotProvider) - } - - if len(reason) == 0 || len(reason) > 256 { - panic(ErrSalusInvalidReason) - } - - // Get provider address - provider := ic.VM.GetCallingScriptHash() - - // Get patient's Vita - if s.Vita == nil { - panic(ErrSalusNoVita) - } - vita, err := s.Vita.GetTokenByOwner(ic.DAO, patient) - if err != nil || vita == nil || vita.Status != state.TokenStatusActive { - panic(ErrSalusNoVita) - } - - // Get config for emergency duration - cfg := s.getConfigInternal(ic.DAO) - - // Get next emergency ID - cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) - emergencyID := cache.emergencyCount - cache.emergencyCount++ - s.setEmergencyCounter(ic.DAO, cache.emergencyCount) - - // Create emergency access - emergency := &state.EmergencyAccess{ - ID: emergencyID, - VitaID: vita.TokenID, - Patient: patient, - Provider: provider, - Reason: reason, - GrantedAt: ic.Block.Index, - ExpiresAt: ic.Block.Index + cfg.EmergencyAccessDuration, - WasReviewed: false, - } - - s.putEmergency(ic.DAO, emergency) - - // Emit event - ic.AddNotification(s.Hash, EmergencyAccessGrantedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(emergencyID))), - stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), - stackitem.NewByteArray(provider.BytesBE()), - })) - - return stackitem.NewBigInteger(big.NewInt(int64(emergencyID))) -} - -// reviewEmergencyAccess marks emergency access as reviewed (committee only). -func (s *Salus) reviewEmergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { - emergencyID := toUint64(args[0]) - - // Committee only - if !s.checkCommittee(ic) { - panic(ErrSalusNotCommittee) - } - - emergency := s.getEmergencyInternal(ic.DAO, emergencyID) - if emergency == nil { - panic(ErrSalusEmergencyNotFound) - } - - // Mark as reviewed - emergency.WasReviewed = true - s.putEmergency(ic.DAO, emergency) - - // Emit event - ic.AddNotification(s.Hash, EmergencyAccessReviewedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(emergencyID))), - })) - - return stackitem.NewBool(true) -} - -// getEmergencyAccess returns emergency access details. -func (s *Salus) getEmergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { - emergencyID := toUint64(args[0]) - - emergency := s.getEmergencyInternal(ic.DAO, emergencyID) - if emergency == nil { - return stackitem.Null{} - } - - item, _ := emergency.ToStackItem() - return item -} - -// getConfig returns the Salus configuration. -func (s *Salus) getConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cfg := s.getConfigInternal(ic.DAO) - item, _ := cfg.ToStackItem() - return item -} - -// getTotalAccounts returns the total number of healthcare accounts. -func (s *Salus) getTotalAccounts(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(s.ID).(*SalusCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.accountCount))) -} - -// getTotalRecords returns the total number of medical records. -func (s *Salus) getTotalRecords(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(s.ID).(*SalusCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.recordCount))) -} - -// getTotalProviders returns the total number of healthcare providers. -func (s *Salus) getTotalProviders(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(s.ID).(*SalusCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.providerCount))) -} - -// ===== Public Interface Methods for Cross-Contract Access ===== - -// GetAccountByOwner returns a healthcare account by owner address. -func (s *Salus) GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.HealthcareAccount, error) { - vitaID, found := s.getVitaIDByOwner(d, owner) - if !found { - return nil, ErrSalusAccountNotFound - } - acc := s.getAccountInternal(d, vitaID) - if acc == nil { - return nil, ErrSalusAccountNotFound - } - return acc, nil -} - -// HasValidAuthorization checks if provider has valid authorization for patient. -func (s *Salus) HasValidAuthorization(d *dao.Simple, patient util.Uint160, provider util.Uint160, blockHeight uint32) bool { - vitaID, found := s.getVitaIDByOwner(d, patient) - if !found { - return false - } - - authID, exists := s.getActiveAuthID(d, vitaID, provider) - if !exists { - return false - } - - auth := s.getAuthInternal(d, authID) - if auth == nil { - return false - } - - return auth.IsValid(blockHeight) -} - -// Address returns the contract's script hash. -func (s *Salus) Address() util.Uint160 { - return s.Hash -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Salus represents the universal healthcare native contract. +type Salus struct { + interop.ContractMD + Tutus ITutus + Vita IVita + RoleRegistry IRoleRegistry + Lex ILex +} + +// SalusCache represents the cached state for Salus contract. +type SalusCache struct { + accountCount uint64 + recordCount uint64 + providerCount uint64 + authorizationCount uint64 + emergencyCount uint64 +} + +// Storage key prefixes for Salus. +const ( + salusPrefixAccount byte = 0x01 // vitaID -> HealthcareAccount + salusPrefixAccountByOwner byte = 0x02 // owner -> vitaID + salusPrefixRecord byte = 0x10 // recordID -> MedicalRecord + salusPrefixRecordByPatient byte = 0x11 // vitaID + recordID -> exists + salusPrefixRecordByProvider byte = 0x12 // provider + recordID -> exists + salusPrefixProvider byte = 0x20 // providerID -> HealthcareProvider + salusPrefixProviderByAddress byte = 0x21 // provider address -> providerID + salusPrefixAuthorization byte = 0x30 // authID -> ProviderAuthorization + salusPrefixAuthByPatient byte = 0x31 // vitaID + authID -> exists + salusPrefixAuthByProvider byte = 0x32 // provider + authID -> exists + salusPrefixActiveAuth byte = 0x33 // vitaID + provider -> authID + salusPrefixEmergencyAccess byte = 0x40 // emergencyID -> EmergencyAccess + salusPrefixEmergencyByPatient byte = 0x41 // vitaID + emergencyID -> exists + salusPrefixAccountCounter byte = 0xF0 // -> uint64 + salusPrefixRecordCounter byte = 0xF1 // -> next record ID + salusPrefixProviderCounter byte = 0xF2 // -> next provider ID + salusPrefixAuthCounter byte = 0xF3 // -> next authorization ID + salusPrefixEmergencyCounter byte = 0xF4 // -> next emergency access ID + salusPrefixConfig byte = 0xFF // -> SalusConfig +) + +// Event names for Salus. +const ( + HealthcareActivatedEvent = "HealthcareActivated" + CreditsAllocatedEventSalus = "CreditsAllocated" + MedicalRecordCreatedEvent = "MedicalRecordCreated" + ProviderRegisteredEvent = "ProviderRegistered" + ProviderSuspendedEvent = "ProviderSuspended" + AuthorizationGrantedEvent = "AuthorizationGranted" + AuthorizationRevokedEvent = "AuthorizationRevoked" + EmergencyAccessGrantedEvent = "EmergencyAccessGranted" + EmergencyAccessReviewedEvent = "EmergencyAccessReviewed" +) + +// Role constants for healthcare providers. +const ( + RoleHealthcare uint64 = 21 // Can record medical events and access authorized records +) + +// Various errors for Salus. +var ( + ErrSalusAccountNotFound = errors.New("healthcare account not found") + ErrSalusAccountExists = errors.New("healthcare account already exists") + ErrSalusAccountSuspended = errors.New("healthcare account is suspended") + ErrSalusAccountClosed = errors.New("healthcare account is closed") + ErrSalusNoVita = errors.New("owner must have an active Vita") + ErrSalusInsufficientCredits = errors.New("insufficient healthcare credits") + ErrSalusInvalidCredits = errors.New("invalid credit amount") + ErrSalusRecordNotFound = errors.New("medical record not found") + ErrSalusProviderNotFound = errors.New("healthcare provider not found") + ErrSalusProviderExists = errors.New("healthcare provider already registered") + ErrSalusProviderSuspended = errors.New("healthcare provider is suspended") + ErrSalusProviderRevoked = errors.New("healthcare provider is revoked") + ErrSalusNotProvider = errors.New("caller is not an authorized healthcare provider") + ErrSalusNotCommittee = errors.New("invalid committee signature") + ErrSalusInvalidOwner = errors.New("invalid owner address") + ErrSalusInvalidProvider = errors.New("invalid provider address") + ErrSalusAuthorizationNotFound = errors.New("authorization not found") + ErrSalusAuthorizationExpired = errors.New("authorization has expired") + ErrSalusAuthorizationExists = errors.New("authorization already exists") + ErrSalusNotPatient = errors.New("caller is not the patient") + ErrSalusHealthcareRestricted = errors.New("healthcare right is restricted") + ErrSalusEmergencyNotFound = errors.New("emergency access not found") + ErrSalusInvalidReason = errors.New("invalid reason") + ErrSalusInvalidName = errors.New("invalid name") + ErrSalusInvalidSpecialty = errors.New("invalid specialty") + ErrSalusNoAccess = errors.New("no access to patient records") + ErrSalusExceedsMaxDuration = errors.New("exceeds maximum authorization duration") +) + +var ( + _ interop.Contract = (*Salus)(nil) + _ dao.NativeContractCache = (*SalusCache)(nil) +) + +// Copy implements NativeContractCache interface. +func (c *SalusCache) Copy() dao.NativeContractCache { + return &SalusCache{ + accountCount: c.accountCount, + recordCount: c.recordCount, + providerCount: c.providerCount, + authorizationCount: c.authorizationCount, + emergencyCount: c.emergencyCount, + } +} + +// checkCommittee checks if the caller has committee authority. +func (s *Salus) checkCommittee(ic *interop.Context) bool { + if s.RoleRegistry != nil { + return s.RoleRegistry.CheckCommittee(ic) + } + return s.Tutus.CheckCommittee(ic) +} + +// checkHealthcareProvider checks if the caller has healthcare provider authority. +func (s *Salus) checkHealthcareProvider(ic *interop.Context) bool { + caller := ic.VM.GetCallingScriptHash() + if s.RoleRegistry != nil { + if s.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleHealthcare, ic.Block.Index) { + return true + } + } + // Committee members can also act as healthcare providers + return s.checkCommittee(ic) +} + +// checkHealthcareRight checks if subject has healthcare rights via Lex. +func (s *Salus) checkHealthcareRight(ic *interop.Context, subject util.Uint160) bool { + if s.Lex == nil { + return true // Allow if Lex not available + } + return s.Lex.HasRightInternal(ic.DAO, subject, state.RightHealthcare, ic.Block.Index) +} + +// newSalus creates a new Salus native contract. +func newSalus() *Salus { + s := &Salus{ + ContractMD: *interop.NewContractMD(nativenames.Salus, nativeids.Salus), + } + defer s.BuildHFSpecificMD(s.ActiveIn()) + + // ===== Account Management ===== + + // activateHealthcare - Activate healthcare account for a Vita holder + desc := NewDescriptor("activateHealthcare", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md := NewMethodAndPrice(s.activateHealthcare, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getAccount - Get healthcare account by owner + desc = NewDescriptor("getAccount", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(s.getAccount, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getAccountByVitaID - Get account by Vita ID + desc = NewDescriptor("getAccountByVitaID", smartcontract.ArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getAccountByVitaID, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // allocateCredits - Allocate healthcare credits (committee only) + desc = NewDescriptor("allocateCredits", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(s.allocateCredits, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getCredits - Get available credits + desc = NewDescriptor("getCredits", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(s.getCredits, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Medical Records ===== + + // recordMedicalEvent - Record a medical event (provider only) + desc = NewDescriptor("recordMedicalEvent", smartcontract.IntegerType, + manifest.NewParameter("patient", smartcontract.Hash160Type), + manifest.NewParameter("recordType", smartcontract.IntegerType), + manifest.NewParameter("contentHash", smartcontract.Hash256Type), + manifest.NewParameter("credits", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.recordMedicalEvent, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getMedicalRecord - Get medical record by ID + desc = NewDescriptor("getMedicalRecord", smartcontract.ArrayType, + manifest.NewParameter("recordID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getMedicalRecord, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Provider Management ===== + + // registerProvider - Register a healthcare provider (committee only) + desc = NewDescriptor("registerProvider", smartcontract.IntegerType, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("specialty", smartcontract.StringType), + manifest.NewParameter("licenseHash", smartcontract.Hash256Type)) + md = NewMethodAndPrice(s.registerProvider, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // suspendProvider - Suspend a healthcare provider (committee only) + desc = NewDescriptor("suspendProvider", smartcontract.BoolType, + manifest.NewParameter("providerID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(s.suspendProvider, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getProvider - Get provider details + desc = NewDescriptor("getProvider", smartcontract.ArrayType, + manifest.NewParameter("providerID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getProvider, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getProviderByAddress - Get provider by address + desc = NewDescriptor("getProviderByAddress", smartcontract.ArrayType, + manifest.NewParameter("address", smartcontract.Hash160Type)) + md = NewMethodAndPrice(s.getProviderByAddress, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Authorization Management ===== + + // authorizeAccess - Grant provider access to patient records + desc = NewDescriptor("authorizeAccess", smartcontract.IntegerType, + manifest.NewParameter("patient", smartcontract.Hash160Type), + manifest.NewParameter("provider", smartcontract.Hash160Type), + manifest.NewParameter("accessLevel", smartcontract.IntegerType), + manifest.NewParameter("duration", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.authorizeAccess, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // revokeAccess - Revoke provider access + desc = NewDescriptor("revokeAccess", smartcontract.BoolType, + manifest.NewParameter("authID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.revokeAccess, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getAuthorization - Get authorization details + desc = NewDescriptor("getAuthorization", smartcontract.ArrayType, + manifest.NewParameter("authID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getAuthorization, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // hasAccess - Check if provider has access to patient + desc = NewDescriptor("hasAccess", smartcontract.BoolType, + manifest.NewParameter("patient", smartcontract.Hash160Type), + manifest.NewParameter("provider", smartcontract.Hash160Type)) + md = NewMethodAndPrice(s.hasAccess, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Emergency Access ===== + + // emergencyAccess - Request emergency access (provider only) + desc = NewDescriptor("emergencyAccess", smartcontract.IntegerType, + manifest.NewParameter("patient", smartcontract.Hash160Type), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(s.emergencyAccess, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // reviewEmergencyAccess - Review emergency access (committee only) + desc = NewDescriptor("reviewEmergencyAccess", smartcontract.BoolType, + manifest.NewParameter("emergencyID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.reviewEmergencyAccess, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getEmergencyAccess - Get emergency access details + desc = NewDescriptor("getEmergencyAccess", smartcontract.ArrayType, + manifest.NewParameter("emergencyID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getEmergencyAccess, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Query Methods ===== + + // getConfig - Get Salus configuration + desc = NewDescriptor("getConfig", smartcontract.ArrayType) + md = NewMethodAndPrice(s.getConfig, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getTotalAccounts - Get total healthcare accounts + desc = NewDescriptor("getTotalAccounts", smartcontract.IntegerType) + md = NewMethodAndPrice(s.getTotalAccounts, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getTotalRecords - Get total medical records + desc = NewDescriptor("getTotalRecords", smartcontract.IntegerType) + md = NewMethodAndPrice(s.getTotalRecords, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getTotalProviders - Get total healthcare providers + desc = NewDescriptor("getTotalProviders", smartcontract.IntegerType) + md = NewMethodAndPrice(s.getTotalProviders, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Events ===== + + // HealthcareActivated event + eDesc := NewEventDescriptor(HealthcareActivatedEvent, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("owner", smartcontract.Hash160Type)) + s.AddEvent(NewEvent(eDesc)) + + // CreditsAllocated event + eDesc = NewEventDescriptor(CreditsAllocatedEventSalus, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("total", smartcontract.IntegerType)) + s.AddEvent(NewEvent(eDesc)) + + // MedicalRecordCreated event + eDesc = NewEventDescriptor(MedicalRecordCreatedEvent, + manifest.NewParameter("recordID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("recordType", smartcontract.IntegerType)) + s.AddEvent(NewEvent(eDesc)) + + // ProviderRegistered event + eDesc = NewEventDescriptor(ProviderRegisteredEvent, + manifest.NewParameter("providerID", smartcontract.IntegerType), + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("specialty", smartcontract.StringType)) + s.AddEvent(NewEvent(eDesc)) + + // ProviderSuspended event + eDesc = NewEventDescriptor(ProviderSuspendedEvent, + manifest.NewParameter("providerID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + s.AddEvent(NewEvent(eDesc)) + + // AuthorizationGranted event + eDesc = NewEventDescriptor(AuthorizationGrantedEvent, + manifest.NewParameter("authID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("provider", smartcontract.Hash160Type)) + s.AddEvent(NewEvent(eDesc)) + + // AuthorizationRevoked event + eDesc = NewEventDescriptor(AuthorizationRevokedEvent, + manifest.NewParameter("authID", smartcontract.IntegerType)) + s.AddEvent(NewEvent(eDesc)) + + // EmergencyAccessGranted event + eDesc = NewEventDescriptor(EmergencyAccessGrantedEvent, + manifest.NewParameter("emergencyID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("provider", smartcontract.Hash160Type)) + s.AddEvent(NewEvent(eDesc)) + + // EmergencyAccessReviewed event + eDesc = NewEventDescriptor(EmergencyAccessReviewedEvent, + manifest.NewParameter("emergencyID", smartcontract.IntegerType)) + s.AddEvent(NewEvent(eDesc)) + + return s +} + +// Metadata returns contract metadata. +func (s *Salus) Metadata() *interop.ContractMD { + return &s.ContractMD +} + +// Initialize initializes the Salus contract. +func (s *Salus) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != s.ActiveIn() { + return nil + } + + // Initialize counters + s.setAccountCounter(ic.DAO, 0) + s.setRecordCounter(ic.DAO, 0) + s.setProviderCounter(ic.DAO, 0) + s.setAuthCounter(ic.DAO, 0) + s.setEmergencyCounter(ic.DAO, 0) + + // Initialize config with defaults + cfg := &state.SalusConfig{ + DefaultAnnualCredits: 10000, // 10000 healthcare credits per year + EmergencyAccessDuration: 86400, // ~24 hours (1-second blocks) + PreventiveCareBonus: 500, // Bonus for preventive care visits + MaxAuthorizationDuration: 2592000, // ~30 days (1-second blocks) + } + s.setConfig(ic.DAO, cfg) + + // Initialize cache + cache := &SalusCache{ + accountCount: 0, + recordCount: 0, + providerCount: 0, + authorizationCount: 0, + emergencyCount: 0, + } + ic.DAO.SetCache(s.ID, cache) + + return nil +} + +// InitializeCache initializes the cache from storage. +func (s *Salus) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cache := &SalusCache{ + accountCount: s.getAccountCounter(d), + recordCount: s.getRecordCounter(d), + providerCount: s.getProviderCounter(d), + authorizationCount: s.getAuthCounter(d), + emergencyCount: s.getEmergencyCounter(d), + } + d.SetCache(s.ID, cache) + return nil +} + +// OnPersist is called before block is committed. +func (s *Salus) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist is called after block is committed. +func (s *Salus) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn returns the hardfork at which this contract is activated. +func (s *Salus) ActiveIn() *config.Hardfork { + return nil // Always active +} + +// ===== Storage Helpers ===== + +func (s *Salus) makeAccountKey(vitaID uint64) []byte { + key := make([]byte, 9) + key[0] = salusPrefixAccount + binary.BigEndian.PutUint64(key[1:], vitaID) + return key +} + +func (s *Salus) makeAccountByOwnerKey(owner util.Uint160) []byte { + key := make([]byte, 21) + key[0] = salusPrefixAccountByOwner + copy(key[1:], owner.BytesBE()) + return key +} + +func (s *Salus) makeRecordKey(recordID uint64) []byte { + key := make([]byte, 9) + key[0] = salusPrefixRecord + binary.BigEndian.PutUint64(key[1:], recordID) + return key +} + +func (s *Salus) makeRecordByPatientKey(vitaID, recordID uint64) []byte { + key := make([]byte, 17) + key[0] = salusPrefixRecordByPatient + binary.BigEndian.PutUint64(key[1:9], vitaID) + binary.BigEndian.PutUint64(key[9:], recordID) + return key +} + +func (s *Salus) makeProviderKey(providerID uint64) []byte { + key := make([]byte, 9) + key[0] = salusPrefixProvider + binary.BigEndian.PutUint64(key[1:], providerID) + return key +} + +func (s *Salus) makeProviderByAddressKey(address util.Uint160) []byte { + key := make([]byte, 21) + key[0] = salusPrefixProviderByAddress + copy(key[1:], address.BytesBE()) + return key +} + +func (s *Salus) makeAuthorizationKey(authID uint64) []byte { + key := make([]byte, 9) + key[0] = salusPrefixAuthorization + binary.BigEndian.PutUint64(key[1:], authID) + return key +} + +func (s *Salus) makeActiveAuthKey(vitaID uint64, provider util.Uint160) []byte { + key := make([]byte, 29) + key[0] = salusPrefixActiveAuth + binary.BigEndian.PutUint64(key[1:9], vitaID) + copy(key[9:], provider.BytesBE()) + return key +} + +func (s *Salus) makeEmergencyKey(emergencyID uint64) []byte { + key := make([]byte, 9) + key[0] = salusPrefixEmergencyAccess + binary.BigEndian.PutUint64(key[1:], emergencyID) + return key +} + +// Counter getters/setters +func (s *Salus) getAccountCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{salusPrefixAccountCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Salus) setAccountCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{salusPrefixAccountCounter}, buf) +} + +func (s *Salus) getRecordCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{salusPrefixRecordCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Salus) setRecordCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{salusPrefixRecordCounter}, buf) +} + +func (s *Salus) getProviderCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{salusPrefixProviderCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Salus) setProviderCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{salusPrefixProviderCounter}, buf) +} + +func (s *Salus) getAuthCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{salusPrefixAuthCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Salus) setAuthCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{salusPrefixAuthCounter}, buf) +} + +func (s *Salus) getEmergencyCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{salusPrefixEmergencyCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Salus) setEmergencyCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{salusPrefixEmergencyCounter}, buf) +} + +// Config getter/setter +func (s *Salus) getConfigInternal(d *dao.Simple) *state.SalusConfig { + si := d.GetStorageItem(s.ID, []byte{salusPrefixConfig}) + if si == nil { + return &state.SalusConfig{ + DefaultAnnualCredits: 10000, + EmergencyAccessDuration: 86400, + PreventiveCareBonus: 500, + MaxAuthorizationDuration: 2592000, + } + } + cfg := new(state.SalusConfig) + item, _ := stackitem.Deserialize(si) + cfg.FromStackItem(item) + return cfg +} + +func (s *Salus) setConfig(d *dao.Simple, cfg *state.SalusConfig) { + item, _ := cfg.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, []byte{salusPrefixConfig}, data) +} + +// Account storage +func (s *Salus) getAccountInternal(d *dao.Simple, vitaID uint64) *state.HealthcareAccount { + si := d.GetStorageItem(s.ID, s.makeAccountKey(vitaID)) + if si == nil { + return nil + } + acc := new(state.HealthcareAccount) + item, _ := stackitem.Deserialize(si) + acc.FromStackItem(item) + return acc +} + +func (s *Salus) putAccount(d *dao.Simple, acc *state.HealthcareAccount) { + item, _ := acc.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeAccountKey(acc.VitaID), data) +} + +func (s *Salus) getVitaIDByOwner(d *dao.Simple, owner util.Uint160) (uint64, bool) { + si := d.GetStorageItem(s.ID, s.makeAccountByOwnerKey(owner)) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint64(si), true +} + +func (s *Salus) setOwnerToVitaID(d *dao.Simple, owner util.Uint160, vitaID uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, vitaID) + d.PutStorageItem(s.ID, s.makeAccountByOwnerKey(owner), buf) +} + +// Record storage +func (s *Salus) getRecordInternal(d *dao.Simple, recordID uint64) *state.MedicalRecord { + si := d.GetStorageItem(s.ID, s.makeRecordKey(recordID)) + if si == nil { + return nil + } + record := new(state.MedicalRecord) + item, _ := stackitem.Deserialize(si) + record.FromStackItem(item) + return record +} + +func (s *Salus) putRecord(d *dao.Simple, record *state.MedicalRecord) { + item, _ := record.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeRecordKey(record.ID), data) +} + +func (s *Salus) setRecordByPatient(d *dao.Simple, vitaID, recordID uint64) { + d.PutStorageItem(s.ID, s.makeRecordByPatientKey(vitaID, recordID), []byte{1}) +} + +// Provider storage +func (s *Salus) getProviderInternal(d *dao.Simple, providerID uint64) *state.HealthcareProvider { + si := d.GetStorageItem(s.ID, s.makeProviderKey(providerID)) + if si == nil { + return nil + } + provider := new(state.HealthcareProvider) + item, _ := stackitem.Deserialize(si) + provider.FromStackItem(item) + return provider +} + +func (s *Salus) putProvider(d *dao.Simple, provider *state.HealthcareProvider) { + item, _ := provider.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeProviderKey(provider.ProviderID), data) +} + +func (s *Salus) getProviderIDByAddress(d *dao.Simple, address util.Uint160) (uint64, bool) { + si := d.GetStorageItem(s.ID, s.makeProviderByAddressKey(address)) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint64(si), true +} + +func (s *Salus) setProviderByAddress(d *dao.Simple, address util.Uint160, providerID uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, providerID) + d.PutStorageItem(s.ID, s.makeProviderByAddressKey(address), buf) +} + +// Authorization storage +func (s *Salus) getAuthInternal(d *dao.Simple, authID uint64) *state.ProviderAuthorization { + si := d.GetStorageItem(s.ID, s.makeAuthorizationKey(authID)) + if si == nil { + return nil + } + auth := new(state.ProviderAuthorization) + item, _ := stackitem.Deserialize(si) + auth.FromStackItem(item) + return auth +} + +func (s *Salus) putAuth(d *dao.Simple, auth *state.ProviderAuthorization) { + item, _ := auth.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeAuthorizationKey(auth.ID), data) +} + +func (s *Salus) getActiveAuthID(d *dao.Simple, vitaID uint64, provider util.Uint160) (uint64, bool) { + si := d.GetStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider)) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint64(si), true +} + +func (s *Salus) setActiveAuthID(d *dao.Simple, vitaID uint64, provider util.Uint160, authID uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, authID) + d.PutStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider), buf) +} + +func (s *Salus) clearActiveAuth(d *dao.Simple, vitaID uint64, provider util.Uint160) { + d.DeleteStorageItem(s.ID, s.makeActiveAuthKey(vitaID, provider)) +} + +// Emergency access storage +func (s *Salus) getEmergencyInternal(d *dao.Simple, emergencyID uint64) *state.EmergencyAccess { + si := d.GetStorageItem(s.ID, s.makeEmergencyKey(emergencyID)) + if si == nil { + return nil + } + emergency := new(state.EmergencyAccess) + item, _ := stackitem.Deserialize(si) + emergency.FromStackItem(item) + return emergency +} + +func (s *Salus) putEmergency(d *dao.Simple, emergency *state.EmergencyAccess) { + item, _ := emergency.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeEmergencyKey(emergency.ID), data) +} + +// ===== Contract Methods ===== + +// activateHealthcare activates healthcare account for a Vita holder. +func (s *Salus) activateHealthcare(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + // Check owner has active Vita + if s.Vita == nil { + panic(ErrSalusNoVita) + } + vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) + if err != nil || vita == nil { + panic(ErrSalusNoVita) + } + if vita.Status != state.TokenStatusActive { + panic(ErrSalusNoVita) + } + + // Check if account already exists + existing := s.getAccountInternal(ic.DAO, vita.TokenID) + if existing != nil { + panic(ErrSalusAccountExists) + } + + // Check healthcare rights + if !s.checkHealthcareRight(ic, owner) { + // Log but allow (EnforcementLogging) + } + + // Get cache and increment counter + cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) + cache.accountCount++ + s.setAccountCounter(ic.DAO, cache.accountCount) + + // Get default credits from config + cfg := s.getConfigInternal(ic.DAO) + + // Create account + acc := &state.HealthcareAccount{ + VitaID: vita.TokenID, + Owner: owner, + AnnualAllocation: cfg.DefaultAnnualCredits, + CreditsUsed: 0, + CreditsAvailable: cfg.DefaultAnnualCredits, + BiologicalAge: 0, + LastCheckup: 0, + Status: state.HealthcareAccountActive, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + + // Store account + s.putAccount(ic.DAO, acc) + s.setOwnerToVitaID(ic.DAO, owner, vita.TokenID) + + // Emit event + ic.AddNotification(s.Hash, HealthcareActivatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), + stackitem.NewByteArray(owner.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +// getAccount returns healthcare account by owner. +func (s *Salus) getAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.Null{} + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + return stackitem.Null{} + } + + item, _ := acc.ToStackItem() + return item +} + +// getAccountByVitaID returns healthcare account by Vita ID. +func (s *Salus) getAccountByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + return stackitem.Null{} + } + + item, _ := acc.ToStackItem() + return item +} + +// allocateCredits allocates healthcare credits to an account (committee only). +func (s *Salus) allocateCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + amount := toUint64(args[1]) + // reason := toString(args[2]) // for logging + + // Committee only + if !s.checkCommittee(ic) { + panic(ErrSalusNotCommittee) + } + + if amount == 0 { + panic(ErrSalusInvalidCredits) + } + + // Get or create account + vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) + if !found { + // Auto-create account if Vita exists + if s.Vita == nil { + panic(ErrSalusNoVita) + } + vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) + if err != nil || vita == nil || vita.Status != state.TokenStatusActive { + panic(ErrSalusNoVita) + } + vitaID = vita.TokenID + + cfg := s.getConfigInternal(ic.DAO) + + // Create account + cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) + cache.accountCount++ + s.setAccountCounter(ic.DAO, cache.accountCount) + + acc := &state.HealthcareAccount{ + VitaID: vitaID, + Owner: owner, + AnnualAllocation: cfg.DefaultAnnualCredits, + CreditsUsed: 0, + CreditsAvailable: amount, + BiologicalAge: 0, + LastCheckup: 0, + Status: state.HealthcareAccountActive, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + s.putAccount(ic.DAO, acc) + s.setOwnerToVitaID(ic.DAO, owner, vitaID) + + // Emit events + ic.AddNotification(s.Hash, HealthcareActivatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewByteArray(owner.BytesBE()), + })) + ic.AddNotification(s.Hash, CreditsAllocatedEventSalus, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(amount))), + stackitem.NewBigInteger(big.NewInt(int64(amount))), + })) + + return stackitem.NewBool(true) + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + panic(ErrSalusAccountNotFound) + } + if acc.Status != state.HealthcareAccountActive { + panic(ErrSalusAccountSuspended) + } + + // Add credits + acc.CreditsAvailable += amount + acc.UpdatedAt = ic.Block.Index + + s.putAccount(ic.DAO, acc) + + // Emit event + ic.AddNotification(s.Hash, CreditsAllocatedEventSalus, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(amount))), + stackitem.NewBigInteger(big.NewInt(int64(acc.CreditsAvailable))), + })) + + return stackitem.NewBool(true) +} + +// getCredits returns available credits for an owner. +func (s *Salus) getCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + return stackitem.NewBigInteger(big.NewInt(int64(acc.CreditsAvailable))) +} + +// recordMedicalEvent records a medical event (provider only). +func (s *Salus) recordMedicalEvent(ic *interop.Context, args []stackitem.Item) stackitem.Item { + patient := toUint160(args[0]) + recordType := state.MedicalRecordType(toUint64(args[1])) + contentHashBytes := toBytes(args[2]) + credits := toUint64(args[3]) + + // Convert bytes to Uint256 + var contentHash util.Uint256 + if len(contentHashBytes) == 32 { + copy(contentHash[:], contentHashBytes) + } + + // Check provider authority + if !s.checkHealthcareProvider(ic) { + panic(ErrSalusNotProvider) + } + + // Get provider address + provider := ic.VM.GetCallingScriptHash() + + // Get patient's Vita + if s.Vita == nil { + panic(ErrSalusNoVita) + } + vita, err := s.Vita.GetTokenByOwner(ic.DAO, patient) + if err != nil || vita == nil || vita.Status != state.TokenStatusActive { + panic(ErrSalusNoVita) + } + + // Check provider has access (unless emergency or authorized) + vitaID, found := s.getVitaIDByOwner(ic.DAO, patient) + if found { + authID, hasAuth := s.getActiveAuthID(ic.DAO, vitaID, provider) + if hasAuth { + auth := s.getAuthInternal(ic.DAO, authID) + if auth == nil || !auth.IsValid(ic.Block.Index) { + panic(ErrSalusNoAccess) + } + } + // If no auth, still allow (emergency can be logged separately) + } + + // Get or auto-create account + if !found { + // Auto-create + cfg := s.getConfigInternal(ic.DAO) + cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) + cache.accountCount++ + s.setAccountCounter(ic.DAO, cache.accountCount) + + acc := &state.HealthcareAccount{ + VitaID: vita.TokenID, + Owner: patient, + AnnualAllocation: cfg.DefaultAnnualCredits, + CreditsUsed: 0, + CreditsAvailable: cfg.DefaultAnnualCredits, + BiologicalAge: 0, + LastCheckup: 0, + Status: state.HealthcareAccountActive, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + s.putAccount(ic.DAO, acc) + s.setOwnerToVitaID(ic.DAO, patient, vita.TokenID) + vitaID = vita.TokenID + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + panic(ErrSalusAccountNotFound) + } + if acc.Status != state.HealthcareAccountActive { + panic(ErrSalusAccountSuspended) + } + + // Check sufficient credits + if acc.CreditsAvailable < credits { + panic(ErrSalusInsufficientCredits) + } + + // Get next record ID + cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) + recordID := cache.recordCount + cache.recordCount++ + s.setRecordCounter(ic.DAO, cache.recordCount) + + // Deduct credits + acc.CreditsUsed += credits + acc.CreditsAvailable -= credits + acc.UpdatedAt = ic.Block.Index + + // Update last checkup if appropriate + if recordType == state.RecordTypeCheckup || recordType == state.RecordTypePreventive { + acc.LastCheckup = ic.Block.Index + } + + s.putAccount(ic.DAO, acc) + + // Create record + record := &state.MedicalRecord{ + ID: recordID, + VitaID: vitaID, + Patient: patient, + Provider: provider, + RecordType: recordType, + ContentHash: contentHash, + CreditsUsed: credits, + CreatedAt: ic.Block.Index, + IsActive: true, + } + + s.putRecord(ic.DAO, record) + s.setRecordByPatient(ic.DAO, vitaID, recordID) + + // Emit event + ic.AddNotification(s.Hash, MedicalRecordCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(recordID))), + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(recordType))), + })) + + return stackitem.NewBigInteger(big.NewInt(int64(recordID))) +} + +// getMedicalRecord returns medical record by ID. +func (s *Salus) getMedicalRecord(ic *interop.Context, args []stackitem.Item) stackitem.Item { + recordID := toUint64(args[0]) + + record := s.getRecordInternal(ic.DAO, recordID) + if record == nil { + return stackitem.Null{} + } + + item, _ := record.ToStackItem() + return item +} + +// registerProvider registers a healthcare provider (committee only). +func (s *Salus) registerProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + name := toString(args[1]) + specialty := toString(args[2]) + licenseHashBytes := toBytes(args[3]) + + // Convert bytes to Uint256 + var licenseHash util.Uint256 + if len(licenseHashBytes) == 32 { + copy(licenseHash[:], licenseHashBytes) + } + + // Committee only + if !s.checkCommittee(ic) { + panic(ErrSalusNotCommittee) + } + + // Validate inputs + if len(name) == 0 || len(name) > 128 { + panic(ErrSalusInvalidName) + } + if len(specialty) == 0 || len(specialty) > 64 { + panic(ErrSalusInvalidSpecialty) + } + + // Check if provider already exists + _, exists := s.getProviderIDByAddress(ic.DAO, address) + if exists { + panic(ErrSalusProviderExists) + } + + // Get next provider ID + cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) + providerID := cache.providerCount + cache.providerCount++ + s.setProviderCounter(ic.DAO, cache.providerCount) + + // Create provider + provider := &state.HealthcareProvider{ + Address: address, + Name: name, + ProviderID: providerID, + Specialty: specialty, + LicenseHash: licenseHash, + Status: state.ProviderStatusActive, + RegisteredAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + + s.putProvider(ic.DAO, provider) + s.setProviderByAddress(ic.DAO, address, providerID) + + // Emit event + ic.AddNotification(s.Hash, ProviderRegisteredEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(providerID))), + stackitem.NewByteArray(address.BytesBE()), + stackitem.NewByteArray([]byte(specialty)), + })) + + return stackitem.NewBigInteger(big.NewInt(int64(providerID))) +} + +// suspendProvider suspends a healthcare provider (committee only). +func (s *Salus) suspendProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { + providerID := toUint64(args[0]) + reason := toString(args[1]) + + // Committee only + if !s.checkCommittee(ic) { + panic(ErrSalusNotCommittee) + } + + provider := s.getProviderInternal(ic.DAO, providerID) + if provider == nil { + panic(ErrSalusProviderNotFound) + } + if provider.Status == state.ProviderStatusRevoked { + panic(ErrSalusProviderRevoked) + } + + // Suspend + provider.Status = state.ProviderStatusSuspended + provider.UpdatedAt = ic.Block.Index + s.putProvider(ic.DAO, provider) + + // Emit event + ic.AddNotification(s.Hash, ProviderSuspendedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(providerID))), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +// getProvider returns provider details. +func (s *Salus) getProvider(ic *interop.Context, args []stackitem.Item) stackitem.Item { + providerID := toUint64(args[0]) + + provider := s.getProviderInternal(ic.DAO, providerID) + if provider == nil { + return stackitem.Null{} + } + + item, _ := provider.ToStackItem() + return item +} + +// getProviderByAddress returns provider by address. +func (s *Salus) getProviderByAddress(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + + providerID, found := s.getProviderIDByAddress(ic.DAO, address) + if !found { + return stackitem.Null{} + } + + provider := s.getProviderInternal(ic.DAO, providerID) + if provider == nil { + return stackitem.Null{} + } + + item, _ := provider.ToStackItem() + return item +} + +// authorizeAccess grants provider access to patient records. +func (s *Salus) authorizeAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { + patient := toUint160(args[0]) + provider := toUint160(args[1]) + accessLevel := state.AccessLevel(toUint64(args[2])) + duration := toUint32(args[3]) + + // Get patient's Vita and account + vitaID, found := s.getVitaIDByOwner(ic.DAO, patient) + if !found { + panic(ErrSalusAccountNotFound) + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + panic(ErrSalusAccountNotFound) + } + if acc.Status != state.HealthcareAccountActive { + panic(ErrSalusAccountSuspended) + } + + // Check caller is patient (self-authorization) + caller := ic.VM.GetCallingScriptHash() + if caller != patient && !s.checkCommittee(ic) { + panic(ErrSalusNotPatient) + } + + // Check max duration + cfg := s.getConfigInternal(ic.DAO) + if duration > cfg.MaxAuthorizationDuration { + panic(ErrSalusExceedsMaxDuration) + } + + // Check if authorization already exists + existingAuthID, exists := s.getActiveAuthID(ic.DAO, vitaID, provider) + if exists { + existingAuth := s.getAuthInternal(ic.DAO, existingAuthID) + if existingAuth != nil && existingAuth.IsValid(ic.Block.Index) { + panic(ErrSalusAuthorizationExists) + } + } + + // Get next auth ID + cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) + authID := cache.authorizationCount + cache.authorizationCount++ + s.setAuthCounter(ic.DAO, cache.authorizationCount) + + // Calculate expiry + expiresAt := uint32(0) + if duration > 0 { + expiresAt = ic.Block.Index + duration + } + + // Create authorization + auth := &state.ProviderAuthorization{ + ID: authID, + VitaID: vitaID, + Patient: patient, + Provider: provider, + AccessLevel: accessLevel, + StartsAt: ic.Block.Index, + ExpiresAt: expiresAt, + IsActive: true, + GrantedAt: ic.Block.Index, + } + + s.putAuth(ic.DAO, auth) + s.setActiveAuthID(ic.DAO, vitaID, provider, authID) + + // Emit event + ic.AddNotification(s.Hash, AuthorizationGrantedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(authID))), + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewByteArray(provider.BytesBE()), + })) + + return stackitem.NewBigInteger(big.NewInt(int64(authID))) +} + +// revokeAccess revokes provider access. +func (s *Salus) revokeAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { + authID := toUint64(args[0]) + + auth := s.getAuthInternal(ic.DAO, authID) + if auth == nil { + panic(ErrSalusAuthorizationNotFound) + } + + // Check caller is patient or committee + caller := ic.VM.GetCallingScriptHash() + if caller != auth.Patient && !s.checkCommittee(ic) { + panic(ErrSalusNotPatient) + } + + // Revoke + auth.IsActive = false + s.putAuth(ic.DAO, auth) + s.clearActiveAuth(ic.DAO, auth.VitaID, auth.Provider) + + // Emit event + ic.AddNotification(s.Hash, AuthorizationRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(authID))), + })) + + return stackitem.NewBool(true) +} + +// getAuthorization returns authorization details. +func (s *Salus) getAuthorization(ic *interop.Context, args []stackitem.Item) stackitem.Item { + authID := toUint64(args[0]) + + auth := s.getAuthInternal(ic.DAO, authID) + if auth == nil { + return stackitem.Null{} + } + + item, _ := auth.ToStackItem() + return item +} + +// hasAccess checks if provider has access to patient. +func (s *Salus) hasAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { + patient := toUint160(args[0]) + provider := toUint160(args[1]) + + vitaID, found := s.getVitaIDByOwner(ic.DAO, patient) + if !found { + return stackitem.NewBool(false) + } + + authID, exists := s.getActiveAuthID(ic.DAO, vitaID, provider) + if !exists { + return stackitem.NewBool(false) + } + + auth := s.getAuthInternal(ic.DAO, authID) + if auth == nil { + return stackitem.NewBool(false) + } + + return stackitem.NewBool(auth.IsValid(ic.Block.Index)) +} + +// emergencyAccess requests emergency access to patient records. +func (s *Salus) emergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { + patient := toUint160(args[0]) + reason := toString(args[1]) + + // Check provider authority + if !s.checkHealthcareProvider(ic) { + panic(ErrSalusNotProvider) + } + + if len(reason) == 0 || len(reason) > 256 { + panic(ErrSalusInvalidReason) + } + + // Get provider address + provider := ic.VM.GetCallingScriptHash() + + // Get patient's Vita + if s.Vita == nil { + panic(ErrSalusNoVita) + } + vita, err := s.Vita.GetTokenByOwner(ic.DAO, patient) + if err != nil || vita == nil || vita.Status != state.TokenStatusActive { + panic(ErrSalusNoVita) + } + + // Get config for emergency duration + cfg := s.getConfigInternal(ic.DAO) + + // Get next emergency ID + cache := ic.DAO.GetRWCache(s.ID).(*SalusCache) + emergencyID := cache.emergencyCount + cache.emergencyCount++ + s.setEmergencyCounter(ic.DAO, cache.emergencyCount) + + // Create emergency access + emergency := &state.EmergencyAccess{ + ID: emergencyID, + VitaID: vita.TokenID, + Patient: patient, + Provider: provider, + Reason: reason, + GrantedAt: ic.Block.Index, + ExpiresAt: ic.Block.Index + cfg.EmergencyAccessDuration, + WasReviewed: false, + } + + s.putEmergency(ic.DAO, emergency) + + // Emit event + ic.AddNotification(s.Hash, EmergencyAccessGrantedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(emergencyID))), + stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), + stackitem.NewByteArray(provider.BytesBE()), + })) + + return stackitem.NewBigInteger(big.NewInt(int64(emergencyID))) +} + +// reviewEmergencyAccess marks emergency access as reviewed (committee only). +func (s *Salus) reviewEmergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { + emergencyID := toUint64(args[0]) + + // Committee only + if !s.checkCommittee(ic) { + panic(ErrSalusNotCommittee) + } + + emergency := s.getEmergencyInternal(ic.DAO, emergencyID) + if emergency == nil { + panic(ErrSalusEmergencyNotFound) + } + + // Mark as reviewed + emergency.WasReviewed = true + s.putEmergency(ic.DAO, emergency) + + // Emit event + ic.AddNotification(s.Hash, EmergencyAccessReviewedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(emergencyID))), + })) + + return stackitem.NewBool(true) +} + +// getEmergencyAccess returns emergency access details. +func (s *Salus) getEmergencyAccess(ic *interop.Context, args []stackitem.Item) stackitem.Item { + emergencyID := toUint64(args[0]) + + emergency := s.getEmergencyInternal(ic.DAO, emergencyID) + if emergency == nil { + return stackitem.Null{} + } + + item, _ := emergency.ToStackItem() + return item +} + +// getConfig returns the Salus configuration. +func (s *Salus) getConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cfg := s.getConfigInternal(ic.DAO) + item, _ := cfg.ToStackItem() + return item +} + +// getTotalAccounts returns the total number of healthcare accounts. +func (s *Salus) getTotalAccounts(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(s.ID).(*SalusCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.accountCount))) +} + +// getTotalRecords returns the total number of medical records. +func (s *Salus) getTotalRecords(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(s.ID).(*SalusCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.recordCount))) +} + +// getTotalProviders returns the total number of healthcare providers. +func (s *Salus) getTotalProviders(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(s.ID).(*SalusCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.providerCount))) +} + +// ===== Public Interface Methods for Cross-Contract Access ===== + +// GetAccountByOwner returns a healthcare account by owner address. +func (s *Salus) GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.HealthcareAccount, error) { + vitaID, found := s.getVitaIDByOwner(d, owner) + if !found { + return nil, ErrSalusAccountNotFound + } + acc := s.getAccountInternal(d, vitaID) + if acc == nil { + return nil, ErrSalusAccountNotFound + } + return acc, nil +} + +// HasValidAuthorization checks if provider has valid authorization for patient. +func (s *Salus) HasValidAuthorization(d *dao.Simple, patient util.Uint160, provider util.Uint160, blockHeight uint32) bool { + vitaID, found := s.getVitaIDByOwner(d, patient) + if !found { + return false + } + + authID, exists := s.getActiveAuthID(d, vitaID, provider) + if !exists { + return false + } + + auth := s.getAuthInternal(d, authID) + if auth == nil { + return false + } + + return auth.IsValid(blockHeight) +} + +// Address returns the contract's script hash. +func (s *Salus) Address() util.Uint160 { + return s.Hash +} diff --git a/pkg/core/native/scire.go b/pkg/core/native/scire.go index 2abcf20..afda537 100644 --- a/pkg/core/native/scire.go +++ b/pkg/core/native/scire.go @@ -1,1277 +1,1277 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Scire represents the universal education native contract. -type Scire struct { - interop.ContractMD - Tutus ITutus - Vita IVita - RoleRegistry IRoleRegistry - Lex ILex -} - -// ScireCache represents the cached state for Scire contract. -type ScireCache struct { - accountCount uint64 - certCount uint64 - enrollCount uint64 -} - -// Storage key prefixes for Scire. -const ( - scirePrefixAccount byte = 0x01 // vitaID -> EducationAccount - scirePrefixAccountByOwner byte = 0x02 // owner -> vitaID - scirePrefixCertification byte = 0x10 // certID -> Certification - scirePrefixCertByOwner byte = 0x11 // vitaID + certID -> exists - scirePrefixCertByInstitution byte = 0x12 // institution + certID -> exists - scirePrefixCertByType byte = 0x13 // certType hash + certID -> exists - scirePrefixEnrollment byte = 0x20 // enrollmentID -> Enrollment - scirePrefixEnrollByStudent byte = 0x21 // vitaID + enrollmentID -> exists - scirePrefixEnrollByProgram byte = 0x22 // programID hash + enrollmentID -> exists - scirePrefixActiveEnrollment byte = 0x23 // vitaID -> active enrollmentID - scirePrefixAccountCounter byte = 0xF0 // -> uint64 - scirePrefixCertCounter byte = 0xF1 // -> next certification ID - scirePrefixEnrollCounter byte = 0xF2 // -> next enrollment ID - scirePrefixConfig byte = 0xFF // -> ScireConfig -) - -// Event names for Scire. -const ( - AccountCreatedEvent = "AccountCreated" - CreditsAllocatedEvent = "CreditsAllocated" - EnrollmentCreatedEvent = "EnrollmentCreated" - EnrollmentCompletedEvent = "EnrollmentCompleted" - EnrollmentWithdrawnEvent = "EnrollmentWithdrawn" - EnrollmentTransferredEvent = "EnrollmentTransferred" - CertificationIssuedEvent = "CertificationIssued" - CertificationRevokedEvent = "CertificationRevoked" - CertificationRenewedEvent = "CertificationRenewed" -) - -// Role constants for educators. -const ( - RoleEducator uint64 = 20 // Can issue certifications and manage enrollments -) - -// Various errors for Scire. -var ( - ErrScireAccountNotFound = errors.New("education account not found") - ErrScireAccountExists = errors.New("education account already exists") - ErrScireAccountSuspended = errors.New("education account is suspended") - ErrScireAccountClosed = errors.New("education account is closed") - ErrScireNoVita = errors.New("owner must have an active Vita") - ErrScireInsufficientCredits = errors.New("insufficient education credits") - ErrScireInvalidCredits = errors.New("invalid credit amount") - ErrScireCertNotFound = errors.New("certification not found") - ErrScireCertExpired = errors.New("certification has expired") - ErrScireCertRevoked = errors.New("certification is revoked") - ErrScireEnrollNotFound = errors.New("enrollment not found") - ErrScireEnrollNotActive = errors.New("enrollment is not active") - ErrScireAlreadyEnrolled = errors.New("already enrolled in a program") - ErrScireNotEducator = errors.New("caller is not an authorized educator") - ErrScireNotCommittee = errors.New("invalid committee signature") - ErrScireInvalidOwner = errors.New("invalid owner address") - ErrScireInvalidInstitution = errors.New("invalid institution address") - ErrScireInvalidProgramID = errors.New("invalid program ID") - ErrScireInvalidCertType = errors.New("invalid certification type") - ErrScireInvalidName = errors.New("invalid certification name") - ErrScireEducationRestricted = errors.New("education right is restricted") - ErrScireNotStudent = errors.New("caller is not the student") - ErrScireNotInstitution = errors.New("caller is not the institution") - ErrScireExceedsMaxCredits = errors.New("exceeds maximum credits per program") -) - -var ( - _ interop.Contract = (*Scire)(nil) - _ dao.NativeContractCache = (*ScireCache)(nil) -) - -// Copy implements NativeContractCache interface. -func (c *ScireCache) Copy() dao.NativeContractCache { - return &ScireCache{ - accountCount: c.accountCount, - certCount: c.certCount, - enrollCount: c.enrollCount, - } -} - -// checkCommittee checks if the caller has committee authority. -func (s *Scire) checkCommittee(ic *interop.Context) bool { - if s.RoleRegistry != nil { - return s.RoleRegistry.CheckCommittee(ic) - } - return s.Tutus.CheckCommittee(ic) -} - -// checkEducator checks if the caller has educator authority. -func (s *Scire) checkEducator(ic *interop.Context) bool { - caller := ic.VM.GetCallingScriptHash() - if s.RoleRegistry != nil { - if s.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleEducator, ic.Block.Index) { - return true - } - } - // Committee members can also act as educators - return s.checkCommittee(ic) -} - -// checkEducationRight checks if subject has education rights via Lex. -func (s *Scire) checkEducationRight(ic *interop.Context, subject util.Uint160) bool { - if s.Lex == nil { - return true // Allow if Lex not available - } - return s.Lex.HasRightInternal(ic.DAO, subject, state.RightEducation, ic.Block.Index) -} - -// newScire creates a new Scire native contract. -func newScire() *Scire { - s := &Scire{ - ContractMD: *interop.NewContractMD(nativenames.Scire, nativeids.Scire), - } - defer s.BuildHFSpecificMD(s.ActiveIn()) - - // ===== Account Management ===== - - // createAccount - Create education account for a Vita holder - desc := NewDescriptor("createAccount", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md := NewMethodAndPrice(s.createAccount, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getAccount - Get education account by owner - desc = NewDescriptor("getAccount", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(s.getAccount, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getAccountByVitaID - Get account by Vita ID - desc = NewDescriptor("getAccountByVitaID", smartcontract.ArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getAccountByVitaID, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // allocateCredits - Allocate learning credits (committee only) - desc = NewDescriptor("allocateCredits", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(s.allocateCredits, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getCredits - Get available credits - desc = NewDescriptor("getCredits", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(s.getCredits, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Enrollment Management ===== - - // enroll - Enroll in an education program - desc = NewDescriptor("enroll", smartcontract.IntegerType, - manifest.NewParameter("student", smartcontract.Hash160Type), - manifest.NewParameter("programID", smartcontract.StringType), - manifest.NewParameter("institution", smartcontract.Hash160Type), - manifest.NewParameter("credits", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.enroll, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // completeEnrollment - Mark enrollment as completed (institution only) - desc = NewDescriptor("completeEnrollment", smartcontract.BoolType, - manifest.NewParameter("enrollmentID", smartcontract.IntegerType), - manifest.NewParameter("contentHash", smartcontract.Hash256Type)) - md = NewMethodAndPrice(s.completeEnrollment, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // withdrawEnrollment - Withdraw from program - desc = NewDescriptor("withdrawEnrollment", smartcontract.BoolType, - manifest.NewParameter("enrollmentID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(s.withdrawEnrollment, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getEnrollment - Get enrollment details - desc = NewDescriptor("getEnrollment", smartcontract.ArrayType, - manifest.NewParameter("enrollmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getEnrollment, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getActiveEnrollment - Get student's active enrollment - desc = NewDescriptor("getActiveEnrollment", smartcontract.ArrayType, - manifest.NewParameter("student", smartcontract.Hash160Type)) - md = NewMethodAndPrice(s.getActiveEnrollment, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Certification Management ===== - - // issueCertification - Issue a certification (educator only) - desc = NewDescriptor("issueCertification", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("certType", smartcontract.StringType), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("contentHash", smartcontract.Hash256Type), - manifest.NewParameter("expiresAt", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.issueCertification, 1<<17, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // revokeCertification - Revoke a certification (institution only) - desc = NewDescriptor("revokeCertification", smartcontract.BoolType, - manifest.NewParameter("certID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(s.revokeCertification, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // renewCertification - Extend certification expiry - desc = NewDescriptor("renewCertification", smartcontract.BoolType, - manifest.NewParameter("certID", smartcontract.IntegerType), - manifest.NewParameter("newExpiresAt", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.renewCertification, 1<<16, callflag.States|callflag.AllowNotify) - s.AddMethod(md, desc) - - // getCertification - Get certification details - desc = NewDescriptor("getCertification", smartcontract.ArrayType, - manifest.NewParameter("certID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.getCertification, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // verifyCertification - Check if certification is valid - desc = NewDescriptor("verifyCertification", smartcontract.BoolType, - manifest.NewParameter("certID", smartcontract.IntegerType)) - md = NewMethodAndPrice(s.verifyCertification, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // hasCertification - Check if owner has specific cert type - desc = NewDescriptor("hasCertification", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("certType", smartcontract.StringType)) - md = NewMethodAndPrice(s.hasCertification, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Query Methods ===== - - // getConfig - Get Scire configuration - desc = NewDescriptor("getConfig", smartcontract.ArrayType) - md = NewMethodAndPrice(s.getConfig, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getTotalAccounts - Get total education accounts - desc = NewDescriptor("getTotalAccounts", smartcontract.IntegerType) - md = NewMethodAndPrice(s.getTotalAccounts, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getTotalCertifications - Get total certifications issued - desc = NewDescriptor("getTotalCertifications", smartcontract.IntegerType) - md = NewMethodAndPrice(s.getTotalCertifications, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // getTotalEnrollments - Get total enrollments - desc = NewDescriptor("getTotalEnrollments", smartcontract.IntegerType) - md = NewMethodAndPrice(s.getTotalEnrollments, 1<<15, callflag.ReadStates) - s.AddMethod(md, desc) - - // ===== Events ===== - - // AccountCreated event - eDesc := NewEventDescriptor(AccountCreatedEvent, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("owner", smartcontract.Hash160Type)) - s.AddEvent(NewEvent(eDesc)) - - // CreditsAllocated event - eDesc = NewEventDescriptor(CreditsAllocatedEvent, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("total", smartcontract.IntegerType)) - s.AddEvent(NewEvent(eDesc)) - - // EnrollmentCreated event - eDesc = NewEventDescriptor(EnrollmentCreatedEvent, - manifest.NewParameter("enrollmentID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("programID", smartcontract.StringType)) - s.AddEvent(NewEvent(eDesc)) - - // EnrollmentCompleted event - eDesc = NewEventDescriptor(EnrollmentCompletedEvent, - manifest.NewParameter("enrollmentID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType)) - s.AddEvent(NewEvent(eDesc)) - - // EnrollmentWithdrawn event - eDesc = NewEventDescriptor(EnrollmentWithdrawnEvent, - manifest.NewParameter("enrollmentID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - s.AddEvent(NewEvent(eDesc)) - - // CertificationIssued event - eDesc = NewEventDescriptor(CertificationIssuedEvent, - manifest.NewParameter("certID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("certType", smartcontract.StringType)) - s.AddEvent(NewEvent(eDesc)) - - // CertificationRevoked event - eDesc = NewEventDescriptor(CertificationRevokedEvent, - manifest.NewParameter("certID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - s.AddEvent(NewEvent(eDesc)) - - // CertificationRenewed event - eDesc = NewEventDescriptor(CertificationRenewedEvent, - manifest.NewParameter("certID", smartcontract.IntegerType), - manifest.NewParameter("newExpiresAt", smartcontract.IntegerType)) - s.AddEvent(NewEvent(eDesc)) - - return s -} - -// Metadata returns contract metadata. -func (s *Scire) Metadata() *interop.ContractMD { - return &s.ContractMD -} - -// Initialize initializes the Scire contract. -func (s *Scire) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != s.ActiveIn() { - return nil - } - - // Initialize counters - s.setAccountCounter(ic.DAO, 0) - s.setCertCounter(ic.DAO, 0) - s.setEnrollCounter(ic.DAO, 0) - - // Initialize config with defaults - cfg := &state.ScireConfig{ - AnnualCreditAllocation: 1000, // 1000 credits per year - MaxCreditsPerProgram: 500, // Max 500 credits per program - CertificationFee: 0, // Free certification - MinEnrollmentDuration: 86400, // ~1 day in blocks (1-second blocks) - } - s.setConfig(ic.DAO, cfg) - - // Initialize cache - cache := &ScireCache{ - accountCount: 0, - certCount: 0, - enrollCount: 0, - } - ic.DAO.SetCache(s.ID, cache) - - return nil -} - -// InitializeCache initializes the cache from storage. -func (s *Scire) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cache := &ScireCache{ - accountCount: s.getAccountCounter(d), - certCount: s.getCertCounter(d), - enrollCount: s.getEnrollCounter(d), - } - d.SetCache(s.ID, cache) - return nil -} - -// OnPersist is called before block is committed. -func (s *Scire) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist is called after block is committed. -func (s *Scire) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn returns the hardfork at which this contract is activated. -func (s *Scire) ActiveIn() *config.Hardfork { - return nil // Always active -} - -// ===== Storage Helpers ===== - -func (s *Scire) makeAccountKey(vitaID uint64) []byte { - key := make([]byte, 9) - key[0] = scirePrefixAccount - binary.BigEndian.PutUint64(key[1:], vitaID) - return key -} - -func (s *Scire) makeAccountByOwnerKey(owner util.Uint160) []byte { - key := make([]byte, 21) - key[0] = scirePrefixAccountByOwner - copy(key[1:], owner.BytesBE()) - return key -} - -func (s *Scire) makeCertificationKey(certID uint64) []byte { - key := make([]byte, 9) - key[0] = scirePrefixCertification - binary.BigEndian.PutUint64(key[1:], certID) - return key -} - -func (s *Scire) makeCertByOwnerKey(vitaID, certID uint64) []byte { - key := make([]byte, 17) - key[0] = scirePrefixCertByOwner - binary.BigEndian.PutUint64(key[1:9], vitaID) - binary.BigEndian.PutUint64(key[9:], certID) - return key -} - -func (s *Scire) makeEnrollmentKey(enrollID uint64) []byte { - key := make([]byte, 9) - key[0] = scirePrefixEnrollment - binary.BigEndian.PutUint64(key[1:], enrollID) - return key -} - -func (s *Scire) makeActiveEnrollmentKey(vitaID uint64) []byte { - key := make([]byte, 9) - key[0] = scirePrefixActiveEnrollment - binary.BigEndian.PutUint64(key[1:], vitaID) - return key -} - -// Counter getters/setters -func (s *Scire) getAccountCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{scirePrefixAccountCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Scire) setAccountCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{scirePrefixAccountCounter}, buf) -} - -func (s *Scire) getCertCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{scirePrefixCertCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Scire) setCertCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{scirePrefixCertCounter}, buf) -} - -func (s *Scire) getEnrollCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(s.ID, []byte{scirePrefixEnrollCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Scire) setEnrollCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(s.ID, []byte{scirePrefixEnrollCounter}, buf) -} - -// Config getter/setter -func (s *Scire) getConfigInternal(d *dao.Simple) *state.ScireConfig { - si := d.GetStorageItem(s.ID, []byte{scirePrefixConfig}) - if si == nil { - return &state.ScireConfig{ - AnnualCreditAllocation: 1000, - MaxCreditsPerProgram: 500, - CertificationFee: 0, - MinEnrollmentDuration: 86400, - } - } - cfg := new(state.ScireConfig) - item, _ := stackitem.Deserialize(si) - cfg.FromStackItem(item) - return cfg -} - -func (s *Scire) setConfig(d *dao.Simple, cfg *state.ScireConfig) { - item, _ := cfg.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, []byte{scirePrefixConfig}, data) -} - -// Account storage -func (s *Scire) getAccountInternal(d *dao.Simple, vitaID uint64) *state.EducationAccount { - si := d.GetStorageItem(s.ID, s.makeAccountKey(vitaID)) - if si == nil { - return nil - } - acc := new(state.EducationAccount) - item, _ := stackitem.Deserialize(si) - acc.FromStackItem(item) - return acc -} - -func (s *Scire) putAccount(d *dao.Simple, acc *state.EducationAccount) { - item, _ := acc.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeAccountKey(acc.VitaID), data) -} - -func (s *Scire) getVitaIDByOwner(d *dao.Simple, owner util.Uint160) (uint64, bool) { - si := d.GetStorageItem(s.ID, s.makeAccountByOwnerKey(owner)) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint64(si), true -} - -func (s *Scire) setOwnerToVitaID(d *dao.Simple, owner util.Uint160, vitaID uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, vitaID) - d.PutStorageItem(s.ID, s.makeAccountByOwnerKey(owner), buf) -} - -// Enrollment storage -func (s *Scire) getEnrollmentInternal(d *dao.Simple, enrollID uint64) *state.Enrollment { - si := d.GetStorageItem(s.ID, s.makeEnrollmentKey(enrollID)) - if si == nil { - return nil - } - enroll := new(state.Enrollment) - item, _ := stackitem.Deserialize(si) - enroll.FromStackItem(item) - return enroll -} - -func (s *Scire) putEnrollment(d *dao.Simple, enroll *state.Enrollment) { - item, _ := enroll.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeEnrollmentKey(enroll.ID), data) -} - -func (s *Scire) getActiveEnrollmentID(d *dao.Simple, vitaID uint64) uint64 { - si := d.GetStorageItem(s.ID, s.makeActiveEnrollmentKey(vitaID)) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (s *Scire) setActiveEnrollmentID(d *dao.Simple, vitaID, enrollID uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, enrollID) - d.PutStorageItem(s.ID, s.makeActiveEnrollmentKey(vitaID), buf) -} - -func (s *Scire) clearActiveEnrollment(d *dao.Simple, vitaID uint64) { - d.DeleteStorageItem(s.ID, s.makeActiveEnrollmentKey(vitaID)) -} - -// Certification storage -func (s *Scire) getCertificationInternal(d *dao.Simple, certID uint64) *state.Certification { - si := d.GetStorageItem(s.ID, s.makeCertificationKey(certID)) - if si == nil { - return nil - } - cert := new(state.Certification) - item, _ := stackitem.Deserialize(si) - cert.FromStackItem(item) - return cert -} - -func (s *Scire) putCertification(d *dao.Simple, cert *state.Certification) { - item, _ := cert.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(s.ID, s.makeCertificationKey(cert.ID), data) -} - -func (s *Scire) setCertByOwner(d *dao.Simple, vitaID, certID uint64) { - d.PutStorageItem(s.ID, s.makeCertByOwnerKey(vitaID, certID), []byte{1}) -} - -// ===== Contract Methods ===== - -// createAccount creates an education account for a Vita holder. -func (s *Scire) createAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - // Check owner has active Vita - if s.Vita == nil { - panic(ErrScireNoVita) - } - vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) - if err != nil || vita == nil { - panic(ErrScireNoVita) - } - if vita.Status != state.TokenStatusActive { - panic(ErrScireNoVita) - } - - // Check if account already exists - existing := s.getAccountInternal(ic.DAO, vita.TokenID) - if existing != nil { - panic(ErrScireAccountExists) - } - - // Check education rights - if !s.checkEducationRight(ic, owner) { - // Log but allow (EnforcementLogging) - // In the future, we could emit an event here - } - - // Get cache and increment counter - cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) - accountNum := cache.accountCount - cache.accountCount++ - s.setAccountCounter(ic.DAO, cache.accountCount) - - // Create account - acc := &state.EducationAccount{ - VitaID: vita.TokenID, - Owner: owner, - TotalCredits: 0, - UsedCredits: 0, - AvailableCredits: 0, - Status: state.EducationAccountActive, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - - // Store account - s.putAccount(ic.DAO, acc) - s.setOwnerToVitaID(ic.DAO, owner, vita.TokenID) - - // Emit event - ic.AddNotification(s.Hash, AccountCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), - stackitem.NewByteArray(owner.BytesBE()), - })) - - _ = accountNum // suppress unused warning - return stackitem.NewBool(true) -} - -// getAccount returns education account by owner. -func (s *Scire) getAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.Null{} - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - return stackitem.Null{} - } - - item, _ := acc.ToStackItem() - return item -} - -// getAccountByVitaID returns education account by Vita ID. -func (s *Scire) getAccountByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toUint64(args[0]) - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - return stackitem.Null{} - } - - item, _ := acc.ToStackItem() - return item -} - -// allocateCredits allocates learning credits to an account (committee only). -func (s *Scire) allocateCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - amount := toUint64(args[1]) - // reason := toString(args[2]) // for logging - - // Committee only - if !s.checkCommittee(ic) { - panic(ErrScireNotCommittee) - } - - if amount == 0 { - panic(ErrScireInvalidCredits) - } - - // Get or create account - vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) - if !found { - // Auto-create account if Vita exists - if s.Vita == nil { - panic(ErrScireNoVita) - } - vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) - if err != nil || vita == nil || vita.Status != state.TokenStatusActive { - panic(ErrScireNoVita) - } - vitaID = vita.TokenID - - // Create account - cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) - cache.accountCount++ - s.setAccountCounter(ic.DAO, cache.accountCount) - - acc := &state.EducationAccount{ - VitaID: vitaID, - Owner: owner, - TotalCredits: amount, - UsedCredits: 0, - AvailableCredits: amount, - Status: state.EducationAccountActive, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - } - s.putAccount(ic.DAO, acc) - s.setOwnerToVitaID(ic.DAO, owner, vitaID) - - // Emit events - ic.AddNotification(s.Hash, AccountCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewByteArray(owner.BytesBE()), - })) - ic.AddNotification(s.Hash, CreditsAllocatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(amount))), - stackitem.NewBigInteger(big.NewInt(int64(amount))), - })) - - return stackitem.NewBool(true) - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - panic(ErrScireAccountNotFound) - } - if acc.Status != state.EducationAccountActive { - panic(ErrScireAccountSuspended) - } - - // Add credits - acc.TotalCredits += amount - acc.AvailableCredits += amount - acc.UpdatedAt = ic.Block.Index - - s.putAccount(ic.DAO, acc) - - // Emit event - ic.AddNotification(s.Hash, CreditsAllocatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewBigInteger(big.NewInt(int64(amount))), - stackitem.NewBigInteger(big.NewInt(int64(acc.AvailableCredits))), - })) - - return stackitem.NewBool(true) -} - -// getCredits returns available credits for an owner. -func (s *Scire) getCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - return stackitem.NewBigInteger(big.NewInt(int64(acc.AvailableCredits))) -} - -// enroll enrolls a student in an education program. -func (s *Scire) enroll(ic *interop.Context, args []stackitem.Item) stackitem.Item { - student := toUint160(args[0]) - programID := toString(args[1]) - institution := toUint160(args[2]) - credits := toUint64(args[3]) - - // Validate inputs - if len(programID) == 0 || len(programID) > 128 { - panic(ErrScireInvalidProgramID) - } - if credits == 0 { - panic(ErrScireInvalidCredits) - } - - // Check max credits - cfg := s.getConfigInternal(ic.DAO) - if credits > cfg.MaxCreditsPerProgram { - panic(ErrScireExceedsMaxCredits) - } - - // Get student's account - vitaID, found := s.getVitaIDByOwner(ic.DAO, student) - if !found { - panic(ErrScireAccountNotFound) - } - - acc := s.getAccountInternal(ic.DAO, vitaID) - if acc == nil { - panic(ErrScireAccountNotFound) - } - if acc.Status != state.EducationAccountActive { - panic(ErrScireAccountSuspended) - } - - // Check sufficient credits - if acc.AvailableCredits < credits { - panic(ErrScireInsufficientCredits) - } - - // Check not already enrolled - activeEnrollID := s.getActiveEnrollmentID(ic.DAO, vitaID) - if activeEnrollID != 0 { - panic(ErrScireAlreadyEnrolled) - } - - // Check education rights - if !s.checkEducationRight(ic, student) { - // Log but allow - } - - // Get next enrollment ID - cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) - enrollID := cache.enrollCount - cache.enrollCount++ - s.setEnrollCounter(ic.DAO, cache.enrollCount) - - // Deduct credits - acc.UsedCredits += credits - acc.AvailableCredits -= credits - acc.UpdatedAt = ic.Block.Index - s.putAccount(ic.DAO, acc) - - // Create enrollment - enroll := &state.Enrollment{ - ID: enrollID, - VitaID: vitaID, - Student: student, - ProgramID: programID, - Institution: institution, - CreditsAllocated: credits, - StartedAt: ic.Block.Index, - CompletedAt: 0, - Status: state.EnrollmentActive, - } - - s.putEnrollment(ic.DAO, enroll) - s.setActiveEnrollmentID(ic.DAO, vitaID, enrollID) - - // Emit event - ic.AddNotification(s.Hash, EnrollmentCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(enrollID))), - stackitem.NewBigInteger(big.NewInt(int64(vitaID))), - stackitem.NewByteArray([]byte(programID)), - })) - - return stackitem.NewBigInteger(big.NewInt(int64(enrollID))) -} - -// completeEnrollment marks an enrollment as completed (institution/educator only). -func (s *Scire) completeEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - enrollID := toUint64(args[0]) - // contentHash := toUint256(args[1]) // for proof storage - - enroll := s.getEnrollmentInternal(ic.DAO, enrollID) - if enroll == nil { - panic(ErrScireEnrollNotFound) - } - if enroll.Status != state.EnrollmentActive { - panic(ErrScireEnrollNotActive) - } - - // Check caller is institution or educator - caller := ic.VM.GetCallingScriptHash() - if caller != enroll.Institution && !s.checkEducator(ic) { - panic(ErrScireNotInstitution) - } - - // Update enrollment - enroll.Status = state.EnrollmentCompleted - enroll.CompletedAt = ic.Block.Index - s.putEnrollment(ic.DAO, enroll) - - // Clear active enrollment - s.clearActiveEnrollment(ic.DAO, enroll.VitaID) - - // Emit event - ic.AddNotification(s.Hash, EnrollmentCompletedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(enrollID))), - stackitem.NewBigInteger(big.NewInt(int64(enroll.VitaID))), - })) - - return stackitem.NewBool(true) -} - -// withdrawEnrollment withdraws from an enrollment. -func (s *Scire) withdrawEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - enrollID := toUint64(args[0]) - reason := toString(args[1]) - - enroll := s.getEnrollmentInternal(ic.DAO, enrollID) - if enroll == nil { - panic(ErrScireEnrollNotFound) - } - if enroll.Status != state.EnrollmentActive { - panic(ErrScireEnrollNotActive) - } - - // Check caller is student or institution - caller := ic.VM.GetCallingScriptHash() - if caller != enroll.Student && caller != enroll.Institution && !s.checkEducator(ic) { - panic(ErrScireNotStudent) - } - - // Update enrollment - enroll.Status = state.EnrollmentWithdrawn - enroll.CompletedAt = ic.Block.Index - s.putEnrollment(ic.DAO, enroll) - - // Clear active enrollment - s.clearActiveEnrollment(ic.DAO, enroll.VitaID) - - // Partial refund: return 50% of credits - acc := s.getAccountInternal(ic.DAO, enroll.VitaID) - if acc != nil { - refund := enroll.CreditsAllocated / 2 - acc.AvailableCredits += refund - acc.UsedCredits -= refund - acc.UpdatedAt = ic.Block.Index - s.putAccount(ic.DAO, acc) - } - - // Emit event - ic.AddNotification(s.Hash, EnrollmentWithdrawnEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(enrollID))), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -// getEnrollment returns enrollment details. -func (s *Scire) getEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - enrollID := toUint64(args[0]) - - enroll := s.getEnrollmentInternal(ic.DAO, enrollID) - if enroll == nil { - return stackitem.Null{} - } - - item, _ := enroll.ToStackItem() - return item -} - -// getActiveEnrollment returns a student's active enrollment. -func (s *Scire) getActiveEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - student := toUint160(args[0]) - - vitaID, found := s.getVitaIDByOwner(ic.DAO, student) - if !found { - return stackitem.Null{} - } - - enrollID := s.getActiveEnrollmentID(ic.DAO, vitaID) - if enrollID == 0 { - return stackitem.Null{} - } - - enroll := s.getEnrollmentInternal(ic.DAO, enrollID) - if enroll == nil { - return stackitem.Null{} - } - - item, _ := enroll.ToStackItem() - return item -} - -// issueCertification issues a certification (educator only). -func (s *Scire) issueCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - certType := toString(args[1]) - name := toString(args[2]) - contentHashBytes := toBytes(args[3]) - expiresAt := toUint32(args[4]) - - // Convert bytes to Uint256 - var contentHash util.Uint256 - if len(contentHashBytes) == 32 { - copy(contentHash[:], contentHashBytes) - } - - // Validate inputs - if len(certType) == 0 || len(certType) > 64 { - panic(ErrScireInvalidCertType) - } - if len(name) == 0 || len(name) > 128 { - panic(ErrScireInvalidName) - } - - // Check educator authority - if !s.checkEducator(ic) { - panic(ErrScireNotEducator) - } - - // Get owner's Vita - if s.Vita == nil { - panic(ErrScireNoVita) - } - vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) - if err != nil || vita == nil || vita.Status != state.TokenStatusActive { - panic(ErrScireNoVita) - } - - // Get issuing institution - institution := ic.VM.GetCallingScriptHash() - - // Get next cert ID - cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) - certID := cache.certCount - cache.certCount++ - s.setCertCounter(ic.DAO, cache.certCount) - - // Create certification - cert := &state.Certification{ - ID: certID, - VitaID: vita.TokenID, - Owner: owner, - CertType: certType, - Name: name, - Institution: institution, - ContentHash: contentHash, - IssuedAt: ic.Block.Index, - ExpiresAt: expiresAt, - Status: state.CertificationActive, - } - - s.putCertification(ic.DAO, cert) - s.setCertByOwner(ic.DAO, vita.TokenID, certID) - - // Emit event - ic.AddNotification(s.Hash, CertificationIssuedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(certID))), - stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), - stackitem.NewByteArray([]byte(certType)), - })) - - return stackitem.NewBigInteger(big.NewInt(int64(certID))) -} - -// revokeCertification revokes a certification (institution only). -func (s *Scire) revokeCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - certID := toUint64(args[0]) - reason := toString(args[1]) - - cert := s.getCertificationInternal(ic.DAO, certID) - if cert == nil { - panic(ErrScireCertNotFound) - } - if cert.Status == state.CertificationRevoked { - panic(ErrScireCertRevoked) - } - - // Check caller is issuing institution or educator - caller := ic.VM.GetCallingScriptHash() - if caller != cert.Institution && !s.checkEducator(ic) { - panic(ErrScireNotInstitution) - } - - // Revoke - cert.Status = state.CertificationRevoked - s.putCertification(ic.DAO, cert) - - // Emit event - ic.AddNotification(s.Hash, CertificationRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(certID))), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -// renewCertification extends a certification's expiry. -func (s *Scire) renewCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - certID := toUint64(args[0]) - newExpiresAt := toUint32(args[1]) - - cert := s.getCertificationInternal(ic.DAO, certID) - if cert == nil { - panic(ErrScireCertNotFound) - } - if cert.Status == state.CertificationRevoked { - panic(ErrScireCertRevoked) - } - - // Check caller is issuing institution or educator - caller := ic.VM.GetCallingScriptHash() - if caller != cert.Institution && !s.checkEducator(ic) { - panic(ErrScireNotInstitution) - } - - // Update expiry - cert.ExpiresAt = newExpiresAt - cert.Status = state.CertificationActive // Reactivate if was expired - s.putCertification(ic.DAO, cert) - - // Emit event - ic.AddNotification(s.Hash, CertificationRenewedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(certID))), - stackitem.NewBigInteger(big.NewInt(int64(newExpiresAt))), - })) - - return stackitem.NewBool(true) -} - -// getCertification returns certification details. -func (s *Scire) getCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - certID := toUint64(args[0]) - - cert := s.getCertificationInternal(ic.DAO, certID) - if cert == nil { - return stackitem.Null{} - } - - item, _ := cert.ToStackItem() - return item -} - -// verifyCertification checks if a certification is currently valid. -func (s *Scire) verifyCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - certID := toUint64(args[0]) - - cert := s.getCertificationInternal(ic.DAO, certID) - if cert == nil { - return stackitem.NewBool(false) - } - - return stackitem.NewBool(cert.IsValid(ic.Block.Index)) -} - -// hasCertification checks if an owner has a specific certification type. -func (s *Scire) hasCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - certType := toString(args[1]) - - vitaID, exists := s.getVitaIDByOwner(ic.DAO, owner) - if !exists { - return stackitem.NewBool(false) - } - - // Scan certifications for this owner - prefix := make([]byte, 9) - prefix[0] = scirePrefixCertByOwner - binary.BigEndian.PutUint64(prefix[1:], vitaID) - - var hasCert bool - ic.DAO.Seek(s.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 8 { - certID := binary.BigEndian.Uint64(k[len(k)-8:]) - cert := s.getCertificationInternal(ic.DAO, certID) - if cert != nil && cert.CertType == certType && cert.IsValid(ic.Block.Index) { - hasCert = true - return false // Stop iteration - } - } - return true - }) - - return stackitem.NewBool(hasCert) -} - -// getConfig returns the Scire configuration. -func (s *Scire) getConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cfg := s.getConfigInternal(ic.DAO) - item, _ := cfg.ToStackItem() - return item -} - -// getTotalAccounts returns the total number of education accounts. -func (s *Scire) getTotalAccounts(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(s.ID).(*ScireCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.accountCount))) -} - -// getTotalCertifications returns the total number of certifications. -func (s *Scire) getTotalCertifications(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(s.ID).(*ScireCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.certCount))) -} - -// getTotalEnrollments returns the total number of enrollments. -func (s *Scire) getTotalEnrollments(ic *interop.Context, args []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(s.ID).(*ScireCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.enrollCount))) -} - -// ===== Public Interface Methods for Cross-Contract Access ===== - -// GetAccountByOwner returns an education account by owner address. -func (s *Scire) GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.EducationAccount, error) { - vitaID, found := s.getVitaIDByOwner(d, owner) - if !found { - return nil, ErrScireAccountNotFound - } - acc := s.getAccountInternal(d, vitaID) - if acc == nil { - return nil, ErrScireAccountNotFound - } - return acc, nil -} - -// HasValidCertification checks if owner has a valid certification of the given type. -func (s *Scire) HasValidCertification(d *dao.Simple, owner util.Uint160, certType string, blockHeight uint32) bool { - vitaID, exists := s.getVitaIDByOwner(d, owner) - if !exists { - return false - } - - prefix := make([]byte, 9) - prefix[0] = scirePrefixCertByOwner - binary.BigEndian.PutUint64(prefix[1:], vitaID) - - var hasCert bool - d.Seek(s.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - if len(k) >= 8 { - certID := binary.BigEndian.Uint64(k[len(k)-8:]) - cert := s.getCertificationInternal(d, certID) - if cert != nil && cert.CertType == certType { - if cert.Status == state.CertificationActive { - if cert.ExpiresAt == 0 || cert.ExpiresAt > blockHeight { - hasCert = true - return false - } - } - } - } - return true - }) - - return hasCert -} - -// Address returns the contract's script hash. -func (s *Scire) Address() util.Uint160 { - return s.Hash -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Scire represents the universal education native contract. +type Scire struct { + interop.ContractMD + Tutus ITutus + Vita IVita + RoleRegistry IRoleRegistry + Lex ILex +} + +// ScireCache represents the cached state for Scire contract. +type ScireCache struct { + accountCount uint64 + certCount uint64 + enrollCount uint64 +} + +// Storage key prefixes for Scire. +const ( + scirePrefixAccount byte = 0x01 // vitaID -> EducationAccount + scirePrefixAccountByOwner byte = 0x02 // owner -> vitaID + scirePrefixCertification byte = 0x10 // certID -> Certification + scirePrefixCertByOwner byte = 0x11 // vitaID + certID -> exists + scirePrefixCertByInstitution byte = 0x12 // institution + certID -> exists + scirePrefixCertByType byte = 0x13 // certType hash + certID -> exists + scirePrefixEnrollment byte = 0x20 // enrollmentID -> Enrollment + scirePrefixEnrollByStudent byte = 0x21 // vitaID + enrollmentID -> exists + scirePrefixEnrollByProgram byte = 0x22 // programID hash + enrollmentID -> exists + scirePrefixActiveEnrollment byte = 0x23 // vitaID -> active enrollmentID + scirePrefixAccountCounter byte = 0xF0 // -> uint64 + scirePrefixCertCounter byte = 0xF1 // -> next certification ID + scirePrefixEnrollCounter byte = 0xF2 // -> next enrollment ID + scirePrefixConfig byte = 0xFF // -> ScireConfig +) + +// Event names for Scire. +const ( + AccountCreatedEvent = "AccountCreated" + CreditsAllocatedEvent = "CreditsAllocated" + EnrollmentCreatedEvent = "EnrollmentCreated" + EnrollmentCompletedEvent = "EnrollmentCompleted" + EnrollmentWithdrawnEvent = "EnrollmentWithdrawn" + EnrollmentTransferredEvent = "EnrollmentTransferred" + CertificationIssuedEvent = "CertificationIssued" + CertificationRevokedEvent = "CertificationRevoked" + CertificationRenewedEvent = "CertificationRenewed" +) + +// Role constants for educators. +const ( + RoleEducator uint64 = 20 // Can issue certifications and manage enrollments +) + +// Various errors for Scire. +var ( + ErrScireAccountNotFound = errors.New("education account not found") + ErrScireAccountExists = errors.New("education account already exists") + ErrScireAccountSuspended = errors.New("education account is suspended") + ErrScireAccountClosed = errors.New("education account is closed") + ErrScireNoVita = errors.New("owner must have an active Vita") + ErrScireInsufficientCredits = errors.New("insufficient education credits") + ErrScireInvalidCredits = errors.New("invalid credit amount") + ErrScireCertNotFound = errors.New("certification not found") + ErrScireCertExpired = errors.New("certification has expired") + ErrScireCertRevoked = errors.New("certification is revoked") + ErrScireEnrollNotFound = errors.New("enrollment not found") + ErrScireEnrollNotActive = errors.New("enrollment is not active") + ErrScireAlreadyEnrolled = errors.New("already enrolled in a program") + ErrScireNotEducator = errors.New("caller is not an authorized educator") + ErrScireNotCommittee = errors.New("invalid committee signature") + ErrScireInvalidOwner = errors.New("invalid owner address") + ErrScireInvalidInstitution = errors.New("invalid institution address") + ErrScireInvalidProgramID = errors.New("invalid program ID") + ErrScireInvalidCertType = errors.New("invalid certification type") + ErrScireInvalidName = errors.New("invalid certification name") + ErrScireEducationRestricted = errors.New("education right is restricted") + ErrScireNotStudent = errors.New("caller is not the student") + ErrScireNotInstitution = errors.New("caller is not the institution") + ErrScireExceedsMaxCredits = errors.New("exceeds maximum credits per program") +) + +var ( + _ interop.Contract = (*Scire)(nil) + _ dao.NativeContractCache = (*ScireCache)(nil) +) + +// Copy implements NativeContractCache interface. +func (c *ScireCache) Copy() dao.NativeContractCache { + return &ScireCache{ + accountCount: c.accountCount, + certCount: c.certCount, + enrollCount: c.enrollCount, + } +} + +// checkCommittee checks if the caller has committee authority. +func (s *Scire) checkCommittee(ic *interop.Context) bool { + if s.RoleRegistry != nil { + return s.RoleRegistry.CheckCommittee(ic) + } + return s.Tutus.CheckCommittee(ic) +} + +// checkEducator checks if the caller has educator authority. +func (s *Scire) checkEducator(ic *interop.Context) bool { + caller := ic.VM.GetCallingScriptHash() + if s.RoleRegistry != nil { + if s.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleEducator, ic.Block.Index) { + return true + } + } + // Committee members can also act as educators + return s.checkCommittee(ic) +} + +// checkEducationRight checks if subject has education rights via Lex. +func (s *Scire) checkEducationRight(ic *interop.Context, subject util.Uint160) bool { + if s.Lex == nil { + return true // Allow if Lex not available + } + return s.Lex.HasRightInternal(ic.DAO, subject, state.RightEducation, ic.Block.Index) +} + +// newScire creates a new Scire native contract. +func newScire() *Scire { + s := &Scire{ + ContractMD: *interop.NewContractMD(nativenames.Scire, nativeids.Scire), + } + defer s.BuildHFSpecificMD(s.ActiveIn()) + + // ===== Account Management ===== + + // createAccount - Create education account for a Vita holder + desc := NewDescriptor("createAccount", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md := NewMethodAndPrice(s.createAccount, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getAccount - Get education account by owner + desc = NewDescriptor("getAccount", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(s.getAccount, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getAccountByVitaID - Get account by Vita ID + desc = NewDescriptor("getAccountByVitaID", smartcontract.ArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getAccountByVitaID, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // allocateCredits - Allocate learning credits (committee only) + desc = NewDescriptor("allocateCredits", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(s.allocateCredits, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getCredits - Get available credits + desc = NewDescriptor("getCredits", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(s.getCredits, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Enrollment Management ===== + + // enroll - Enroll in an education program + desc = NewDescriptor("enroll", smartcontract.IntegerType, + manifest.NewParameter("student", smartcontract.Hash160Type), + manifest.NewParameter("programID", smartcontract.StringType), + manifest.NewParameter("institution", smartcontract.Hash160Type), + manifest.NewParameter("credits", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.enroll, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // completeEnrollment - Mark enrollment as completed (institution only) + desc = NewDescriptor("completeEnrollment", smartcontract.BoolType, + manifest.NewParameter("enrollmentID", smartcontract.IntegerType), + manifest.NewParameter("contentHash", smartcontract.Hash256Type)) + md = NewMethodAndPrice(s.completeEnrollment, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // withdrawEnrollment - Withdraw from program + desc = NewDescriptor("withdrawEnrollment", smartcontract.BoolType, + manifest.NewParameter("enrollmentID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(s.withdrawEnrollment, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getEnrollment - Get enrollment details + desc = NewDescriptor("getEnrollment", smartcontract.ArrayType, + manifest.NewParameter("enrollmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getEnrollment, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getActiveEnrollment - Get student's active enrollment + desc = NewDescriptor("getActiveEnrollment", smartcontract.ArrayType, + manifest.NewParameter("student", smartcontract.Hash160Type)) + md = NewMethodAndPrice(s.getActiveEnrollment, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Certification Management ===== + + // issueCertification - Issue a certification (educator only) + desc = NewDescriptor("issueCertification", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("certType", smartcontract.StringType), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("contentHash", smartcontract.Hash256Type), + manifest.NewParameter("expiresAt", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.issueCertification, 1<<17, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // revokeCertification - Revoke a certification (institution only) + desc = NewDescriptor("revokeCertification", smartcontract.BoolType, + manifest.NewParameter("certID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(s.revokeCertification, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // renewCertification - Extend certification expiry + desc = NewDescriptor("renewCertification", smartcontract.BoolType, + manifest.NewParameter("certID", smartcontract.IntegerType), + manifest.NewParameter("newExpiresAt", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.renewCertification, 1<<16, callflag.States|callflag.AllowNotify) + s.AddMethod(md, desc) + + // getCertification - Get certification details + desc = NewDescriptor("getCertification", smartcontract.ArrayType, + manifest.NewParameter("certID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.getCertification, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // verifyCertification - Check if certification is valid + desc = NewDescriptor("verifyCertification", smartcontract.BoolType, + manifest.NewParameter("certID", smartcontract.IntegerType)) + md = NewMethodAndPrice(s.verifyCertification, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // hasCertification - Check if owner has specific cert type + desc = NewDescriptor("hasCertification", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("certType", smartcontract.StringType)) + md = NewMethodAndPrice(s.hasCertification, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Query Methods ===== + + // getConfig - Get Scire configuration + desc = NewDescriptor("getConfig", smartcontract.ArrayType) + md = NewMethodAndPrice(s.getConfig, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getTotalAccounts - Get total education accounts + desc = NewDescriptor("getTotalAccounts", smartcontract.IntegerType) + md = NewMethodAndPrice(s.getTotalAccounts, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getTotalCertifications - Get total certifications issued + desc = NewDescriptor("getTotalCertifications", smartcontract.IntegerType) + md = NewMethodAndPrice(s.getTotalCertifications, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // getTotalEnrollments - Get total enrollments + desc = NewDescriptor("getTotalEnrollments", smartcontract.IntegerType) + md = NewMethodAndPrice(s.getTotalEnrollments, 1<<15, callflag.ReadStates) + s.AddMethod(md, desc) + + // ===== Events ===== + + // AccountCreated event + eDesc := NewEventDescriptor(AccountCreatedEvent, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("owner", smartcontract.Hash160Type)) + s.AddEvent(NewEvent(eDesc)) + + // CreditsAllocated event + eDesc = NewEventDescriptor(CreditsAllocatedEvent, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("total", smartcontract.IntegerType)) + s.AddEvent(NewEvent(eDesc)) + + // EnrollmentCreated event + eDesc = NewEventDescriptor(EnrollmentCreatedEvent, + manifest.NewParameter("enrollmentID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("programID", smartcontract.StringType)) + s.AddEvent(NewEvent(eDesc)) + + // EnrollmentCompleted event + eDesc = NewEventDescriptor(EnrollmentCompletedEvent, + manifest.NewParameter("enrollmentID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType)) + s.AddEvent(NewEvent(eDesc)) + + // EnrollmentWithdrawn event + eDesc = NewEventDescriptor(EnrollmentWithdrawnEvent, + manifest.NewParameter("enrollmentID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + s.AddEvent(NewEvent(eDesc)) + + // CertificationIssued event + eDesc = NewEventDescriptor(CertificationIssuedEvent, + manifest.NewParameter("certID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("certType", smartcontract.StringType)) + s.AddEvent(NewEvent(eDesc)) + + // CertificationRevoked event + eDesc = NewEventDescriptor(CertificationRevokedEvent, + manifest.NewParameter("certID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + s.AddEvent(NewEvent(eDesc)) + + // CertificationRenewed event + eDesc = NewEventDescriptor(CertificationRenewedEvent, + manifest.NewParameter("certID", smartcontract.IntegerType), + manifest.NewParameter("newExpiresAt", smartcontract.IntegerType)) + s.AddEvent(NewEvent(eDesc)) + + return s +} + +// Metadata returns contract metadata. +func (s *Scire) Metadata() *interop.ContractMD { + return &s.ContractMD +} + +// Initialize initializes the Scire contract. +func (s *Scire) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != s.ActiveIn() { + return nil + } + + // Initialize counters + s.setAccountCounter(ic.DAO, 0) + s.setCertCounter(ic.DAO, 0) + s.setEnrollCounter(ic.DAO, 0) + + // Initialize config with defaults + cfg := &state.ScireConfig{ + AnnualCreditAllocation: 1000, // 1000 credits per year + MaxCreditsPerProgram: 500, // Max 500 credits per program + CertificationFee: 0, // Free certification + MinEnrollmentDuration: 86400, // ~1 day in blocks (1-second blocks) + } + s.setConfig(ic.DAO, cfg) + + // Initialize cache + cache := &ScireCache{ + accountCount: 0, + certCount: 0, + enrollCount: 0, + } + ic.DAO.SetCache(s.ID, cache) + + return nil +} + +// InitializeCache initializes the cache from storage. +func (s *Scire) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cache := &ScireCache{ + accountCount: s.getAccountCounter(d), + certCount: s.getCertCounter(d), + enrollCount: s.getEnrollCounter(d), + } + d.SetCache(s.ID, cache) + return nil +} + +// OnPersist is called before block is committed. +func (s *Scire) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist is called after block is committed. +func (s *Scire) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn returns the hardfork at which this contract is activated. +func (s *Scire) ActiveIn() *config.Hardfork { + return nil // Always active +} + +// ===== Storage Helpers ===== + +func (s *Scire) makeAccountKey(vitaID uint64) []byte { + key := make([]byte, 9) + key[0] = scirePrefixAccount + binary.BigEndian.PutUint64(key[1:], vitaID) + return key +} + +func (s *Scire) makeAccountByOwnerKey(owner util.Uint160) []byte { + key := make([]byte, 21) + key[0] = scirePrefixAccountByOwner + copy(key[1:], owner.BytesBE()) + return key +} + +func (s *Scire) makeCertificationKey(certID uint64) []byte { + key := make([]byte, 9) + key[0] = scirePrefixCertification + binary.BigEndian.PutUint64(key[1:], certID) + return key +} + +func (s *Scire) makeCertByOwnerKey(vitaID, certID uint64) []byte { + key := make([]byte, 17) + key[0] = scirePrefixCertByOwner + binary.BigEndian.PutUint64(key[1:9], vitaID) + binary.BigEndian.PutUint64(key[9:], certID) + return key +} + +func (s *Scire) makeEnrollmentKey(enrollID uint64) []byte { + key := make([]byte, 9) + key[0] = scirePrefixEnrollment + binary.BigEndian.PutUint64(key[1:], enrollID) + return key +} + +func (s *Scire) makeActiveEnrollmentKey(vitaID uint64) []byte { + key := make([]byte, 9) + key[0] = scirePrefixActiveEnrollment + binary.BigEndian.PutUint64(key[1:], vitaID) + return key +} + +// Counter getters/setters +func (s *Scire) getAccountCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{scirePrefixAccountCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Scire) setAccountCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{scirePrefixAccountCounter}, buf) +} + +func (s *Scire) getCertCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{scirePrefixCertCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Scire) setCertCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{scirePrefixCertCounter}, buf) +} + +func (s *Scire) getEnrollCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(s.ID, []byte{scirePrefixEnrollCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Scire) setEnrollCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(s.ID, []byte{scirePrefixEnrollCounter}, buf) +} + +// Config getter/setter +func (s *Scire) getConfigInternal(d *dao.Simple) *state.ScireConfig { + si := d.GetStorageItem(s.ID, []byte{scirePrefixConfig}) + if si == nil { + return &state.ScireConfig{ + AnnualCreditAllocation: 1000, + MaxCreditsPerProgram: 500, + CertificationFee: 0, + MinEnrollmentDuration: 86400, + } + } + cfg := new(state.ScireConfig) + item, _ := stackitem.Deserialize(si) + cfg.FromStackItem(item) + return cfg +} + +func (s *Scire) setConfig(d *dao.Simple, cfg *state.ScireConfig) { + item, _ := cfg.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, []byte{scirePrefixConfig}, data) +} + +// Account storage +func (s *Scire) getAccountInternal(d *dao.Simple, vitaID uint64) *state.EducationAccount { + si := d.GetStorageItem(s.ID, s.makeAccountKey(vitaID)) + if si == nil { + return nil + } + acc := new(state.EducationAccount) + item, _ := stackitem.Deserialize(si) + acc.FromStackItem(item) + return acc +} + +func (s *Scire) putAccount(d *dao.Simple, acc *state.EducationAccount) { + item, _ := acc.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeAccountKey(acc.VitaID), data) +} + +func (s *Scire) getVitaIDByOwner(d *dao.Simple, owner util.Uint160) (uint64, bool) { + si := d.GetStorageItem(s.ID, s.makeAccountByOwnerKey(owner)) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint64(si), true +} + +func (s *Scire) setOwnerToVitaID(d *dao.Simple, owner util.Uint160, vitaID uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, vitaID) + d.PutStorageItem(s.ID, s.makeAccountByOwnerKey(owner), buf) +} + +// Enrollment storage +func (s *Scire) getEnrollmentInternal(d *dao.Simple, enrollID uint64) *state.Enrollment { + si := d.GetStorageItem(s.ID, s.makeEnrollmentKey(enrollID)) + if si == nil { + return nil + } + enroll := new(state.Enrollment) + item, _ := stackitem.Deserialize(si) + enroll.FromStackItem(item) + return enroll +} + +func (s *Scire) putEnrollment(d *dao.Simple, enroll *state.Enrollment) { + item, _ := enroll.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeEnrollmentKey(enroll.ID), data) +} + +func (s *Scire) getActiveEnrollmentID(d *dao.Simple, vitaID uint64) uint64 { + si := d.GetStorageItem(s.ID, s.makeActiveEnrollmentKey(vitaID)) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (s *Scire) setActiveEnrollmentID(d *dao.Simple, vitaID, enrollID uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, enrollID) + d.PutStorageItem(s.ID, s.makeActiveEnrollmentKey(vitaID), buf) +} + +func (s *Scire) clearActiveEnrollment(d *dao.Simple, vitaID uint64) { + d.DeleteStorageItem(s.ID, s.makeActiveEnrollmentKey(vitaID)) +} + +// Certification storage +func (s *Scire) getCertificationInternal(d *dao.Simple, certID uint64) *state.Certification { + si := d.GetStorageItem(s.ID, s.makeCertificationKey(certID)) + if si == nil { + return nil + } + cert := new(state.Certification) + item, _ := stackitem.Deserialize(si) + cert.FromStackItem(item) + return cert +} + +func (s *Scire) putCertification(d *dao.Simple, cert *state.Certification) { + item, _ := cert.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(s.ID, s.makeCertificationKey(cert.ID), data) +} + +func (s *Scire) setCertByOwner(d *dao.Simple, vitaID, certID uint64) { + d.PutStorageItem(s.ID, s.makeCertByOwnerKey(vitaID, certID), []byte{1}) +} + +// ===== Contract Methods ===== + +// createAccount creates an education account for a Vita holder. +func (s *Scire) createAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + // Check owner has active Vita + if s.Vita == nil { + panic(ErrScireNoVita) + } + vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) + if err != nil || vita == nil { + panic(ErrScireNoVita) + } + if vita.Status != state.TokenStatusActive { + panic(ErrScireNoVita) + } + + // Check if account already exists + existing := s.getAccountInternal(ic.DAO, vita.TokenID) + if existing != nil { + panic(ErrScireAccountExists) + } + + // Check education rights + if !s.checkEducationRight(ic, owner) { + // Log but allow (EnforcementLogging) + // In the future, we could emit an event here + } + + // Get cache and increment counter + cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) + accountNum := cache.accountCount + cache.accountCount++ + s.setAccountCounter(ic.DAO, cache.accountCount) + + // Create account + acc := &state.EducationAccount{ + VitaID: vita.TokenID, + Owner: owner, + TotalCredits: 0, + UsedCredits: 0, + AvailableCredits: 0, + Status: state.EducationAccountActive, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + + // Store account + s.putAccount(ic.DAO, acc) + s.setOwnerToVitaID(ic.DAO, owner, vita.TokenID) + + // Emit event + ic.AddNotification(s.Hash, AccountCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), + stackitem.NewByteArray(owner.BytesBE()), + })) + + _ = accountNum // suppress unused warning + return stackitem.NewBool(true) +} + +// getAccount returns education account by owner. +func (s *Scire) getAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.Null{} + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + return stackitem.Null{} + } + + item, _ := acc.ToStackItem() + return item +} + +// getAccountByVitaID returns education account by Vita ID. +func (s *Scire) getAccountByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toUint64(args[0]) + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + return stackitem.Null{} + } + + item, _ := acc.ToStackItem() + return item +} + +// allocateCredits allocates learning credits to an account (committee only). +func (s *Scire) allocateCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + amount := toUint64(args[1]) + // reason := toString(args[2]) // for logging + + // Committee only + if !s.checkCommittee(ic) { + panic(ErrScireNotCommittee) + } + + if amount == 0 { + panic(ErrScireInvalidCredits) + } + + // Get or create account + vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) + if !found { + // Auto-create account if Vita exists + if s.Vita == nil { + panic(ErrScireNoVita) + } + vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) + if err != nil || vita == nil || vita.Status != state.TokenStatusActive { + panic(ErrScireNoVita) + } + vitaID = vita.TokenID + + // Create account + cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) + cache.accountCount++ + s.setAccountCounter(ic.DAO, cache.accountCount) + + acc := &state.EducationAccount{ + VitaID: vitaID, + Owner: owner, + TotalCredits: amount, + UsedCredits: 0, + AvailableCredits: amount, + Status: state.EducationAccountActive, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + } + s.putAccount(ic.DAO, acc) + s.setOwnerToVitaID(ic.DAO, owner, vitaID) + + // Emit events + ic.AddNotification(s.Hash, AccountCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewByteArray(owner.BytesBE()), + })) + ic.AddNotification(s.Hash, CreditsAllocatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(amount))), + stackitem.NewBigInteger(big.NewInt(int64(amount))), + })) + + return stackitem.NewBool(true) + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + panic(ErrScireAccountNotFound) + } + if acc.Status != state.EducationAccountActive { + panic(ErrScireAccountSuspended) + } + + // Add credits + acc.TotalCredits += amount + acc.AvailableCredits += amount + acc.UpdatedAt = ic.Block.Index + + s.putAccount(ic.DAO, acc) + + // Emit event + ic.AddNotification(s.Hash, CreditsAllocatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewBigInteger(big.NewInt(int64(amount))), + stackitem.NewBigInteger(big.NewInt(int64(acc.AvailableCredits))), + })) + + return stackitem.NewBool(true) +} + +// getCredits returns available credits for an owner. +func (s *Scire) getCredits(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := s.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + return stackitem.NewBigInteger(big.NewInt(int64(acc.AvailableCredits))) +} + +// enroll enrolls a student in an education program. +func (s *Scire) enroll(ic *interop.Context, args []stackitem.Item) stackitem.Item { + student := toUint160(args[0]) + programID := toString(args[1]) + institution := toUint160(args[2]) + credits := toUint64(args[3]) + + // Validate inputs + if len(programID) == 0 || len(programID) > 128 { + panic(ErrScireInvalidProgramID) + } + if credits == 0 { + panic(ErrScireInvalidCredits) + } + + // Check max credits + cfg := s.getConfigInternal(ic.DAO) + if credits > cfg.MaxCreditsPerProgram { + panic(ErrScireExceedsMaxCredits) + } + + // Get student's account + vitaID, found := s.getVitaIDByOwner(ic.DAO, student) + if !found { + panic(ErrScireAccountNotFound) + } + + acc := s.getAccountInternal(ic.DAO, vitaID) + if acc == nil { + panic(ErrScireAccountNotFound) + } + if acc.Status != state.EducationAccountActive { + panic(ErrScireAccountSuspended) + } + + // Check sufficient credits + if acc.AvailableCredits < credits { + panic(ErrScireInsufficientCredits) + } + + // Check not already enrolled + activeEnrollID := s.getActiveEnrollmentID(ic.DAO, vitaID) + if activeEnrollID != 0 { + panic(ErrScireAlreadyEnrolled) + } + + // Check education rights + if !s.checkEducationRight(ic, student) { + // Log but allow + } + + // Get next enrollment ID + cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) + enrollID := cache.enrollCount + cache.enrollCount++ + s.setEnrollCounter(ic.DAO, cache.enrollCount) + + // Deduct credits + acc.UsedCredits += credits + acc.AvailableCredits -= credits + acc.UpdatedAt = ic.Block.Index + s.putAccount(ic.DAO, acc) + + // Create enrollment + enroll := &state.Enrollment{ + ID: enrollID, + VitaID: vitaID, + Student: student, + ProgramID: programID, + Institution: institution, + CreditsAllocated: credits, + StartedAt: ic.Block.Index, + CompletedAt: 0, + Status: state.EnrollmentActive, + } + + s.putEnrollment(ic.DAO, enroll) + s.setActiveEnrollmentID(ic.DAO, vitaID, enrollID) + + // Emit event + ic.AddNotification(s.Hash, EnrollmentCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(enrollID))), + stackitem.NewBigInteger(big.NewInt(int64(vitaID))), + stackitem.NewByteArray([]byte(programID)), + })) + + return stackitem.NewBigInteger(big.NewInt(int64(enrollID))) +} + +// completeEnrollment marks an enrollment as completed (institution/educator only). +func (s *Scire) completeEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + enrollID := toUint64(args[0]) + // contentHash := toUint256(args[1]) // for proof storage + + enroll := s.getEnrollmentInternal(ic.DAO, enrollID) + if enroll == nil { + panic(ErrScireEnrollNotFound) + } + if enroll.Status != state.EnrollmentActive { + panic(ErrScireEnrollNotActive) + } + + // Check caller is institution or educator + caller := ic.VM.GetCallingScriptHash() + if caller != enroll.Institution && !s.checkEducator(ic) { + panic(ErrScireNotInstitution) + } + + // Update enrollment + enroll.Status = state.EnrollmentCompleted + enroll.CompletedAt = ic.Block.Index + s.putEnrollment(ic.DAO, enroll) + + // Clear active enrollment + s.clearActiveEnrollment(ic.DAO, enroll.VitaID) + + // Emit event + ic.AddNotification(s.Hash, EnrollmentCompletedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(enrollID))), + stackitem.NewBigInteger(big.NewInt(int64(enroll.VitaID))), + })) + + return stackitem.NewBool(true) +} + +// withdrawEnrollment withdraws from an enrollment. +func (s *Scire) withdrawEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + enrollID := toUint64(args[0]) + reason := toString(args[1]) + + enroll := s.getEnrollmentInternal(ic.DAO, enrollID) + if enroll == nil { + panic(ErrScireEnrollNotFound) + } + if enroll.Status != state.EnrollmentActive { + panic(ErrScireEnrollNotActive) + } + + // Check caller is student or institution + caller := ic.VM.GetCallingScriptHash() + if caller != enroll.Student && caller != enroll.Institution && !s.checkEducator(ic) { + panic(ErrScireNotStudent) + } + + // Update enrollment + enroll.Status = state.EnrollmentWithdrawn + enroll.CompletedAt = ic.Block.Index + s.putEnrollment(ic.DAO, enroll) + + // Clear active enrollment + s.clearActiveEnrollment(ic.DAO, enroll.VitaID) + + // Partial refund: return 50% of credits + acc := s.getAccountInternal(ic.DAO, enroll.VitaID) + if acc != nil { + refund := enroll.CreditsAllocated / 2 + acc.AvailableCredits += refund + acc.UsedCredits -= refund + acc.UpdatedAt = ic.Block.Index + s.putAccount(ic.DAO, acc) + } + + // Emit event + ic.AddNotification(s.Hash, EnrollmentWithdrawnEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(enrollID))), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +// getEnrollment returns enrollment details. +func (s *Scire) getEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + enrollID := toUint64(args[0]) + + enroll := s.getEnrollmentInternal(ic.DAO, enrollID) + if enroll == nil { + return stackitem.Null{} + } + + item, _ := enroll.ToStackItem() + return item +} + +// getActiveEnrollment returns a student's active enrollment. +func (s *Scire) getActiveEnrollment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + student := toUint160(args[0]) + + vitaID, found := s.getVitaIDByOwner(ic.DAO, student) + if !found { + return stackitem.Null{} + } + + enrollID := s.getActiveEnrollmentID(ic.DAO, vitaID) + if enrollID == 0 { + return stackitem.Null{} + } + + enroll := s.getEnrollmentInternal(ic.DAO, enrollID) + if enroll == nil { + return stackitem.Null{} + } + + item, _ := enroll.ToStackItem() + return item +} + +// issueCertification issues a certification (educator only). +func (s *Scire) issueCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + certType := toString(args[1]) + name := toString(args[2]) + contentHashBytes := toBytes(args[3]) + expiresAt := toUint32(args[4]) + + // Convert bytes to Uint256 + var contentHash util.Uint256 + if len(contentHashBytes) == 32 { + copy(contentHash[:], contentHashBytes) + } + + // Validate inputs + if len(certType) == 0 || len(certType) > 64 { + panic(ErrScireInvalidCertType) + } + if len(name) == 0 || len(name) > 128 { + panic(ErrScireInvalidName) + } + + // Check educator authority + if !s.checkEducator(ic) { + panic(ErrScireNotEducator) + } + + // Get owner's Vita + if s.Vita == nil { + panic(ErrScireNoVita) + } + vita, err := s.Vita.GetTokenByOwner(ic.DAO, owner) + if err != nil || vita == nil || vita.Status != state.TokenStatusActive { + panic(ErrScireNoVita) + } + + // Get issuing institution + institution := ic.VM.GetCallingScriptHash() + + // Get next cert ID + cache := ic.DAO.GetRWCache(s.ID).(*ScireCache) + certID := cache.certCount + cache.certCount++ + s.setCertCounter(ic.DAO, cache.certCount) + + // Create certification + cert := &state.Certification{ + ID: certID, + VitaID: vita.TokenID, + Owner: owner, + CertType: certType, + Name: name, + Institution: institution, + ContentHash: contentHash, + IssuedAt: ic.Block.Index, + ExpiresAt: expiresAt, + Status: state.CertificationActive, + } + + s.putCertification(ic.DAO, cert) + s.setCertByOwner(ic.DAO, vita.TokenID, certID) + + // Emit event + ic.AddNotification(s.Hash, CertificationIssuedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(certID))), + stackitem.NewBigInteger(big.NewInt(int64(vita.TokenID))), + stackitem.NewByteArray([]byte(certType)), + })) + + return stackitem.NewBigInteger(big.NewInt(int64(certID))) +} + +// revokeCertification revokes a certification (institution only). +func (s *Scire) revokeCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + certID := toUint64(args[0]) + reason := toString(args[1]) + + cert := s.getCertificationInternal(ic.DAO, certID) + if cert == nil { + panic(ErrScireCertNotFound) + } + if cert.Status == state.CertificationRevoked { + panic(ErrScireCertRevoked) + } + + // Check caller is issuing institution or educator + caller := ic.VM.GetCallingScriptHash() + if caller != cert.Institution && !s.checkEducator(ic) { + panic(ErrScireNotInstitution) + } + + // Revoke + cert.Status = state.CertificationRevoked + s.putCertification(ic.DAO, cert) + + // Emit event + ic.AddNotification(s.Hash, CertificationRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(certID))), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +// renewCertification extends a certification's expiry. +func (s *Scire) renewCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + certID := toUint64(args[0]) + newExpiresAt := toUint32(args[1]) + + cert := s.getCertificationInternal(ic.DAO, certID) + if cert == nil { + panic(ErrScireCertNotFound) + } + if cert.Status == state.CertificationRevoked { + panic(ErrScireCertRevoked) + } + + // Check caller is issuing institution or educator + caller := ic.VM.GetCallingScriptHash() + if caller != cert.Institution && !s.checkEducator(ic) { + panic(ErrScireNotInstitution) + } + + // Update expiry + cert.ExpiresAt = newExpiresAt + cert.Status = state.CertificationActive // Reactivate if was expired + s.putCertification(ic.DAO, cert) + + // Emit event + ic.AddNotification(s.Hash, CertificationRenewedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(certID))), + stackitem.NewBigInteger(big.NewInt(int64(newExpiresAt))), + })) + + return stackitem.NewBool(true) +} + +// getCertification returns certification details. +func (s *Scire) getCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + certID := toUint64(args[0]) + + cert := s.getCertificationInternal(ic.DAO, certID) + if cert == nil { + return stackitem.Null{} + } + + item, _ := cert.ToStackItem() + return item +} + +// verifyCertification checks if a certification is currently valid. +func (s *Scire) verifyCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + certID := toUint64(args[0]) + + cert := s.getCertificationInternal(ic.DAO, certID) + if cert == nil { + return stackitem.NewBool(false) + } + + return stackitem.NewBool(cert.IsValid(ic.Block.Index)) +} + +// hasCertification checks if an owner has a specific certification type. +func (s *Scire) hasCertification(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + certType := toString(args[1]) + + vitaID, exists := s.getVitaIDByOwner(ic.DAO, owner) + if !exists { + return stackitem.NewBool(false) + } + + // Scan certifications for this owner + prefix := make([]byte, 9) + prefix[0] = scirePrefixCertByOwner + binary.BigEndian.PutUint64(prefix[1:], vitaID) + + var hasCert bool + ic.DAO.Seek(s.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 8 { + certID := binary.BigEndian.Uint64(k[len(k)-8:]) + cert := s.getCertificationInternal(ic.DAO, certID) + if cert != nil && cert.CertType == certType && cert.IsValid(ic.Block.Index) { + hasCert = true + return false // Stop iteration + } + } + return true + }) + + return stackitem.NewBool(hasCert) +} + +// getConfig returns the Scire configuration. +func (s *Scire) getConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cfg := s.getConfigInternal(ic.DAO) + item, _ := cfg.ToStackItem() + return item +} + +// getTotalAccounts returns the total number of education accounts. +func (s *Scire) getTotalAccounts(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(s.ID).(*ScireCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.accountCount))) +} + +// getTotalCertifications returns the total number of certifications. +func (s *Scire) getTotalCertifications(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(s.ID).(*ScireCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.certCount))) +} + +// getTotalEnrollments returns the total number of enrollments. +func (s *Scire) getTotalEnrollments(ic *interop.Context, args []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(s.ID).(*ScireCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.enrollCount))) +} + +// ===== Public Interface Methods for Cross-Contract Access ===== + +// GetAccountByOwner returns an education account by owner address. +func (s *Scire) GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.EducationAccount, error) { + vitaID, found := s.getVitaIDByOwner(d, owner) + if !found { + return nil, ErrScireAccountNotFound + } + acc := s.getAccountInternal(d, vitaID) + if acc == nil { + return nil, ErrScireAccountNotFound + } + return acc, nil +} + +// HasValidCertification checks if owner has a valid certification of the given type. +func (s *Scire) HasValidCertification(d *dao.Simple, owner util.Uint160, certType string, blockHeight uint32) bool { + vitaID, exists := s.getVitaIDByOwner(d, owner) + if !exists { + return false + } + + prefix := make([]byte, 9) + prefix[0] = scirePrefixCertByOwner + binary.BigEndian.PutUint64(prefix[1:], vitaID) + + var hasCert bool + d.Seek(s.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + if len(k) >= 8 { + certID := binary.BigEndian.Uint64(k[len(k)-8:]) + cert := s.getCertificationInternal(d, certID) + if cert != nil && cert.CertType == certType { + if cert.Status == state.CertificationActive { + if cert.ExpiresAt == 0 || cert.ExpiresAt > blockHeight { + hasCert = true + return false + } + } + } + } + return true + }) + + return hasCert +} + +// Address returns the contract's script hash. +func (s *Scire) Address() util.Uint160 { + return s.Hash +} diff --git a/pkg/core/native/sese.go b/pkg/core/native/sese.go index 56bf1f5..02f1a33 100644 --- a/pkg/core/native/sese.go +++ b/pkg/core/native/sese.go @@ -5,17 +5,17 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Sese represents the life planning native contract. diff --git a/pkg/core/native/std.go b/pkg/core/native/std.go index cd8164b..8659e12 100644 --- a/pkg/core/native/std.go +++ b/pkg/core/native/std.go @@ -12,17 +12,17 @@ import ( "unicode/utf8" "github.com/mr-tron/base58" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - base58neogo "github.com/tutus-one/tutus-chain/pkg/encoding/base58" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + base58neogo "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/base58" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Std represents an StdLib contract. diff --git a/pkg/core/native/std_test.go b/pkg/core/native/std_test.go index d069127..ddd40d7 100644 --- a/pkg/core/native/std_test.go +++ b/pkg/core/native/std_test.go @@ -9,11 +9,11 @@ import ( "testing" "github.com/mr-tron/base58" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - base58neogo "github.com/tutus-one/tutus-chain/pkg/encoding/base58" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + base58neogo "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/base58" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/native/treasury.go b/pkg/core/native/treasury.go index 2e6d0d3..235a462 100644 --- a/pkg/core/native/treasury.go +++ b/pkg/core/native/treasury.go @@ -3,16 +3,16 @@ package native import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Treasury represents Treasury native contract. diff --git a/pkg/core/native/tribute.go b/pkg/core/native/tribute.go index b33ca2c..68a32d6 100644 --- a/pkg/core/native/tribute.go +++ b/pkg/core/native/tribute.go @@ -1,1647 +1,1647 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Tribute represents the anti-hoarding economics native contract. -type Tribute struct { - interop.ContractMD - Tutus ITutus - Vita IVita - VTS IVTS - RoleRegistry IRoleRegistry - Lex ILex -} - -// TributeCache represents the cached state for Tribute contract. -type TributeCache struct { - accountCount uint64 - assessmentCount uint64 - incentiveCount uint64 - redistributionCount uint64 -} - -// Storage key prefixes for Tribute. -const ( - tributePrefixAccount byte = 0x01 // vitaID -> VelocityAccount - tributePrefixAccountByOwner byte = 0x02 // owner -> vitaID - tributePrefixAssessment byte = 0x10 // assessmentID -> TributeAssessment - tributePrefixAssessmentByOwner byte = 0x11 // vitaID + assessmentID -> exists - tributePrefixPendingAssessment byte = 0x12 // vitaID -> latest pending assessmentID - tributePrefixIncentive byte = 0x20 // incentiveID -> CirculationIncentive - tributePrefixIncentiveByOwner byte = 0x21 // vitaID + incentiveID -> exists - tributePrefixUnclaimedIncentive byte = 0x22 // vitaID + incentiveID -> exists (unclaimed only) - tributePrefixRedistribution byte = 0x30 // redistID -> RedistributionRecord - tributePrefixAccountCounter byte = 0xF0 // -> uint64 - tributePrefixAssessmentCounter byte = 0xF1 // -> next assessment ID - tributePrefixIncentiveCounter byte = 0xF2 // -> next incentive ID - tributePrefixRedistributionCtr byte = 0xF3 // -> next redistribution ID - tributePrefixTotalTributePool byte = 0xF8 // -> total tribute collected for redistribution - tributePrefixConfig byte = 0xFF // -> TributeConfig -) - -// Event names for Tribute. -const ( - VelocityAccountCreatedEvent = "VelocityAccountCreated" - VelocityUpdatedEvent = "VelocityUpdated" - TributeAssessedEvent = "TributeAssessed" - TributeCollectedEvent = "TributeCollected" - TributeWaivedEvent = "TributeWaived" - TributeAppealedEvent = "TributeAppealed" - IncentiveGrantedEvent = "IncentiveGranted" - IncentiveClaimedEvent = "IncentiveClaimed" - RedistributionExecutedEvent = "RedistributionExecuted" - ExemptionGrantedEvent = "ExemptionGranted" - ExemptionRevokedEvent = "ExemptionRevoked" -) - -// Role constants for tribute administrators. -const ( - RoleTributeAdmin uint64 = 23 // Can manage exemptions and appeals -) - -// Various errors for Tribute. -var ( - ErrTributeAccountNotFound = errors.New("velocity account not found") - ErrTributeAccountExists = errors.New("velocity account already exists") - ErrTributeAccountExempt = errors.New("account is exempt from tribute") - ErrTributeAccountSuspended = errors.New("velocity account is suspended") - ErrTributeNoVita = errors.New("owner must have an active Vita") - ErrTributeAssessmentNotFound = errors.New("tribute assessment not found") - ErrTributeAssessmentNotPending = errors.New("assessment is not pending") - ErrTributeAssessmentAlreadyPaid = errors.New("assessment already collected") - ErrTributeIncentiveNotFound = errors.New("incentive not found") - ErrTributeIncentiveClaimed = errors.New("incentive already claimed") - ErrTributeInsufficientBalance = errors.New("insufficient balance for tribute") - ErrTributeNotCommittee = errors.New("invalid committee signature") - ErrTributeNotOwner = errors.New("caller is not the owner") - ErrTributeNotAdmin = errors.New("caller is not an authorized tribute admin") - ErrTributePropertyRestricted = errors.New("property right is restricted") - ErrTributeInvalidAmount = errors.New("invalid amount") - ErrTributeInvalidReason = errors.New("invalid reason") - ErrTributeBelowExemption = errors.New("balance below exemption threshold") - ErrTributeNoHoarding = errors.New("no hoarding detected") - ErrTributeNothingToRedistribute = errors.New("nothing to redistribute") -) - -var ( - _ interop.Contract = (*Tribute)(nil) - _ dao.NativeContractCache = (*TributeCache)(nil) -) - -// Copy implements NativeContractCache interface. -func (c *TributeCache) Copy() dao.NativeContractCache { - return &TributeCache{ - accountCount: c.accountCount, - assessmentCount: c.assessmentCount, - incentiveCount: c.incentiveCount, - redistributionCount: c.redistributionCount, - } -} - -// checkCommittee checks if the caller has committee authority. -func (t *Tribute) checkCommittee(ic *interop.Context) bool { - if t.RoleRegistry != nil { - return t.RoleRegistry.CheckCommittee(ic) - } - return t.Tutus.CheckCommittee(ic) -} - -// checkTributeAdmin checks if the caller has tribute admin authority. -func (t *Tribute) checkTributeAdmin(ic *interop.Context) bool { - caller := ic.VM.GetCallingScriptHash() - if t.RoleRegistry != nil { - if t.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleTributeAdmin, ic.Block.Index) { - return true - } - } - // Committee members can also act as tribute admins - return t.checkCommittee(ic) -} - -// checkPropertyRight checks if subject has property rights via Lex. -func (t *Tribute) checkPropertyRight(ic *interop.Context, subject util.Uint160) bool { - if t.Lex == nil { - return true // Allow if Lex not available - } - return t.Lex.HasRightInternal(ic.DAO, subject, state.RightProperty, ic.Block.Index) -} - -// newTribute creates a new Tribute native contract. -func newTribute() *Tribute { - t := &Tribute{ - ContractMD: *interop.NewContractMD(nativenames.Tribute, nativeids.Tribute), - } - defer t.BuildHFSpecificMD(t.ActiveIn()) - - // ===== Account Management ===== - - // createVelocityAccount - Create velocity tracking account for a Vita holder - desc := NewDescriptor("createVelocityAccount", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md := NewMethodAndPrice(t.createVelocityAccount, 1<<17, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // getAccount - Get velocity account by owner - desc = NewDescriptor("getAccount", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.getAccount, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getAccountByVitaID - Get account by Vita ID - desc = NewDescriptor("getAccountByVitaID", smartcontract.ArrayType, - manifest.NewParameter("vitaID", smartcontract.IntegerType)) - md = NewMethodAndPrice(t.getAccountByVitaID, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // recordTransaction - Record a transaction for velocity tracking - desc = NewDescriptor("recordTransaction", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("isOutflow", smartcontract.BoolType)) - md = NewMethodAndPrice(t.recordTransaction, 1<<16, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // getVelocity - Get current velocity score for an owner - desc = NewDescriptor("getVelocity", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.getVelocity, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getHoardingLevel - Get current hoarding level for an owner - desc = NewDescriptor("getHoardingLevel", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.getHoardingLevel, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // ===== Exemption Management ===== - - // grantExemption - Grant exemption from tribute (admin only) - desc = NewDescriptor("grantExemption", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(t.grantExemption, 1<<16, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // revokeExemption - Revoke exemption from tribute (admin only) - desc = NewDescriptor("revokeExemption", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.revokeExemption, 1<<16, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // isExempt - Check if account is exempt - desc = NewDescriptor("isExempt", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.isExempt, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // ===== Assessment Management ===== - - // assessTribute - Assess tribute for hoarding (called periodically or on-demand) - desc = NewDescriptor("assessTribute", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.assessTribute, 1<<17, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // getAssessment - Get assessment by ID - desc = NewDescriptor("getAssessment", smartcontract.ArrayType, - manifest.NewParameter("assessmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(t.getAssessment, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getPendingAssessment - Get pending assessment for an owner - desc = NewDescriptor("getPendingAssessment", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.getPendingAssessment, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // collectTribute - Collect pending tribute - desc = NewDescriptor("collectTribute", smartcontract.BoolType, - manifest.NewParameter("assessmentID", smartcontract.IntegerType)) - md = NewMethodAndPrice(t.collectTribute, 1<<17, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // waiveTribute - Waive a tribute assessment (admin only) - desc = NewDescriptor("waiveTribute", smartcontract.BoolType, - manifest.NewParameter("assessmentID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(t.waiveTribute, 1<<16, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // appealTribute - Appeal a tribute assessment - desc = NewDescriptor("appealTribute", smartcontract.BoolType, - manifest.NewParameter("assessmentID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(t.appealTribute, 1<<16, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // ===== Incentive Management ===== - - // grantIncentive - Grant circulation incentive (system/admin) - desc = NewDescriptor("grantIncentive", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("incentiveType", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(t.grantIncentive, 1<<17, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // claimIncentive - Claim a granted incentive - desc = NewDescriptor("claimIncentive", smartcontract.BoolType, - manifest.NewParameter("incentiveID", smartcontract.IntegerType)) - md = NewMethodAndPrice(t.claimIncentive, 1<<17, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // getIncentive - Get incentive by ID - desc = NewDescriptor("getIncentive", smartcontract.ArrayType, - manifest.NewParameter("incentiveID", smartcontract.IntegerType)) - md = NewMethodAndPrice(t.getIncentive, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getUnclaimedIncentives - Get count of unclaimed incentives for owner - desc = NewDescriptor("getUnclaimedIncentives", smartcontract.IntegerType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(t.getUnclaimedIncentives, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // ===== Redistribution ===== - - // redistribute - Execute wealth redistribution (committee only) - desc = NewDescriptor("redistribute", smartcontract.IntegerType, - manifest.NewParameter("targetCategory", smartcontract.StringType), - manifest.NewParameter("recipientCount", smartcontract.IntegerType)) - md = NewMethodAndPrice(t.redistribute, 1<<18, callflag.States|callflag.AllowNotify) - t.AddMethod(md, desc) - - // getRedistribution - Get redistribution record by ID - desc = NewDescriptor("getRedistribution", smartcontract.ArrayType, - manifest.NewParameter("redistID", smartcontract.IntegerType)) - md = NewMethodAndPrice(t.getRedistribution, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getTributePool - Get total tribute pool available for redistribution - desc = NewDescriptor("getTributePool", smartcontract.IntegerType) - md = NewMethodAndPrice(t.getTributePool, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // ===== Configuration & Stats ===== - - // getConfig - Get current configuration - desc = NewDescriptor("getConfig", smartcontract.ArrayType) - md = NewMethodAndPrice(t.getConfig, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getTotalAccounts - Get total velocity accounts - desc = NewDescriptor("getTotalAccounts", smartcontract.IntegerType) - md = NewMethodAndPrice(t.getTotalAccounts, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getTotalAssessments - Get total assessments - desc = NewDescriptor("getTotalAssessments", smartcontract.IntegerType) - md = NewMethodAndPrice(t.getTotalAssessments, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getTotalIncentives - Get total incentives - desc = NewDescriptor("getTotalIncentives", smartcontract.IntegerType) - md = NewMethodAndPrice(t.getTotalIncentives, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // getTotalRedistributions - Get total redistributions - desc = NewDescriptor("getTotalRedistributions", smartcontract.IntegerType) - md = NewMethodAndPrice(t.getTotalRedistributions, 1<<15, callflag.ReadStates) - t.AddMethod(md, desc) - - // ===== Events ===== - - // VelocityAccountCreated event - eDesc := NewEventDescriptor(VelocityAccountCreatedEvent, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("owner", smartcontract.Hash160Type)) - t.AddEvent(NewEvent(eDesc)) - - // VelocityUpdated event - eDesc = NewEventDescriptor(VelocityUpdatedEvent, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("newVelocity", smartcontract.IntegerType), - manifest.NewParameter("hoardingLevel", smartcontract.IntegerType)) - t.AddEvent(NewEvent(eDesc)) - - // TributeAssessed event - eDesc = NewEventDescriptor(TributeAssessedEvent, - manifest.NewParameter("assessmentID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - t.AddEvent(NewEvent(eDesc)) - - // TributeCollected event - eDesc = NewEventDescriptor(TributeCollectedEvent, - manifest.NewParameter("assessmentID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - t.AddEvent(NewEvent(eDesc)) - - // TributeWaived event - eDesc = NewEventDescriptor(TributeWaivedEvent, - manifest.NewParameter("assessmentID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - t.AddEvent(NewEvent(eDesc)) - - // TributeAppealed event - eDesc = NewEventDescriptor(TributeAppealedEvent, - manifest.NewParameter("assessmentID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - t.AddEvent(NewEvent(eDesc)) - - // IncentiveGranted event - eDesc = NewEventDescriptor(IncentiveGrantedEvent, - manifest.NewParameter("incentiveID", smartcontract.IntegerType), - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - t.AddEvent(NewEvent(eDesc)) - - // IncentiveClaimed event - eDesc = NewEventDescriptor(IncentiveClaimedEvent, - manifest.NewParameter("incentiveID", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - t.AddEvent(NewEvent(eDesc)) - - // RedistributionExecuted event - eDesc = NewEventDescriptor(RedistributionExecutedEvent, - manifest.NewParameter("redistID", smartcontract.IntegerType), - manifest.NewParameter("totalAmount", smartcontract.IntegerType), - manifest.NewParameter("recipientCount", smartcontract.IntegerType)) - t.AddEvent(NewEvent(eDesc)) - - // ExemptionGranted event - eDesc = NewEventDescriptor(ExemptionGrantedEvent, - manifest.NewParameter("vitaID", smartcontract.IntegerType), - manifest.NewParameter("reason", smartcontract.StringType)) - t.AddEvent(NewEvent(eDesc)) - - // ExemptionRevoked event - eDesc = NewEventDescriptor(ExemptionRevokedEvent, - manifest.NewParameter("vitaID", smartcontract.IntegerType)) - t.AddEvent(NewEvent(eDesc)) - - return t -} - -// Metadata returns contract metadata. -func (t *Tribute) Metadata() *interop.ContractMD { - return &t.ContractMD -} - -// Initialize initializes the Tribute contract. -func (t *Tribute) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != t.ActiveIn() { - return nil - } - - // Initialize counters - t.setAccountCounter(ic.DAO, 0) - t.setAssessmentCounter(ic.DAO, 0) - t.setIncentiveCounter(ic.DAO, 0) - t.setRedistributionCounter(ic.DAO, 0) - t.setTributePool(ic.DAO, 0) - - // Initialize config with defaults - // Velocity in basis points (0-10000 = 0%-100%) - cfg := &state.TributeConfig{ - VelocityThresholdMild: 5000, // Below 50% velocity = mild hoarding - VelocityThresholdModerate: 3000, // Below 30% = moderate hoarding - VelocityThresholdSevere: 1500, // Below 15% = severe hoarding - VelocityThresholdExtreme: 500, // Below 5% = extreme hoarding - TributeRateMild: 100, // 1% tribute for mild hoarding - TributeRateModerate: 300, // 3% tribute for moderate hoarding - TributeRateSevere: 700, // 7% tribute for severe hoarding - TributeRateExtreme: 1500, // 15% tribute for extreme hoarding - IncentiveRateHigh: 50, // 0.5% incentive for high velocity - IncentiveRateVeryHigh: 150, // 1.5% incentive for very high velocity - StagnancyPeriod: 86400, // ~1 day (1-second blocks) before balance is stagnant - AssessmentPeriod: 604800, // ~7 days between assessments - GracePeriod: 259200, // ~3 days to pay tribute - MinBalanceForTribute: 1000000, // 1 VTS minimum to assess - ExemptionThreshold: 100000, // 0.1 VTS exempt - } - t.setConfig(ic.DAO, cfg) - - // Initialize cache - cache := &TributeCache{ - accountCount: 0, - assessmentCount: 0, - incentiveCount: 0, - redistributionCount: 0, - } - ic.DAO.SetCache(t.ID, cache) - - return nil -} - -// InitializeCache initializes the cache from storage. -func (t *Tribute) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cache := &TributeCache{ - accountCount: t.getAccountCounter(d), - assessmentCount: t.getAssessmentCounter(d), - incentiveCount: t.getIncentiveCounter(d), - redistributionCount: t.getRedistributionCounter(d), - } - d.SetCache(t.ID, cache) - return nil -} - -// OnPersist is called before block is committed. -func (t *Tribute) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist is called after block is committed. -func (t *Tribute) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn returns the hardfork at which this contract is activated. -func (t *Tribute) ActiveIn() *config.Hardfork { - return nil // Always active -} - -// ===== Storage Helpers ===== - -func (t *Tribute) makeAccountKey(vitaID uint64) []byte { - key := make([]byte, 9) - key[0] = tributePrefixAccount - binary.BigEndian.PutUint64(key[1:], vitaID) - return key -} - -func (t *Tribute) makeAccountByOwnerKey(owner util.Uint160) []byte { - key := make([]byte, 21) - key[0] = tributePrefixAccountByOwner - copy(key[1:], owner.BytesBE()) - return key -} - -func (t *Tribute) makeAssessmentKey(assessmentID uint64) []byte { - key := make([]byte, 9) - key[0] = tributePrefixAssessment - binary.BigEndian.PutUint64(key[1:], assessmentID) - return key -} - -func (t *Tribute) makePendingAssessmentKey(vitaID uint64) []byte { - key := make([]byte, 9) - key[0] = tributePrefixPendingAssessment - binary.BigEndian.PutUint64(key[1:], vitaID) - return key -} - -func (t *Tribute) makeIncentiveKey(incentiveID uint64) []byte { - key := make([]byte, 9) - key[0] = tributePrefixIncentive - binary.BigEndian.PutUint64(key[1:], incentiveID) - return key -} - -func (t *Tribute) makeUnclaimedIncentiveKey(vitaID, incentiveID uint64) []byte { - key := make([]byte, 17) - key[0] = tributePrefixUnclaimedIncentive - binary.BigEndian.PutUint64(key[1:], vitaID) - binary.BigEndian.PutUint64(key[9:], incentiveID) - return key -} - -func (t *Tribute) makeRedistributionKey(redistID uint64) []byte { - key := make([]byte, 9) - key[0] = tributePrefixRedistribution - binary.BigEndian.PutUint64(key[1:], redistID) - return key -} - -// ===== Counter Helpers ===== - -func (t *Tribute) getAccountCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(t.ID, []byte{tributePrefixAccountCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (t *Tribute) setAccountCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(t.ID, []byte{tributePrefixAccountCounter}, buf) -} - -func (t *Tribute) getAssessmentCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(t.ID, []byte{tributePrefixAssessmentCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (t *Tribute) setAssessmentCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(t.ID, []byte{tributePrefixAssessmentCounter}, buf) -} - -func (t *Tribute) getIncentiveCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(t.ID, []byte{tributePrefixIncentiveCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (t *Tribute) setIncentiveCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(t.ID, []byte{tributePrefixIncentiveCounter}, buf) -} - -func (t *Tribute) getRedistributionCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(t.ID, []byte{tributePrefixRedistributionCtr}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (t *Tribute) setRedistributionCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(t.ID, []byte{tributePrefixRedistributionCtr}, buf) -} - -func (t *Tribute) getTributePoolValue(d *dao.Simple) uint64 { - si := d.GetStorageItem(t.ID, []byte{tributePrefixTotalTributePool}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (t *Tribute) setTributePool(d *dao.Simple, amount uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, amount) - d.PutStorageItem(t.ID, []byte{tributePrefixTotalTributePool}, buf) -} - -// ===== Account Storage ===== - -func (t *Tribute) getAccountInternal(d *dao.Simple, vitaID uint64) (*state.VelocityAccount, error) { - si := d.GetStorageItem(t.ID, t.makeAccountKey(vitaID)) - if si == nil { - return nil, nil - } - acc := new(state.VelocityAccount) - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - if err := acc.FromStackItem(item); err != nil { - return nil, err - } - return acc, nil -} - -func (t *Tribute) putAccount(d *dao.Simple, acc *state.VelocityAccount) error { - data, err := stackitem.Serialize(acc.ToStackItem()) - if err != nil { - return err - } - d.PutStorageItem(t.ID, t.makeAccountKey(acc.VitaID), data) - return nil -} - -func (t *Tribute) getVitaIDByOwner(d *dao.Simple, owner util.Uint160) (uint64, bool) { - si := d.GetStorageItem(t.ID, t.makeAccountByOwnerKey(owner)) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint64(si), true -} - -func (t *Tribute) setOwnerMapping(d *dao.Simple, owner util.Uint160, vitaID uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, vitaID) - d.PutStorageItem(t.ID, t.makeAccountByOwnerKey(owner), buf) -} - -// ===== Assessment Storage ===== - -func (t *Tribute) getAssessmentInternal(d *dao.Simple, assessmentID uint64) (*state.TributeAssessment, error) { - si := d.GetStorageItem(t.ID, t.makeAssessmentKey(assessmentID)) - if si == nil { - return nil, nil - } - assess := new(state.TributeAssessment) - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - if err := assess.FromStackItem(item); err != nil { - return nil, err - } - return assess, nil -} - -func (t *Tribute) putAssessment(d *dao.Simple, assess *state.TributeAssessment) error { - data, err := stackitem.Serialize(assess.ToStackItem()) - if err != nil { - return err - } - d.PutStorageItem(t.ID, t.makeAssessmentKey(assess.ID), data) - return nil -} - -func (t *Tribute) getPendingAssessmentID(d *dao.Simple, vitaID uint64) (uint64, bool) { - si := d.GetStorageItem(t.ID, t.makePendingAssessmentKey(vitaID)) - if si == nil { - return 0, false - } - return binary.BigEndian.Uint64(si), true -} - -func (t *Tribute) setPendingAssessmentID(d *dao.Simple, vitaID, assessmentID uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, assessmentID) - d.PutStorageItem(t.ID, t.makePendingAssessmentKey(vitaID), buf) -} - -func (t *Tribute) deletePendingAssessment(d *dao.Simple, vitaID uint64) { - d.DeleteStorageItem(t.ID, t.makePendingAssessmentKey(vitaID)) -} - -// ===== Incentive Storage ===== - -func (t *Tribute) getIncentiveInternal(d *dao.Simple, incentiveID uint64) (*state.CirculationIncentive, error) { - si := d.GetStorageItem(t.ID, t.makeIncentiveKey(incentiveID)) - if si == nil { - return nil, nil - } - inc := new(state.CirculationIncentive) - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - if err := inc.FromStackItem(item); err != nil { - return nil, err - } - return inc, nil -} - -func (t *Tribute) putIncentive(d *dao.Simple, inc *state.CirculationIncentive) error { - data, err := stackitem.Serialize(inc.ToStackItem()) - if err != nil { - return err - } - d.PutStorageItem(t.ID, t.makeIncentiveKey(inc.ID), data) - return nil -} - -func (t *Tribute) addUnclaimedIncentive(d *dao.Simple, vitaID, incentiveID uint64) { - d.PutStorageItem(t.ID, t.makeUnclaimedIncentiveKey(vitaID, incentiveID), []byte{1}) -} - -func (t *Tribute) removeUnclaimedIncentive(d *dao.Simple, vitaID, incentiveID uint64) { - d.DeleteStorageItem(t.ID, t.makeUnclaimedIncentiveKey(vitaID, incentiveID)) -} - -// ===== Redistribution Storage ===== - -func (t *Tribute) getRedistributionInternal(d *dao.Simple, redistID uint64) (*state.RedistributionRecord, error) { - si := d.GetStorageItem(t.ID, t.makeRedistributionKey(redistID)) - if si == nil { - return nil, nil - } - rec := new(state.RedistributionRecord) - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - if err := rec.FromStackItem(item); err != nil { - return nil, err - } - return rec, nil -} - -func (t *Tribute) putRedistribution(d *dao.Simple, rec *state.RedistributionRecord) error { - data, err := stackitem.Serialize(rec.ToStackItem()) - if err != nil { - return err - } - d.PutStorageItem(t.ID, t.makeRedistributionKey(rec.ID), data) - return nil -} - -// ===== Config Storage ===== - -func (t *Tribute) getConfigInternal(d *dao.Simple) *state.TributeConfig { - si := d.GetStorageItem(t.ID, []byte{tributePrefixConfig}) - if si == nil { - return nil - } - cfg := new(state.TributeConfig) - item, err := stackitem.Deserialize(si) - if err != nil { - return nil - } - if err := cfg.FromStackItem(item); err != nil { - return nil - } - return cfg -} - -func (t *Tribute) setConfig(d *dao.Simple, cfg *state.TributeConfig) { - data, _ := stackitem.Serialize(cfg.ToStackItem()) - d.PutStorageItem(t.ID, []byte{tributePrefixConfig}, data) -} - -// ===== Internal Helpers ===== - -// calculateVelocity calculates velocity based on inflow/outflow ratio. -func (t *Tribute) calculateVelocity(totalInflow, totalOutflow uint64) uint64 { - if totalInflow == 0 { - if totalOutflow > 0 { - return 10000 // 100% velocity if only outflow - } - return 5000 // Default 50% if no activity - } - // Velocity = (outflow / inflow) * 10000 (basis points) - velocity := (totalOutflow * 10000) / totalInflow - if velocity > 10000 { - velocity = 10000 - } - return velocity -} - -// determineHoardingLevel determines hoarding level based on velocity. -func (t *Tribute) determineHoardingLevel(velocity uint64, cfg *state.TributeConfig) state.HoardingLevel { - if velocity >= cfg.VelocityThresholdMild { - return state.HoardingNone - } - if velocity >= cfg.VelocityThresholdModerate { - return state.HoardingMild - } - if velocity >= cfg.VelocityThresholdSevere { - return state.HoardingModerate - } - if velocity >= cfg.VelocityThresholdExtreme { - return state.HoardingSevere - } - return state.HoardingExtreme -} - -// getTributeRate returns the tribute rate for a hoarding level. -func (t *Tribute) getTributeRate(level state.HoardingLevel, cfg *state.TributeConfig) uint64 { - switch level { - case state.HoardingMild: - return cfg.TributeRateMild - case state.HoardingModerate: - return cfg.TributeRateModerate - case state.HoardingSevere: - return cfg.TributeRateSevere - case state.HoardingExtreme: - return cfg.TributeRateExtreme - default: - return 0 - } -} - -// ===== Contract Methods ===== - -// createVelocityAccount creates a velocity tracking account for a Vita holder. -func (t *Tribute) createVelocityAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - // Verify owner has active Vita - if t.Vita == nil { - panic(ErrTributeNoVita) - } - vita, err := t.Vita.GetTokenByOwner(ic.DAO, owner) - if err != nil || vita == nil { - panic(ErrTributeNoVita) - } - if vita.Status != state.TokenStatusActive { - panic(ErrTributeNoVita) - } - vitaID := vita.TokenID - - // Check if account already exists - existing, _ := t.getAccountInternal(ic.DAO, vitaID) - if existing != nil { - panic(ErrTributeAccountExists) - } - - // Create account - blockHeight := ic.Block.Index - acc := &state.VelocityAccount{ - VitaID: vitaID, - Owner: owner, - CurrentVelocity: 5000, // Default 50% velocity - AverageVelocity: 5000, - LastActivityBlock: blockHeight, - TotalInflow: 0, - TotalOutflow: 0, - StagnantBalance: 0, - HoardingLevel: state.HoardingNone, - ExemptionReason: "", - TotalTributePaid: 0, - TotalIncentivesRcvd: 0, - Status: state.VelocityAccountActive, - CreatedAt: blockHeight, - UpdatedAt: blockHeight, - } - - if err := t.putAccount(ic.DAO, acc); err != nil { - panic(err) - } - t.setOwnerMapping(ic.DAO, owner, vitaID) - - // Update counter - cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) - cache.accountCount++ - t.setAccountCounter(ic.DAO, cache.accountCount) - - // Emit event - ic.AddNotification(t.Hash, VelocityAccountCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), - stackitem.NewByteArray(owner.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -// getAccount returns velocity account by owner. -func (t *Tribute) getAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.Null{} - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - return stackitem.Null{} - } - - return acc.ToStackItem() -} - -// getAccountByVitaID returns velocity account by Vita ID. -func (t *Tribute) getAccountByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item { - vitaID := toBigInt(args[0]).Uint64() - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - return stackitem.Null{} - } - - return acc.ToStackItem() -} - -// recordTransaction records a transaction for velocity tracking. -func (t *Tribute) recordTransaction(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - amount := toBigInt(args[1]).Uint64() - isOutflow := toBool(args[2]) - - if amount == 0 { - panic(ErrTributeInvalidAmount) - } - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - panic(ErrTributeAccountNotFound) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - panic(ErrTributeAccountNotFound) - } - - if acc.Status == state.VelocityAccountSuspended { - panic(ErrTributeAccountSuspended) - } - - // Update transaction totals - if isOutflow { - acc.TotalOutflow += amount - } else { - acc.TotalInflow += amount - } - - // Recalculate velocity - acc.CurrentVelocity = t.calculateVelocity(acc.TotalInflow, acc.TotalOutflow) - - // Update rolling average (simple average for now) - acc.AverageVelocity = (acc.AverageVelocity + acc.CurrentVelocity) / 2 - - // Determine hoarding level - cfg := t.getConfigInternal(ic.DAO) - acc.HoardingLevel = t.determineHoardingLevel(acc.CurrentVelocity, cfg) - - // Update timestamp - acc.LastActivityBlock = ic.Block.Index - acc.UpdatedAt = ic.Block.Index - - if err := t.putAccount(ic.DAO, acc); err != nil { - panic(err) - } - - // Emit event - ic.AddNotification(t.Hash, VelocityUpdatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(acc.CurrentVelocity)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(acc.HoardingLevel))), - })) - - return stackitem.NewBool(true) -} - -// getVelocity returns current velocity score for an owner. -func (t *Tribute) getVelocity(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - return stackitem.NewBigInteger(new(big.Int).SetUint64(acc.CurrentVelocity)) -} - -// getHoardingLevel returns current hoarding level for an owner. -func (t *Tribute) getHoardingLevel(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - return stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(acc.HoardingLevel))) -} - -// grantExemption grants exemption from tribute (admin only). -func (t *Tribute) grantExemption(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - reason := toString(args[1]) - - if !t.checkTributeAdmin(ic) { - panic(ErrTributeNotAdmin) - } - - if len(reason) == 0 || len(reason) > 256 { - panic(ErrTributeInvalidReason) - } - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - panic(ErrTributeAccountNotFound) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - panic(ErrTributeAccountNotFound) - } - - acc.Status = state.VelocityAccountExempt - acc.ExemptionReason = reason - acc.UpdatedAt = ic.Block.Index - - if err := t.putAccount(ic.DAO, acc); err != nil { - panic(err) - } - - // Emit event - ic.AddNotification(t.Hash, ExemptionGrantedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -// revokeExemption revokes exemption from tribute (admin only). -func (t *Tribute) revokeExemption(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - if !t.checkTributeAdmin(ic) { - panic(ErrTributeNotAdmin) - } - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - panic(ErrTributeAccountNotFound) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - panic(ErrTributeAccountNotFound) - } - - if acc.Status != state.VelocityAccountExempt { - panic(ErrTributeAccountNotFound) - } - - acc.Status = state.VelocityAccountActive - acc.ExemptionReason = "" - acc.UpdatedAt = ic.Block.Index - - if err := t.putAccount(ic.DAO, acc); err != nil { - panic(err) - } - - // Emit event - ic.AddNotification(t.Hash, ExemptionRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), - })) - - return stackitem.NewBool(true) -} - -// isExempt checks if account is exempt from tribute. -func (t *Tribute) isExempt(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.NewBool(false) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - return stackitem.NewBool(false) - } - - return stackitem.NewBool(acc.Status == state.VelocityAccountExempt) -} - -// assessTribute assesses tribute for hoarding. -func (t *Tribute) assessTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - panic(ErrTributeAccountNotFound) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - panic(ErrTributeAccountNotFound) - } - - if acc.Status == state.VelocityAccountExempt { - panic(ErrTributeAccountExempt) - } - - if acc.Status == state.VelocityAccountSuspended { - panic(ErrTributeAccountSuspended) - } - - // Check if there's already a pending assessment - if existingID, exists := t.getPendingAssessmentID(ic.DAO, vitaID); exists { - existing, _ := t.getAssessmentInternal(ic.DAO, existingID) - if existing != nil && existing.Status == state.AssessmentPending { - panic("pending assessment already exists") - } - } - - // Check hoarding level - if acc.HoardingLevel == state.HoardingNone { - panic(ErrTributeNoHoarding) - } - - cfg := t.getConfigInternal(ic.DAO) - - // Get stagnant balance (simplified: use total inflow - outflow as approximation) - stagnantBalance := uint64(0) - if acc.TotalInflow > acc.TotalOutflow { - stagnantBalance = acc.TotalInflow - acc.TotalOutflow - } - - if stagnantBalance < cfg.MinBalanceForTribute { - panic(ErrTributeBelowExemption) - } - - // Calculate tribute - tributeRate := t.getTributeRate(acc.HoardingLevel, cfg) - tributeAmount := (stagnantBalance * tributeRate) / 10000 - - // Create assessment - cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) - assessmentID := cache.assessmentCount - cache.assessmentCount++ - t.setAssessmentCounter(ic.DAO, cache.assessmentCount) - - blockHeight := ic.Block.Index - assessment := &state.TributeAssessment{ - ID: assessmentID, - VitaID: vitaID, - Owner: owner, - AssessmentBlock: blockHeight, - HoardingLevel: acc.HoardingLevel, - StagnantAmount: stagnantBalance, - TributeRate: tributeRate, - TributeAmount: tributeAmount, - DueBlock: blockHeight + cfg.GracePeriod, - CollectedBlock: 0, - Status: state.AssessmentPending, - AppealReason: "", - } - - if err := t.putAssessment(ic.DAO, assessment); err != nil { - panic(err) - } - t.setPendingAssessmentID(ic.DAO, vitaID, assessmentID) - - // Emit event - ic.AddNotification(t.Hash, TributeAssessedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(tributeAmount)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)) -} - -// getAssessment returns assessment by ID. -func (t *Tribute) getAssessment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - assessmentID := toBigInt(args[0]).Uint64() - - assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) - if err != nil || assess == nil { - return stackitem.Null{} - } - - return assess.ToStackItem() -} - -// getPendingAssessment returns pending assessment for an owner. -func (t *Tribute) getPendingAssessment(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.Null{} - } - - assessmentID, exists := t.getPendingAssessmentID(ic.DAO, vitaID) - if !exists { - return stackitem.Null{} - } - - assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) - if err != nil || assess == nil { - return stackitem.Null{} - } - - if assess.Status != state.AssessmentPending { - return stackitem.Null{} - } - - return assess.ToStackItem() -} - -// collectTribute collects pending tribute. -func (t *Tribute) collectTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - assessmentID := toBigInt(args[0]).Uint64() - - assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) - if err != nil || assess == nil { - panic(ErrTributeAssessmentNotFound) - } - - if assess.Status != state.AssessmentPending { - panic(ErrTributeAssessmentNotPending) - } - - // Check property right before taking tribute - if !t.checkPropertyRight(ic, assess.Owner) { - panic(ErrTributePropertyRestricted) - } - - // Mark as collected - assess.Status = state.AssessmentCollected - assess.CollectedBlock = ic.Block.Index - - if err := t.putAssessment(ic.DAO, assess); err != nil { - panic(err) - } - t.deletePendingAssessment(ic.DAO, assess.VitaID) - - // Update account - acc, _ := t.getAccountInternal(ic.DAO, assess.VitaID) - if acc != nil { - acc.TotalTributePaid += assess.TributeAmount - acc.UpdatedAt = ic.Block.Index - t.putAccount(ic.DAO, acc) - } - - // Add to tribute pool for redistribution - pool := t.getTributePoolValue(ic.DAO) - pool += assess.TributeAmount - t.setTributePool(ic.DAO, pool) - - // Emit event - ic.AddNotification(t.Hash, TributeCollectedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(assess.TributeAmount)), - })) - - return stackitem.NewBool(true) -} - -// waiveTribute waives a tribute assessment (admin only). -func (t *Tribute) waiveTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - assessmentID := toBigInt(args[0]).Uint64() - reason := toString(args[1]) - - if !t.checkTributeAdmin(ic) { - panic(ErrTributeNotAdmin) - } - - if len(reason) == 0 || len(reason) > 256 { - panic(ErrTributeInvalidReason) - } - - assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) - if err != nil || assess == nil { - panic(ErrTributeAssessmentNotFound) - } - - if assess.Status != state.AssessmentPending && assess.Status != state.AssessmentAppealed { - panic(ErrTributeAssessmentNotPending) - } - - assess.Status = state.AssessmentWaived - assess.AppealReason = reason - - if err := t.putAssessment(ic.DAO, assess); err != nil { - panic(err) - } - t.deletePendingAssessment(ic.DAO, assess.VitaID) - - // Emit event - ic.AddNotification(t.Hash, TributeWaivedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -// appealTribute appeals a tribute assessment. -func (t *Tribute) appealTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - assessmentID := toBigInt(args[0]).Uint64() - reason := toString(args[1]) - - if len(reason) == 0 || len(reason) > 512 { - panic(ErrTributeInvalidReason) - } - - assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) - if err != nil || assess == nil { - panic(ErrTributeAssessmentNotFound) - } - - // Check caller is owner - ok, err := checkWitness(ic, assess.Owner) - if err != nil || !ok { - panic(ErrTributeNotOwner) - } - - if assess.Status != state.AssessmentPending { - panic(ErrTributeAssessmentNotPending) - } - - assess.Status = state.AssessmentAppealed - assess.AppealReason = reason - - if err := t.putAssessment(ic.DAO, assess); err != nil { - panic(err) - } - - // Emit event - ic.AddNotification(t.Hash, TributeAppealedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), - stackitem.NewByteArray([]byte(reason)), - })) - - return stackitem.NewBool(true) -} - -// grantIncentive grants circulation incentive (system/admin). -func (t *Tribute) grantIncentive(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - incentiveType := state.IncentiveType(toBigInt(args[1]).Uint64()) - amount := toBigInt(args[2]).Uint64() - reason := toString(args[3]) - - if !t.checkTributeAdmin(ic) { - panic(ErrTributeNotAdmin) - } - - if amount == 0 { - panic(ErrTributeInvalidAmount) - } - - if len(reason) == 0 || len(reason) > 256 { - panic(ErrTributeInvalidReason) - } - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - panic(ErrTributeAccountNotFound) - } - - acc, err := t.getAccountInternal(ic.DAO, vitaID) - if err != nil || acc == nil { - panic(ErrTributeAccountNotFound) - } - - // Create incentive - cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) - incentiveID := cache.incentiveCount - cache.incentiveCount++ - t.setIncentiveCounter(ic.DAO, cache.incentiveCount) - - blockHeight := ic.Block.Index - incentive := &state.CirculationIncentive{ - ID: incentiveID, - VitaID: vitaID, - Recipient: owner, - IncentiveType: incentiveType, - Amount: amount, - Reason: reason, - VelocityScore: acc.CurrentVelocity, - GrantedBlock: blockHeight, - ClaimedBlock: 0, - Claimed: false, - } - - if err := t.putIncentive(ic.DAO, incentive); err != nil { - panic(err) - } - t.addUnclaimedIncentive(ic.DAO, vitaID, incentiveID) - - // Emit event - ic.AddNotification(t.Hash, IncentiveGrantedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(incentiveID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(amount)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(incentiveID)) -} - -// claimIncentive claims a granted incentive. -func (t *Tribute) claimIncentive(ic *interop.Context, args []stackitem.Item) stackitem.Item { - incentiveID := toBigInt(args[0]).Uint64() - - incentive, err := t.getIncentiveInternal(ic.DAO, incentiveID) - if err != nil || incentive == nil { - panic(ErrTributeIncentiveNotFound) - } - - // Check caller is recipient - ok, err := checkWitness(ic, incentive.Recipient) - if err != nil || !ok { - panic(ErrTributeNotOwner) - } - - if incentive.Claimed { - panic(ErrTributeIncentiveClaimed) - } - - // Mark as claimed - incentive.Claimed = true - incentive.ClaimedBlock = ic.Block.Index - - if err := t.putIncentive(ic.DAO, incentive); err != nil { - panic(err) - } - t.removeUnclaimedIncentive(ic.DAO, incentive.VitaID, incentiveID) - - // Update account - acc, _ := t.getAccountInternal(ic.DAO, incentive.VitaID) - if acc != nil { - acc.TotalIncentivesRcvd += incentive.Amount - acc.UpdatedAt = ic.Block.Index - t.putAccount(ic.DAO, acc) - } - - // Emit event - ic.AddNotification(t.Hash, IncentiveClaimedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(incentiveID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(incentive.Amount)), - })) - - return stackitem.NewBool(true) -} - -// getIncentive returns incentive by ID. -func (t *Tribute) getIncentive(ic *interop.Context, args []stackitem.Item) stackitem.Item { - incentiveID := toBigInt(args[0]).Uint64() - - incentive, err := t.getIncentiveInternal(ic.DAO, incentiveID) - if err != nil || incentive == nil { - return stackitem.Null{} - } - - return incentive.ToStackItem() -} - -// getUnclaimedIncentives returns count of unclaimed incentives for owner. -func (t *Tribute) getUnclaimedIncentives(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) - if !found { - return stackitem.NewBigInteger(big.NewInt(0)) - } - - // Count unclaimed incentives by iterating storage - // This is a simplified version - in production, we'd track this counter - count := uint64(0) - prefix := make([]byte, 9) - prefix[0] = tributePrefixUnclaimedIncentive - binary.BigEndian.PutUint64(prefix[1:], vitaID) - - ic.DAO.Seek(t.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - count++ - return true - }) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) -} - -// redistribute executes wealth redistribution (committee only). -func (t *Tribute) redistribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - targetCategory := toString(args[0]) - recipientCount := toBigInt(args[1]).Uint64() - - if !t.checkCommittee(ic) { - panic(ErrTributeNotCommittee) - } - - pool := t.getTributePoolValue(ic.DAO) - if pool == 0 { - panic(ErrTributeNothingToRedistribute) - } - - if recipientCount == 0 { - panic(ErrTributeInvalidAmount) - } - - perCapita := pool / recipientCount - - // Create redistribution record - cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) - redistID := cache.redistributionCount - cache.redistributionCount++ - t.setRedistributionCounter(ic.DAO, cache.redistributionCount) - - record := &state.RedistributionRecord{ - ID: redistID, - SourceAssessment: 0, // Could link to specific assessment if needed - TotalAmount: pool, - RecipientCount: recipientCount, - PerCapitaAmount: perCapita, - RedistBlock: ic.Block.Index, - TargetCategory: targetCategory, - } - - if err := t.putRedistribution(ic.DAO, record); err != nil { - panic(err) - } - - // Clear the pool (actual redistribution would happen via VTS transfers) - t.setTributePool(ic.DAO, 0) - - // Emit event - ic.AddNotification(t.Hash, RedistributionExecutedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(redistID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(pool)), - stackitem.NewBigInteger(new(big.Int).SetUint64(recipientCount)), - })) - - return stackitem.NewBigInteger(new(big.Int).SetUint64(redistID)) -} - -// getRedistribution returns redistribution record by ID. -func (t *Tribute) getRedistribution(ic *interop.Context, args []stackitem.Item) stackitem.Item { - redistID := toBigInt(args[0]).Uint64() - - rec, err := t.getRedistributionInternal(ic.DAO, redistID) - if err != nil || rec == nil { - return stackitem.Null{} - } - - return rec.ToStackItem() -} - -// getTributePool returns total tribute pool available for redistribution. -func (t *Tribute) getTributePool(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getTributePoolValue(ic.DAO))) -} - -// getConfig returns current configuration. -func (t *Tribute) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - cfg := t.getConfigInternal(ic.DAO) - if cfg == nil { - return stackitem.Null{} - } - return cfg.ToStackItem() -} - -// getTotalAccounts returns total velocity accounts. -func (t *Tribute) getTotalAccounts(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getAccountCounter(ic.DAO))) -} - -// getTotalAssessments returns total assessments. -func (t *Tribute) getTotalAssessments(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getAssessmentCounter(ic.DAO))) -} - -// getTotalIncentives returns total incentives. -func (t *Tribute) getTotalIncentives(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getIncentiveCounter(ic.DAO))) -} - -// getTotalRedistributions returns total redistributions. -func (t *Tribute) getTotalRedistributions(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getRedistributionCounter(ic.DAO))) -} - -// ===== Public Internal Methods ===== - -// GetAccountByOwner returns velocity account by owner (internal API). -func (t *Tribute) GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.VelocityAccount, error) { - vitaID, found := t.getVitaIDByOwner(d, owner) - if !found { - return nil, ErrTributeAccountNotFound - } - return t.getAccountInternal(d, vitaID) -} - -// GetVelocity returns velocity score for an owner (internal API). -func (t *Tribute) GetVelocity(d *dao.Simple, owner util.Uint160) uint64 { - vitaID, found := t.getVitaIDByOwner(d, owner) - if !found { - return 5000 // Default 50% - } - acc, err := t.getAccountInternal(d, vitaID) - if err != nil || acc == nil { - return 5000 - } - return acc.CurrentVelocity -} - -// IsHoarding returns true if owner is hoarding (internal API). -func (t *Tribute) IsHoarding(d *dao.Simple, owner util.Uint160) bool { - vitaID, found := t.getVitaIDByOwner(d, owner) - if !found { - return false - } - acc, err := t.getAccountInternal(d, vitaID) - if err != nil || acc == nil { - return false - } - return acc.HoardingLevel != state.HoardingNone -} - -// Address returns the contract address. -func (t *Tribute) Address() util.Uint160 { - return t.Hash -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Tribute represents the anti-hoarding economics native contract. +type Tribute struct { + interop.ContractMD + Tutus ITutus + Vita IVita + VTS IVTS + RoleRegistry IRoleRegistry + Lex ILex +} + +// TributeCache represents the cached state for Tribute contract. +type TributeCache struct { + accountCount uint64 + assessmentCount uint64 + incentiveCount uint64 + redistributionCount uint64 +} + +// Storage key prefixes for Tribute. +const ( + tributePrefixAccount byte = 0x01 // vitaID -> VelocityAccount + tributePrefixAccountByOwner byte = 0x02 // owner -> vitaID + tributePrefixAssessment byte = 0x10 // assessmentID -> TributeAssessment + tributePrefixAssessmentByOwner byte = 0x11 // vitaID + assessmentID -> exists + tributePrefixPendingAssessment byte = 0x12 // vitaID -> latest pending assessmentID + tributePrefixIncentive byte = 0x20 // incentiveID -> CirculationIncentive + tributePrefixIncentiveByOwner byte = 0x21 // vitaID + incentiveID -> exists + tributePrefixUnclaimedIncentive byte = 0x22 // vitaID + incentiveID -> exists (unclaimed only) + tributePrefixRedistribution byte = 0x30 // redistID -> RedistributionRecord + tributePrefixAccountCounter byte = 0xF0 // -> uint64 + tributePrefixAssessmentCounter byte = 0xF1 // -> next assessment ID + tributePrefixIncentiveCounter byte = 0xF2 // -> next incentive ID + tributePrefixRedistributionCtr byte = 0xF3 // -> next redistribution ID + tributePrefixTotalTributePool byte = 0xF8 // -> total tribute collected for redistribution + tributePrefixConfig byte = 0xFF // -> TributeConfig +) + +// Event names for Tribute. +const ( + VelocityAccountCreatedEvent = "VelocityAccountCreated" + VelocityUpdatedEvent = "VelocityUpdated" + TributeAssessedEvent = "TributeAssessed" + TributeCollectedEvent = "TributeCollected" + TributeWaivedEvent = "TributeWaived" + TributeAppealedEvent = "TributeAppealed" + IncentiveGrantedEvent = "IncentiveGranted" + IncentiveClaimedEvent = "IncentiveClaimed" + RedistributionExecutedEvent = "RedistributionExecuted" + ExemptionGrantedEvent = "ExemptionGranted" + ExemptionRevokedEvent = "ExemptionRevoked" +) + +// Role constants for tribute administrators. +const ( + RoleTributeAdmin uint64 = 23 // Can manage exemptions and appeals +) + +// Various errors for Tribute. +var ( + ErrTributeAccountNotFound = errors.New("velocity account not found") + ErrTributeAccountExists = errors.New("velocity account already exists") + ErrTributeAccountExempt = errors.New("account is exempt from tribute") + ErrTributeAccountSuspended = errors.New("velocity account is suspended") + ErrTributeNoVita = errors.New("owner must have an active Vita") + ErrTributeAssessmentNotFound = errors.New("tribute assessment not found") + ErrTributeAssessmentNotPending = errors.New("assessment is not pending") + ErrTributeAssessmentAlreadyPaid = errors.New("assessment already collected") + ErrTributeIncentiveNotFound = errors.New("incentive not found") + ErrTributeIncentiveClaimed = errors.New("incentive already claimed") + ErrTributeInsufficientBalance = errors.New("insufficient balance for tribute") + ErrTributeNotCommittee = errors.New("invalid committee signature") + ErrTributeNotOwner = errors.New("caller is not the owner") + ErrTributeNotAdmin = errors.New("caller is not an authorized tribute admin") + ErrTributePropertyRestricted = errors.New("property right is restricted") + ErrTributeInvalidAmount = errors.New("invalid amount") + ErrTributeInvalidReason = errors.New("invalid reason") + ErrTributeBelowExemption = errors.New("balance below exemption threshold") + ErrTributeNoHoarding = errors.New("no hoarding detected") + ErrTributeNothingToRedistribute = errors.New("nothing to redistribute") +) + +var ( + _ interop.Contract = (*Tribute)(nil) + _ dao.NativeContractCache = (*TributeCache)(nil) +) + +// Copy implements NativeContractCache interface. +func (c *TributeCache) Copy() dao.NativeContractCache { + return &TributeCache{ + accountCount: c.accountCount, + assessmentCount: c.assessmentCount, + incentiveCount: c.incentiveCount, + redistributionCount: c.redistributionCount, + } +} + +// checkCommittee checks if the caller has committee authority. +func (t *Tribute) checkCommittee(ic *interop.Context) bool { + if t.RoleRegistry != nil { + return t.RoleRegistry.CheckCommittee(ic) + } + return t.Tutus.CheckCommittee(ic) +} + +// checkTributeAdmin checks if the caller has tribute admin authority. +func (t *Tribute) checkTributeAdmin(ic *interop.Context) bool { + caller := ic.VM.GetCallingScriptHash() + if t.RoleRegistry != nil { + if t.RoleRegistry.HasRoleInternal(ic.DAO, caller, RoleTributeAdmin, ic.Block.Index) { + return true + } + } + // Committee members can also act as tribute admins + return t.checkCommittee(ic) +} + +// checkPropertyRight checks if subject has property rights via Lex. +func (t *Tribute) checkPropertyRight(ic *interop.Context, subject util.Uint160) bool { + if t.Lex == nil { + return true // Allow if Lex not available + } + return t.Lex.HasRightInternal(ic.DAO, subject, state.RightProperty, ic.Block.Index) +} + +// newTribute creates a new Tribute native contract. +func newTribute() *Tribute { + t := &Tribute{ + ContractMD: *interop.NewContractMD(nativenames.Tribute, nativeids.Tribute), + } + defer t.BuildHFSpecificMD(t.ActiveIn()) + + // ===== Account Management ===== + + // createVelocityAccount - Create velocity tracking account for a Vita holder + desc := NewDescriptor("createVelocityAccount", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md := NewMethodAndPrice(t.createVelocityAccount, 1<<17, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // getAccount - Get velocity account by owner + desc = NewDescriptor("getAccount", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.getAccount, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getAccountByVitaID - Get account by Vita ID + desc = NewDescriptor("getAccountByVitaID", smartcontract.ArrayType, + manifest.NewParameter("vitaID", smartcontract.IntegerType)) + md = NewMethodAndPrice(t.getAccountByVitaID, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // recordTransaction - Record a transaction for velocity tracking + desc = NewDescriptor("recordTransaction", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("isOutflow", smartcontract.BoolType)) + md = NewMethodAndPrice(t.recordTransaction, 1<<16, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // getVelocity - Get current velocity score for an owner + desc = NewDescriptor("getVelocity", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.getVelocity, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getHoardingLevel - Get current hoarding level for an owner + desc = NewDescriptor("getHoardingLevel", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.getHoardingLevel, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // ===== Exemption Management ===== + + // grantExemption - Grant exemption from tribute (admin only) + desc = NewDescriptor("grantExemption", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(t.grantExemption, 1<<16, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // revokeExemption - Revoke exemption from tribute (admin only) + desc = NewDescriptor("revokeExemption", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.revokeExemption, 1<<16, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // isExempt - Check if account is exempt + desc = NewDescriptor("isExempt", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.isExempt, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // ===== Assessment Management ===== + + // assessTribute - Assess tribute for hoarding (called periodically or on-demand) + desc = NewDescriptor("assessTribute", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.assessTribute, 1<<17, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // getAssessment - Get assessment by ID + desc = NewDescriptor("getAssessment", smartcontract.ArrayType, + manifest.NewParameter("assessmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(t.getAssessment, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getPendingAssessment - Get pending assessment for an owner + desc = NewDescriptor("getPendingAssessment", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.getPendingAssessment, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // collectTribute - Collect pending tribute + desc = NewDescriptor("collectTribute", smartcontract.BoolType, + manifest.NewParameter("assessmentID", smartcontract.IntegerType)) + md = NewMethodAndPrice(t.collectTribute, 1<<17, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // waiveTribute - Waive a tribute assessment (admin only) + desc = NewDescriptor("waiveTribute", smartcontract.BoolType, + manifest.NewParameter("assessmentID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(t.waiveTribute, 1<<16, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // appealTribute - Appeal a tribute assessment + desc = NewDescriptor("appealTribute", smartcontract.BoolType, + manifest.NewParameter("assessmentID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(t.appealTribute, 1<<16, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // ===== Incentive Management ===== + + // grantIncentive - Grant circulation incentive (system/admin) + desc = NewDescriptor("grantIncentive", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("incentiveType", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(t.grantIncentive, 1<<17, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // claimIncentive - Claim a granted incentive + desc = NewDescriptor("claimIncentive", smartcontract.BoolType, + manifest.NewParameter("incentiveID", smartcontract.IntegerType)) + md = NewMethodAndPrice(t.claimIncentive, 1<<17, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // getIncentive - Get incentive by ID + desc = NewDescriptor("getIncentive", smartcontract.ArrayType, + manifest.NewParameter("incentiveID", smartcontract.IntegerType)) + md = NewMethodAndPrice(t.getIncentive, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getUnclaimedIncentives - Get count of unclaimed incentives for owner + desc = NewDescriptor("getUnclaimedIncentives", smartcontract.IntegerType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(t.getUnclaimedIncentives, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // ===== Redistribution ===== + + // redistribute - Execute wealth redistribution (committee only) + desc = NewDescriptor("redistribute", smartcontract.IntegerType, + manifest.NewParameter("targetCategory", smartcontract.StringType), + manifest.NewParameter("recipientCount", smartcontract.IntegerType)) + md = NewMethodAndPrice(t.redistribute, 1<<18, callflag.States|callflag.AllowNotify) + t.AddMethod(md, desc) + + // getRedistribution - Get redistribution record by ID + desc = NewDescriptor("getRedistribution", smartcontract.ArrayType, + manifest.NewParameter("redistID", smartcontract.IntegerType)) + md = NewMethodAndPrice(t.getRedistribution, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getTributePool - Get total tribute pool available for redistribution + desc = NewDescriptor("getTributePool", smartcontract.IntegerType) + md = NewMethodAndPrice(t.getTributePool, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // ===== Configuration & Stats ===== + + // getConfig - Get current configuration + desc = NewDescriptor("getConfig", smartcontract.ArrayType) + md = NewMethodAndPrice(t.getConfig, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getTotalAccounts - Get total velocity accounts + desc = NewDescriptor("getTotalAccounts", smartcontract.IntegerType) + md = NewMethodAndPrice(t.getTotalAccounts, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getTotalAssessments - Get total assessments + desc = NewDescriptor("getTotalAssessments", smartcontract.IntegerType) + md = NewMethodAndPrice(t.getTotalAssessments, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getTotalIncentives - Get total incentives + desc = NewDescriptor("getTotalIncentives", smartcontract.IntegerType) + md = NewMethodAndPrice(t.getTotalIncentives, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // getTotalRedistributions - Get total redistributions + desc = NewDescriptor("getTotalRedistributions", smartcontract.IntegerType) + md = NewMethodAndPrice(t.getTotalRedistributions, 1<<15, callflag.ReadStates) + t.AddMethod(md, desc) + + // ===== Events ===== + + // VelocityAccountCreated event + eDesc := NewEventDescriptor(VelocityAccountCreatedEvent, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("owner", smartcontract.Hash160Type)) + t.AddEvent(NewEvent(eDesc)) + + // VelocityUpdated event + eDesc = NewEventDescriptor(VelocityUpdatedEvent, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("newVelocity", smartcontract.IntegerType), + manifest.NewParameter("hoardingLevel", smartcontract.IntegerType)) + t.AddEvent(NewEvent(eDesc)) + + // TributeAssessed event + eDesc = NewEventDescriptor(TributeAssessedEvent, + manifest.NewParameter("assessmentID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + t.AddEvent(NewEvent(eDesc)) + + // TributeCollected event + eDesc = NewEventDescriptor(TributeCollectedEvent, + manifest.NewParameter("assessmentID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + t.AddEvent(NewEvent(eDesc)) + + // TributeWaived event + eDesc = NewEventDescriptor(TributeWaivedEvent, + manifest.NewParameter("assessmentID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + t.AddEvent(NewEvent(eDesc)) + + // TributeAppealed event + eDesc = NewEventDescriptor(TributeAppealedEvent, + manifest.NewParameter("assessmentID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + t.AddEvent(NewEvent(eDesc)) + + // IncentiveGranted event + eDesc = NewEventDescriptor(IncentiveGrantedEvent, + manifest.NewParameter("incentiveID", smartcontract.IntegerType), + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + t.AddEvent(NewEvent(eDesc)) + + // IncentiveClaimed event + eDesc = NewEventDescriptor(IncentiveClaimedEvent, + manifest.NewParameter("incentiveID", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + t.AddEvent(NewEvent(eDesc)) + + // RedistributionExecuted event + eDesc = NewEventDescriptor(RedistributionExecutedEvent, + manifest.NewParameter("redistID", smartcontract.IntegerType), + manifest.NewParameter("totalAmount", smartcontract.IntegerType), + manifest.NewParameter("recipientCount", smartcontract.IntegerType)) + t.AddEvent(NewEvent(eDesc)) + + // ExemptionGranted event + eDesc = NewEventDescriptor(ExemptionGrantedEvent, + manifest.NewParameter("vitaID", smartcontract.IntegerType), + manifest.NewParameter("reason", smartcontract.StringType)) + t.AddEvent(NewEvent(eDesc)) + + // ExemptionRevoked event + eDesc = NewEventDescriptor(ExemptionRevokedEvent, + manifest.NewParameter("vitaID", smartcontract.IntegerType)) + t.AddEvent(NewEvent(eDesc)) + + return t +} + +// Metadata returns contract metadata. +func (t *Tribute) Metadata() *interop.ContractMD { + return &t.ContractMD +} + +// Initialize initializes the Tribute contract. +func (t *Tribute) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != t.ActiveIn() { + return nil + } + + // Initialize counters + t.setAccountCounter(ic.DAO, 0) + t.setAssessmentCounter(ic.DAO, 0) + t.setIncentiveCounter(ic.DAO, 0) + t.setRedistributionCounter(ic.DAO, 0) + t.setTributePool(ic.DAO, 0) + + // Initialize config with defaults + // Velocity in basis points (0-10000 = 0%-100%) + cfg := &state.TributeConfig{ + VelocityThresholdMild: 5000, // Below 50% velocity = mild hoarding + VelocityThresholdModerate: 3000, // Below 30% = moderate hoarding + VelocityThresholdSevere: 1500, // Below 15% = severe hoarding + VelocityThresholdExtreme: 500, // Below 5% = extreme hoarding + TributeRateMild: 100, // 1% tribute for mild hoarding + TributeRateModerate: 300, // 3% tribute for moderate hoarding + TributeRateSevere: 700, // 7% tribute for severe hoarding + TributeRateExtreme: 1500, // 15% tribute for extreme hoarding + IncentiveRateHigh: 50, // 0.5% incentive for high velocity + IncentiveRateVeryHigh: 150, // 1.5% incentive for very high velocity + StagnancyPeriod: 86400, // ~1 day (1-second blocks) before balance is stagnant + AssessmentPeriod: 604800, // ~7 days between assessments + GracePeriod: 259200, // ~3 days to pay tribute + MinBalanceForTribute: 1000000, // 1 VTS minimum to assess + ExemptionThreshold: 100000, // 0.1 VTS exempt + } + t.setConfig(ic.DAO, cfg) + + // Initialize cache + cache := &TributeCache{ + accountCount: 0, + assessmentCount: 0, + incentiveCount: 0, + redistributionCount: 0, + } + ic.DAO.SetCache(t.ID, cache) + + return nil +} + +// InitializeCache initializes the cache from storage. +func (t *Tribute) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cache := &TributeCache{ + accountCount: t.getAccountCounter(d), + assessmentCount: t.getAssessmentCounter(d), + incentiveCount: t.getIncentiveCounter(d), + redistributionCount: t.getRedistributionCounter(d), + } + d.SetCache(t.ID, cache) + return nil +} + +// OnPersist is called before block is committed. +func (t *Tribute) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist is called after block is committed. +func (t *Tribute) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn returns the hardfork at which this contract is activated. +func (t *Tribute) ActiveIn() *config.Hardfork { + return nil // Always active +} + +// ===== Storage Helpers ===== + +func (t *Tribute) makeAccountKey(vitaID uint64) []byte { + key := make([]byte, 9) + key[0] = tributePrefixAccount + binary.BigEndian.PutUint64(key[1:], vitaID) + return key +} + +func (t *Tribute) makeAccountByOwnerKey(owner util.Uint160) []byte { + key := make([]byte, 21) + key[0] = tributePrefixAccountByOwner + copy(key[1:], owner.BytesBE()) + return key +} + +func (t *Tribute) makeAssessmentKey(assessmentID uint64) []byte { + key := make([]byte, 9) + key[0] = tributePrefixAssessment + binary.BigEndian.PutUint64(key[1:], assessmentID) + return key +} + +func (t *Tribute) makePendingAssessmentKey(vitaID uint64) []byte { + key := make([]byte, 9) + key[0] = tributePrefixPendingAssessment + binary.BigEndian.PutUint64(key[1:], vitaID) + return key +} + +func (t *Tribute) makeIncentiveKey(incentiveID uint64) []byte { + key := make([]byte, 9) + key[0] = tributePrefixIncentive + binary.BigEndian.PutUint64(key[1:], incentiveID) + return key +} + +func (t *Tribute) makeUnclaimedIncentiveKey(vitaID, incentiveID uint64) []byte { + key := make([]byte, 17) + key[0] = tributePrefixUnclaimedIncentive + binary.BigEndian.PutUint64(key[1:], vitaID) + binary.BigEndian.PutUint64(key[9:], incentiveID) + return key +} + +func (t *Tribute) makeRedistributionKey(redistID uint64) []byte { + key := make([]byte, 9) + key[0] = tributePrefixRedistribution + binary.BigEndian.PutUint64(key[1:], redistID) + return key +} + +// ===== Counter Helpers ===== + +func (t *Tribute) getAccountCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(t.ID, []byte{tributePrefixAccountCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (t *Tribute) setAccountCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(t.ID, []byte{tributePrefixAccountCounter}, buf) +} + +func (t *Tribute) getAssessmentCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(t.ID, []byte{tributePrefixAssessmentCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (t *Tribute) setAssessmentCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(t.ID, []byte{tributePrefixAssessmentCounter}, buf) +} + +func (t *Tribute) getIncentiveCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(t.ID, []byte{tributePrefixIncentiveCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (t *Tribute) setIncentiveCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(t.ID, []byte{tributePrefixIncentiveCounter}, buf) +} + +func (t *Tribute) getRedistributionCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(t.ID, []byte{tributePrefixRedistributionCtr}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (t *Tribute) setRedistributionCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(t.ID, []byte{tributePrefixRedistributionCtr}, buf) +} + +func (t *Tribute) getTributePoolValue(d *dao.Simple) uint64 { + si := d.GetStorageItem(t.ID, []byte{tributePrefixTotalTributePool}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (t *Tribute) setTributePool(d *dao.Simple, amount uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, amount) + d.PutStorageItem(t.ID, []byte{tributePrefixTotalTributePool}, buf) +} + +// ===== Account Storage ===== + +func (t *Tribute) getAccountInternal(d *dao.Simple, vitaID uint64) (*state.VelocityAccount, error) { + si := d.GetStorageItem(t.ID, t.makeAccountKey(vitaID)) + if si == nil { + return nil, nil + } + acc := new(state.VelocityAccount) + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + if err := acc.FromStackItem(item); err != nil { + return nil, err + } + return acc, nil +} + +func (t *Tribute) putAccount(d *dao.Simple, acc *state.VelocityAccount) error { + data, err := stackitem.Serialize(acc.ToStackItem()) + if err != nil { + return err + } + d.PutStorageItem(t.ID, t.makeAccountKey(acc.VitaID), data) + return nil +} + +func (t *Tribute) getVitaIDByOwner(d *dao.Simple, owner util.Uint160) (uint64, bool) { + si := d.GetStorageItem(t.ID, t.makeAccountByOwnerKey(owner)) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint64(si), true +} + +func (t *Tribute) setOwnerMapping(d *dao.Simple, owner util.Uint160, vitaID uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, vitaID) + d.PutStorageItem(t.ID, t.makeAccountByOwnerKey(owner), buf) +} + +// ===== Assessment Storage ===== + +func (t *Tribute) getAssessmentInternal(d *dao.Simple, assessmentID uint64) (*state.TributeAssessment, error) { + si := d.GetStorageItem(t.ID, t.makeAssessmentKey(assessmentID)) + if si == nil { + return nil, nil + } + assess := new(state.TributeAssessment) + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + if err := assess.FromStackItem(item); err != nil { + return nil, err + } + return assess, nil +} + +func (t *Tribute) putAssessment(d *dao.Simple, assess *state.TributeAssessment) error { + data, err := stackitem.Serialize(assess.ToStackItem()) + if err != nil { + return err + } + d.PutStorageItem(t.ID, t.makeAssessmentKey(assess.ID), data) + return nil +} + +func (t *Tribute) getPendingAssessmentID(d *dao.Simple, vitaID uint64) (uint64, bool) { + si := d.GetStorageItem(t.ID, t.makePendingAssessmentKey(vitaID)) + if si == nil { + return 0, false + } + return binary.BigEndian.Uint64(si), true +} + +func (t *Tribute) setPendingAssessmentID(d *dao.Simple, vitaID, assessmentID uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, assessmentID) + d.PutStorageItem(t.ID, t.makePendingAssessmentKey(vitaID), buf) +} + +func (t *Tribute) deletePendingAssessment(d *dao.Simple, vitaID uint64) { + d.DeleteStorageItem(t.ID, t.makePendingAssessmentKey(vitaID)) +} + +// ===== Incentive Storage ===== + +func (t *Tribute) getIncentiveInternal(d *dao.Simple, incentiveID uint64) (*state.CirculationIncentive, error) { + si := d.GetStorageItem(t.ID, t.makeIncentiveKey(incentiveID)) + if si == nil { + return nil, nil + } + inc := new(state.CirculationIncentive) + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + if err := inc.FromStackItem(item); err != nil { + return nil, err + } + return inc, nil +} + +func (t *Tribute) putIncentive(d *dao.Simple, inc *state.CirculationIncentive) error { + data, err := stackitem.Serialize(inc.ToStackItem()) + if err != nil { + return err + } + d.PutStorageItem(t.ID, t.makeIncentiveKey(inc.ID), data) + return nil +} + +func (t *Tribute) addUnclaimedIncentive(d *dao.Simple, vitaID, incentiveID uint64) { + d.PutStorageItem(t.ID, t.makeUnclaimedIncentiveKey(vitaID, incentiveID), []byte{1}) +} + +func (t *Tribute) removeUnclaimedIncentive(d *dao.Simple, vitaID, incentiveID uint64) { + d.DeleteStorageItem(t.ID, t.makeUnclaimedIncentiveKey(vitaID, incentiveID)) +} + +// ===== Redistribution Storage ===== + +func (t *Tribute) getRedistributionInternal(d *dao.Simple, redistID uint64) (*state.RedistributionRecord, error) { + si := d.GetStorageItem(t.ID, t.makeRedistributionKey(redistID)) + if si == nil { + return nil, nil + } + rec := new(state.RedistributionRecord) + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + if err := rec.FromStackItem(item); err != nil { + return nil, err + } + return rec, nil +} + +func (t *Tribute) putRedistribution(d *dao.Simple, rec *state.RedistributionRecord) error { + data, err := stackitem.Serialize(rec.ToStackItem()) + if err != nil { + return err + } + d.PutStorageItem(t.ID, t.makeRedistributionKey(rec.ID), data) + return nil +} + +// ===== Config Storage ===== + +func (t *Tribute) getConfigInternal(d *dao.Simple) *state.TributeConfig { + si := d.GetStorageItem(t.ID, []byte{tributePrefixConfig}) + if si == nil { + return nil + } + cfg := new(state.TributeConfig) + item, err := stackitem.Deserialize(si) + if err != nil { + return nil + } + if err := cfg.FromStackItem(item); err != nil { + return nil + } + return cfg +} + +func (t *Tribute) setConfig(d *dao.Simple, cfg *state.TributeConfig) { + data, _ := stackitem.Serialize(cfg.ToStackItem()) + d.PutStorageItem(t.ID, []byte{tributePrefixConfig}, data) +} + +// ===== Internal Helpers ===== + +// calculateVelocity calculates velocity based on inflow/outflow ratio. +func (t *Tribute) calculateVelocity(totalInflow, totalOutflow uint64) uint64 { + if totalInflow == 0 { + if totalOutflow > 0 { + return 10000 // 100% velocity if only outflow + } + return 5000 // Default 50% if no activity + } + // Velocity = (outflow / inflow) * 10000 (basis points) + velocity := (totalOutflow * 10000) / totalInflow + if velocity > 10000 { + velocity = 10000 + } + return velocity +} + +// determineHoardingLevel determines hoarding level based on velocity. +func (t *Tribute) determineHoardingLevel(velocity uint64, cfg *state.TributeConfig) state.HoardingLevel { + if velocity >= cfg.VelocityThresholdMild { + return state.HoardingNone + } + if velocity >= cfg.VelocityThresholdModerate { + return state.HoardingMild + } + if velocity >= cfg.VelocityThresholdSevere { + return state.HoardingModerate + } + if velocity >= cfg.VelocityThresholdExtreme { + return state.HoardingSevere + } + return state.HoardingExtreme +} + +// getTributeRate returns the tribute rate for a hoarding level. +func (t *Tribute) getTributeRate(level state.HoardingLevel, cfg *state.TributeConfig) uint64 { + switch level { + case state.HoardingMild: + return cfg.TributeRateMild + case state.HoardingModerate: + return cfg.TributeRateModerate + case state.HoardingSevere: + return cfg.TributeRateSevere + case state.HoardingExtreme: + return cfg.TributeRateExtreme + default: + return 0 + } +} + +// ===== Contract Methods ===== + +// createVelocityAccount creates a velocity tracking account for a Vita holder. +func (t *Tribute) createVelocityAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + // Verify owner has active Vita + if t.Vita == nil { + panic(ErrTributeNoVita) + } + vita, err := t.Vita.GetTokenByOwner(ic.DAO, owner) + if err != nil || vita == nil { + panic(ErrTributeNoVita) + } + if vita.Status != state.TokenStatusActive { + panic(ErrTributeNoVita) + } + vitaID := vita.TokenID + + // Check if account already exists + existing, _ := t.getAccountInternal(ic.DAO, vitaID) + if existing != nil { + panic(ErrTributeAccountExists) + } + + // Create account + blockHeight := ic.Block.Index + acc := &state.VelocityAccount{ + VitaID: vitaID, + Owner: owner, + CurrentVelocity: 5000, // Default 50% velocity + AverageVelocity: 5000, + LastActivityBlock: blockHeight, + TotalInflow: 0, + TotalOutflow: 0, + StagnantBalance: 0, + HoardingLevel: state.HoardingNone, + ExemptionReason: "", + TotalTributePaid: 0, + TotalIncentivesRcvd: 0, + Status: state.VelocityAccountActive, + CreatedAt: blockHeight, + UpdatedAt: blockHeight, + } + + if err := t.putAccount(ic.DAO, acc); err != nil { + panic(err) + } + t.setOwnerMapping(ic.DAO, owner, vitaID) + + // Update counter + cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) + cache.accountCount++ + t.setAccountCounter(ic.DAO, cache.accountCount) + + // Emit event + ic.AddNotification(t.Hash, VelocityAccountCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), + stackitem.NewByteArray(owner.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +// getAccount returns velocity account by owner. +func (t *Tribute) getAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.Null{} + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + return stackitem.Null{} + } + + return acc.ToStackItem() +} + +// getAccountByVitaID returns velocity account by Vita ID. +func (t *Tribute) getAccountByVitaID(ic *interop.Context, args []stackitem.Item) stackitem.Item { + vitaID := toBigInt(args[0]).Uint64() + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + return stackitem.Null{} + } + + return acc.ToStackItem() +} + +// recordTransaction records a transaction for velocity tracking. +func (t *Tribute) recordTransaction(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + amount := toBigInt(args[1]).Uint64() + isOutflow := toBool(args[2]) + + if amount == 0 { + panic(ErrTributeInvalidAmount) + } + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + panic(ErrTributeAccountNotFound) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + panic(ErrTributeAccountNotFound) + } + + if acc.Status == state.VelocityAccountSuspended { + panic(ErrTributeAccountSuspended) + } + + // Update transaction totals + if isOutflow { + acc.TotalOutflow += amount + } else { + acc.TotalInflow += amount + } + + // Recalculate velocity + acc.CurrentVelocity = t.calculateVelocity(acc.TotalInflow, acc.TotalOutflow) + + // Update rolling average (simple average for now) + acc.AverageVelocity = (acc.AverageVelocity + acc.CurrentVelocity) / 2 + + // Determine hoarding level + cfg := t.getConfigInternal(ic.DAO) + acc.HoardingLevel = t.determineHoardingLevel(acc.CurrentVelocity, cfg) + + // Update timestamp + acc.LastActivityBlock = ic.Block.Index + acc.UpdatedAt = ic.Block.Index + + if err := t.putAccount(ic.DAO, acc); err != nil { + panic(err) + } + + // Emit event + ic.AddNotification(t.Hash, VelocityUpdatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(acc.CurrentVelocity)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(acc.HoardingLevel))), + })) + + return stackitem.NewBool(true) +} + +// getVelocity returns current velocity score for an owner. +func (t *Tribute) getVelocity(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + return stackitem.NewBigInteger(new(big.Int).SetUint64(acc.CurrentVelocity)) +} + +// getHoardingLevel returns current hoarding level for an owner. +func (t *Tribute) getHoardingLevel(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + return stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(acc.HoardingLevel))) +} + +// grantExemption grants exemption from tribute (admin only). +func (t *Tribute) grantExemption(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + reason := toString(args[1]) + + if !t.checkTributeAdmin(ic) { + panic(ErrTributeNotAdmin) + } + + if len(reason) == 0 || len(reason) > 256 { + panic(ErrTributeInvalidReason) + } + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + panic(ErrTributeAccountNotFound) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + panic(ErrTributeAccountNotFound) + } + + acc.Status = state.VelocityAccountExempt + acc.ExemptionReason = reason + acc.UpdatedAt = ic.Block.Index + + if err := t.putAccount(ic.DAO, acc); err != nil { + panic(err) + } + + // Emit event + ic.AddNotification(t.Hash, ExemptionGrantedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +// revokeExemption revokes exemption from tribute (admin only). +func (t *Tribute) revokeExemption(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + if !t.checkTributeAdmin(ic) { + panic(ErrTributeNotAdmin) + } + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + panic(ErrTributeAccountNotFound) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + panic(ErrTributeAccountNotFound) + } + + if acc.Status != state.VelocityAccountExempt { + panic(ErrTributeAccountNotFound) + } + + acc.Status = state.VelocityAccountActive + acc.ExemptionReason = "" + acc.UpdatedAt = ic.Block.Index + + if err := t.putAccount(ic.DAO, acc); err != nil { + panic(err) + } + + // Emit event + ic.AddNotification(t.Hash, ExemptionRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), + })) + + return stackitem.NewBool(true) +} + +// isExempt checks if account is exempt from tribute. +func (t *Tribute) isExempt(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.NewBool(false) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + return stackitem.NewBool(false) + } + + return stackitem.NewBool(acc.Status == state.VelocityAccountExempt) +} + +// assessTribute assesses tribute for hoarding. +func (t *Tribute) assessTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + panic(ErrTributeAccountNotFound) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + panic(ErrTributeAccountNotFound) + } + + if acc.Status == state.VelocityAccountExempt { + panic(ErrTributeAccountExempt) + } + + if acc.Status == state.VelocityAccountSuspended { + panic(ErrTributeAccountSuspended) + } + + // Check if there's already a pending assessment + if existingID, exists := t.getPendingAssessmentID(ic.DAO, vitaID); exists { + existing, _ := t.getAssessmentInternal(ic.DAO, existingID) + if existing != nil && existing.Status == state.AssessmentPending { + panic("pending assessment already exists") + } + } + + // Check hoarding level + if acc.HoardingLevel == state.HoardingNone { + panic(ErrTributeNoHoarding) + } + + cfg := t.getConfigInternal(ic.DAO) + + // Get stagnant balance (simplified: use total inflow - outflow as approximation) + stagnantBalance := uint64(0) + if acc.TotalInflow > acc.TotalOutflow { + stagnantBalance = acc.TotalInflow - acc.TotalOutflow + } + + if stagnantBalance < cfg.MinBalanceForTribute { + panic(ErrTributeBelowExemption) + } + + // Calculate tribute + tributeRate := t.getTributeRate(acc.HoardingLevel, cfg) + tributeAmount := (stagnantBalance * tributeRate) / 10000 + + // Create assessment + cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) + assessmentID := cache.assessmentCount + cache.assessmentCount++ + t.setAssessmentCounter(ic.DAO, cache.assessmentCount) + + blockHeight := ic.Block.Index + assessment := &state.TributeAssessment{ + ID: assessmentID, + VitaID: vitaID, + Owner: owner, + AssessmentBlock: blockHeight, + HoardingLevel: acc.HoardingLevel, + StagnantAmount: stagnantBalance, + TributeRate: tributeRate, + TributeAmount: tributeAmount, + DueBlock: blockHeight + cfg.GracePeriod, + CollectedBlock: 0, + Status: state.AssessmentPending, + AppealReason: "", + } + + if err := t.putAssessment(ic.DAO, assessment); err != nil { + panic(err) + } + t.setPendingAssessmentID(ic.DAO, vitaID, assessmentID) + + // Emit event + ic.AddNotification(t.Hash, TributeAssessedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(tributeAmount)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)) +} + +// getAssessment returns assessment by ID. +func (t *Tribute) getAssessment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + assessmentID := toBigInt(args[0]).Uint64() + + assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) + if err != nil || assess == nil { + return stackitem.Null{} + } + + return assess.ToStackItem() +} + +// getPendingAssessment returns pending assessment for an owner. +func (t *Tribute) getPendingAssessment(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.Null{} + } + + assessmentID, exists := t.getPendingAssessmentID(ic.DAO, vitaID) + if !exists { + return stackitem.Null{} + } + + assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) + if err != nil || assess == nil { + return stackitem.Null{} + } + + if assess.Status != state.AssessmentPending { + return stackitem.Null{} + } + + return assess.ToStackItem() +} + +// collectTribute collects pending tribute. +func (t *Tribute) collectTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + assessmentID := toBigInt(args[0]).Uint64() + + assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) + if err != nil || assess == nil { + panic(ErrTributeAssessmentNotFound) + } + + if assess.Status != state.AssessmentPending { + panic(ErrTributeAssessmentNotPending) + } + + // Check property right before taking tribute + if !t.checkPropertyRight(ic, assess.Owner) { + panic(ErrTributePropertyRestricted) + } + + // Mark as collected + assess.Status = state.AssessmentCollected + assess.CollectedBlock = ic.Block.Index + + if err := t.putAssessment(ic.DAO, assess); err != nil { + panic(err) + } + t.deletePendingAssessment(ic.DAO, assess.VitaID) + + // Update account + acc, _ := t.getAccountInternal(ic.DAO, assess.VitaID) + if acc != nil { + acc.TotalTributePaid += assess.TributeAmount + acc.UpdatedAt = ic.Block.Index + t.putAccount(ic.DAO, acc) + } + + // Add to tribute pool for redistribution + pool := t.getTributePoolValue(ic.DAO) + pool += assess.TributeAmount + t.setTributePool(ic.DAO, pool) + + // Emit event + ic.AddNotification(t.Hash, TributeCollectedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(assess.TributeAmount)), + })) + + return stackitem.NewBool(true) +} + +// waiveTribute waives a tribute assessment (admin only). +func (t *Tribute) waiveTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + assessmentID := toBigInt(args[0]).Uint64() + reason := toString(args[1]) + + if !t.checkTributeAdmin(ic) { + panic(ErrTributeNotAdmin) + } + + if len(reason) == 0 || len(reason) > 256 { + panic(ErrTributeInvalidReason) + } + + assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) + if err != nil || assess == nil { + panic(ErrTributeAssessmentNotFound) + } + + if assess.Status != state.AssessmentPending && assess.Status != state.AssessmentAppealed { + panic(ErrTributeAssessmentNotPending) + } + + assess.Status = state.AssessmentWaived + assess.AppealReason = reason + + if err := t.putAssessment(ic.DAO, assess); err != nil { + panic(err) + } + t.deletePendingAssessment(ic.DAO, assess.VitaID) + + // Emit event + ic.AddNotification(t.Hash, TributeWaivedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +// appealTribute appeals a tribute assessment. +func (t *Tribute) appealTribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + assessmentID := toBigInt(args[0]).Uint64() + reason := toString(args[1]) + + if len(reason) == 0 || len(reason) > 512 { + panic(ErrTributeInvalidReason) + } + + assess, err := t.getAssessmentInternal(ic.DAO, assessmentID) + if err != nil || assess == nil { + panic(ErrTributeAssessmentNotFound) + } + + // Check caller is owner + ok, err := checkWitness(ic, assess.Owner) + if err != nil || !ok { + panic(ErrTributeNotOwner) + } + + if assess.Status != state.AssessmentPending { + panic(ErrTributeAssessmentNotPending) + } + + assess.Status = state.AssessmentAppealed + assess.AppealReason = reason + + if err := t.putAssessment(ic.DAO, assess); err != nil { + panic(err) + } + + // Emit event + ic.AddNotification(t.Hash, TributeAppealedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(assessmentID)), + stackitem.NewByteArray([]byte(reason)), + })) + + return stackitem.NewBool(true) +} + +// grantIncentive grants circulation incentive (system/admin). +func (t *Tribute) grantIncentive(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + incentiveType := state.IncentiveType(toBigInt(args[1]).Uint64()) + amount := toBigInt(args[2]).Uint64() + reason := toString(args[3]) + + if !t.checkTributeAdmin(ic) { + panic(ErrTributeNotAdmin) + } + + if amount == 0 { + panic(ErrTributeInvalidAmount) + } + + if len(reason) == 0 || len(reason) > 256 { + panic(ErrTributeInvalidReason) + } + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + panic(ErrTributeAccountNotFound) + } + + acc, err := t.getAccountInternal(ic.DAO, vitaID) + if err != nil || acc == nil { + panic(ErrTributeAccountNotFound) + } + + // Create incentive + cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) + incentiveID := cache.incentiveCount + cache.incentiveCount++ + t.setIncentiveCounter(ic.DAO, cache.incentiveCount) + + blockHeight := ic.Block.Index + incentive := &state.CirculationIncentive{ + ID: incentiveID, + VitaID: vitaID, + Recipient: owner, + IncentiveType: incentiveType, + Amount: amount, + Reason: reason, + VelocityScore: acc.CurrentVelocity, + GrantedBlock: blockHeight, + ClaimedBlock: 0, + Claimed: false, + } + + if err := t.putIncentive(ic.DAO, incentive); err != nil { + panic(err) + } + t.addUnclaimedIncentive(ic.DAO, vitaID, incentiveID) + + // Emit event + ic.AddNotification(t.Hash, IncentiveGrantedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(incentiveID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(vitaID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(amount)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(incentiveID)) +} + +// claimIncentive claims a granted incentive. +func (t *Tribute) claimIncentive(ic *interop.Context, args []stackitem.Item) stackitem.Item { + incentiveID := toBigInt(args[0]).Uint64() + + incentive, err := t.getIncentiveInternal(ic.DAO, incentiveID) + if err != nil || incentive == nil { + panic(ErrTributeIncentiveNotFound) + } + + // Check caller is recipient + ok, err := checkWitness(ic, incentive.Recipient) + if err != nil || !ok { + panic(ErrTributeNotOwner) + } + + if incentive.Claimed { + panic(ErrTributeIncentiveClaimed) + } + + // Mark as claimed + incentive.Claimed = true + incentive.ClaimedBlock = ic.Block.Index + + if err := t.putIncentive(ic.DAO, incentive); err != nil { + panic(err) + } + t.removeUnclaimedIncentive(ic.DAO, incentive.VitaID, incentiveID) + + // Update account + acc, _ := t.getAccountInternal(ic.DAO, incentive.VitaID) + if acc != nil { + acc.TotalIncentivesRcvd += incentive.Amount + acc.UpdatedAt = ic.Block.Index + t.putAccount(ic.DAO, acc) + } + + // Emit event + ic.AddNotification(t.Hash, IncentiveClaimedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(incentiveID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(incentive.Amount)), + })) + + return stackitem.NewBool(true) +} + +// getIncentive returns incentive by ID. +func (t *Tribute) getIncentive(ic *interop.Context, args []stackitem.Item) stackitem.Item { + incentiveID := toBigInt(args[0]).Uint64() + + incentive, err := t.getIncentiveInternal(ic.DAO, incentiveID) + if err != nil || incentive == nil { + return stackitem.Null{} + } + + return incentive.ToStackItem() +} + +// getUnclaimedIncentives returns count of unclaimed incentives for owner. +func (t *Tribute) getUnclaimedIncentives(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + vitaID, found := t.getVitaIDByOwner(ic.DAO, owner) + if !found { + return stackitem.NewBigInteger(big.NewInt(0)) + } + + // Count unclaimed incentives by iterating storage + // This is a simplified version - in production, we'd track this counter + count := uint64(0) + prefix := make([]byte, 9) + prefix[0] = tributePrefixUnclaimedIncentive + binary.BigEndian.PutUint64(prefix[1:], vitaID) + + ic.DAO.Seek(t.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + count++ + return true + }) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(count)) +} + +// redistribute executes wealth redistribution (committee only). +func (t *Tribute) redistribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + targetCategory := toString(args[0]) + recipientCount := toBigInt(args[1]).Uint64() + + if !t.checkCommittee(ic) { + panic(ErrTributeNotCommittee) + } + + pool := t.getTributePoolValue(ic.DAO) + if pool == 0 { + panic(ErrTributeNothingToRedistribute) + } + + if recipientCount == 0 { + panic(ErrTributeInvalidAmount) + } + + perCapita := pool / recipientCount + + // Create redistribution record + cache := ic.DAO.GetRWCache(t.ID).(*TributeCache) + redistID := cache.redistributionCount + cache.redistributionCount++ + t.setRedistributionCounter(ic.DAO, cache.redistributionCount) + + record := &state.RedistributionRecord{ + ID: redistID, + SourceAssessment: 0, // Could link to specific assessment if needed + TotalAmount: pool, + RecipientCount: recipientCount, + PerCapitaAmount: perCapita, + RedistBlock: ic.Block.Index, + TargetCategory: targetCategory, + } + + if err := t.putRedistribution(ic.DAO, record); err != nil { + panic(err) + } + + // Clear the pool (actual redistribution would happen via VTS transfers) + t.setTributePool(ic.DAO, 0) + + // Emit event + ic.AddNotification(t.Hash, RedistributionExecutedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(redistID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(pool)), + stackitem.NewBigInteger(new(big.Int).SetUint64(recipientCount)), + })) + + return stackitem.NewBigInteger(new(big.Int).SetUint64(redistID)) +} + +// getRedistribution returns redistribution record by ID. +func (t *Tribute) getRedistribution(ic *interop.Context, args []stackitem.Item) stackitem.Item { + redistID := toBigInt(args[0]).Uint64() + + rec, err := t.getRedistributionInternal(ic.DAO, redistID) + if err != nil || rec == nil { + return stackitem.Null{} + } + + return rec.ToStackItem() +} + +// getTributePool returns total tribute pool available for redistribution. +func (t *Tribute) getTributePool(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getTributePoolValue(ic.DAO))) +} + +// getConfig returns current configuration. +func (t *Tribute) getConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + cfg := t.getConfigInternal(ic.DAO) + if cfg == nil { + return stackitem.Null{} + } + return cfg.ToStackItem() +} + +// getTotalAccounts returns total velocity accounts. +func (t *Tribute) getTotalAccounts(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getAccountCounter(ic.DAO))) +} + +// getTotalAssessments returns total assessments. +func (t *Tribute) getTotalAssessments(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getAssessmentCounter(ic.DAO))) +} + +// getTotalIncentives returns total incentives. +func (t *Tribute) getTotalIncentives(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getIncentiveCounter(ic.DAO))) +} + +// getTotalRedistributions returns total redistributions. +func (t *Tribute) getTotalRedistributions(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(new(big.Int).SetUint64(t.getRedistributionCounter(ic.DAO))) +} + +// ===== Public Internal Methods ===== + +// GetAccountByOwner returns velocity account by owner (internal API). +func (t *Tribute) GetAccountByOwner(d *dao.Simple, owner util.Uint160) (*state.VelocityAccount, error) { + vitaID, found := t.getVitaIDByOwner(d, owner) + if !found { + return nil, ErrTributeAccountNotFound + } + return t.getAccountInternal(d, vitaID) +} + +// GetVelocity returns velocity score for an owner (internal API). +func (t *Tribute) GetVelocity(d *dao.Simple, owner util.Uint160) uint64 { + vitaID, found := t.getVitaIDByOwner(d, owner) + if !found { + return 5000 // Default 50% + } + acc, err := t.getAccountInternal(d, vitaID) + if err != nil || acc == nil { + return 5000 + } + return acc.CurrentVelocity +} + +// IsHoarding returns true if owner is hoarding (internal API). +func (t *Tribute) IsHoarding(d *dao.Simple, owner util.Uint160) bool { + vitaID, found := t.getVitaIDByOwner(d, owner) + if !found { + return false + } + acc, err := t.getAccountInternal(d, vitaID) + if err != nil || acc == nil { + return false + } + return acc.HoardingLevel != state.HoardingNone +} + +// Address returns the contract address. +func (t *Tribute) Address() util.Uint160 { + return t.Hash +} diff --git a/pkg/core/native/util.go b/pkg/core/native/util.go index d104880..fda659e 100644 --- a/pkg/core/native/util.go +++ b/pkg/core/native/util.go @@ -3,10 +3,10 @@ package native import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var intOne = big.NewInt(1) diff --git a/pkg/core/native/validation.go b/pkg/core/native/validation.go index da4a909..62b9749 100644 --- a/pkg/core/native/validation.go +++ b/pkg/core/native/validation.go @@ -1,128 +1,128 @@ -package native - -import ( - "errors" -) - -// LOW-002: Common input validation constants and helpers for all native contracts. -// These ensure consistent validation and prevent DoS attacks via oversized inputs. - -// Maximum string lengths for input validation. -const ( - // Identity-related limits - MaxNameLength = 256 // Names of laws, roles, etc. - MaxDescriptionLength = 4096 // Detailed descriptions - MaxReasonLength = 1024 // Reasons for actions - MaxPurposeLength = 128 // Auth purposes - - // Identifiers - MaxBucketLength = 64 // Bucket/category identifiers - MaxTagLength = 128 // Tags and labels - MaxKeyLength = 256 // Attribute keys - - // Content - MaxAttributeValueLength = 4096 // Attribute values - MaxEvidenceLength = 32768 // Evidence/proof data - - // Query limits - MaxQueryLimit = 100 // Maximum items returned per query - DefaultPageSize = 20 // Default page size for pagination -) - -// Validation errors. -var ( - ErrInputTooLong = errors.New("input exceeds maximum allowed length") - ErrNameTooLong = errors.New("name exceeds maximum length") - ErrDescriptionTooLong = errors.New("description exceeds maximum length") - ErrReasonTooLong = errors.New("reason exceeds maximum length") - ErrBucketTooLong = errors.New("bucket identifier exceeds maximum length") - ErrTagTooLong = errors.New("tag exceeds maximum length") - ErrKeyTooLong = errors.New("key exceeds maximum length") - ErrValueTooLong = errors.New("value exceeds maximum length") - ErrEvidenceTooLong = errors.New("evidence exceeds maximum length") - ErrInvalidPageSize = errors.New("page size exceeds maximum") -) - -// ValidateName checks if a name is within allowed length. -func ValidateName(name string) error { - if len(name) > MaxNameLength { - return ErrNameTooLong - } - return nil -} - -// ValidateDescription checks if a description is within allowed length. -func ValidateDescription(desc string) error { - if len(desc) > MaxDescriptionLength { - return ErrDescriptionTooLong - } - return nil -} - -// ValidateReason checks if a reason is within allowed length. -func ValidateReason(reason string) error { - if len(reason) > MaxReasonLength { - return ErrReasonTooLong - } - return nil -} - -// ValidateBucket checks if a bucket identifier is within allowed length. -func ValidateBucket(bucket string) error { - if len(bucket) > MaxBucketLength { - return ErrBucketTooLong - } - return nil -} - -// ValidateTag checks if a tag is within allowed length. -func ValidateTag(tag string) error { - if len(tag) > MaxTagLength { - return ErrTagTooLong - } - return nil -} - -// ValidateKey checks if a key is within allowed length. -func ValidateKey(key string) error { - if len(key) > MaxKeyLength { - return ErrKeyTooLong - } - return nil -} - -// ValidateValue checks if a value is within allowed length. -func ValidateValue(value string) error { - if len(value) > MaxAttributeValueLength { - return ErrValueTooLong - } - return nil -} - -// ValidateEvidence checks if evidence data is within allowed length. -func ValidateEvidence(evidence []byte) error { - if len(evidence) > MaxEvidenceLength { - return ErrEvidenceTooLong - } - return nil -} - -// ValidatePageSize ensures page size is within limits. -// Returns the validated page size (clamped to max if needed). -func ValidatePageSize(size int) int { - if size <= 0 { - return DefaultPageSize - } - if size > MaxQueryLimit { - return MaxQueryLimit - } - return size -} - -// ValidateOffset ensures offset is non-negative. -func ValidateOffset(offset int) int { - if offset < 0 { - return 0 - } - return offset -} +package native + +import ( + "errors" +) + +// LOW-002: Common input validation constants and helpers for all native contracts. +// These ensure consistent validation and prevent DoS attacks via oversized inputs. + +// Maximum string lengths for input validation. +const ( + // Identity-related limits + MaxNameLength = 256 // Names of laws, roles, etc. + MaxDescriptionLength = 4096 // Detailed descriptions + MaxReasonLength = 1024 // Reasons for actions + MaxPurposeLength = 128 // Auth purposes + + // Identifiers + MaxBucketLength = 64 // Bucket/category identifiers + MaxTagLength = 128 // Tags and labels + MaxKeyLength = 256 // Attribute keys + + // Content + MaxAttributeValueLength = 4096 // Attribute values + MaxEvidenceLength = 32768 // Evidence/proof data + + // Query limits + MaxQueryLimit = 100 // Maximum items returned per query + DefaultPageSize = 20 // Default page size for pagination +) + +// Validation errors. +var ( + ErrInputTooLong = errors.New("input exceeds maximum allowed length") + ErrNameTooLong = errors.New("name exceeds maximum length") + ErrDescriptionTooLong = errors.New("description exceeds maximum length") + ErrReasonTooLong = errors.New("reason exceeds maximum length") + ErrBucketTooLong = errors.New("bucket identifier exceeds maximum length") + ErrTagTooLong = errors.New("tag exceeds maximum length") + ErrKeyTooLong = errors.New("key exceeds maximum length") + ErrValueTooLong = errors.New("value exceeds maximum length") + ErrEvidenceTooLong = errors.New("evidence exceeds maximum length") + ErrInvalidPageSize = errors.New("page size exceeds maximum") +) + +// ValidateName checks if a name is within allowed length. +func ValidateName(name string) error { + if len(name) > MaxNameLength { + return ErrNameTooLong + } + return nil +} + +// ValidateDescription checks if a description is within allowed length. +func ValidateDescription(desc string) error { + if len(desc) > MaxDescriptionLength { + return ErrDescriptionTooLong + } + return nil +} + +// ValidateReason checks if a reason is within allowed length. +func ValidateReason(reason string) error { + if len(reason) > MaxReasonLength { + return ErrReasonTooLong + } + return nil +} + +// ValidateBucket checks if a bucket identifier is within allowed length. +func ValidateBucket(bucket string) error { + if len(bucket) > MaxBucketLength { + return ErrBucketTooLong + } + return nil +} + +// ValidateTag checks if a tag is within allowed length. +func ValidateTag(tag string) error { + if len(tag) > MaxTagLength { + return ErrTagTooLong + } + return nil +} + +// ValidateKey checks if a key is within allowed length. +func ValidateKey(key string) error { + if len(key) > MaxKeyLength { + return ErrKeyTooLong + } + return nil +} + +// ValidateValue checks if a value is within allowed length. +func ValidateValue(value string) error { + if len(value) > MaxAttributeValueLength { + return ErrValueTooLong + } + return nil +} + +// ValidateEvidence checks if evidence data is within allowed length. +func ValidateEvidence(evidence []byte) error { + if len(evidence) > MaxEvidenceLength { + return ErrEvidenceTooLong + } + return nil +} + +// ValidatePageSize ensures page size is within limits. +// Returns the validated page size (clamped to max if needed). +func ValidatePageSize(size int) int { + if size <= 0 { + return DefaultPageSize + } + if size > MaxQueryLimit { + return MaxQueryLimit + } + return size +} + +// ValidateOffset ensures offset is non-negative. +func ValidateOffset(offset int) int { + if offset < 0 { + return 0 + } + return offset +} diff --git a/pkg/core/native/vita.go b/pkg/core/native/vita.go index c76de51..e998bfe 100644 --- a/pkg/core/native/vita.go +++ b/pkg/core/native/vita.go @@ -1,2262 +1,2262 @@ -package native - -import ( - "encoding/binary" - "errors" - "fmt" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Vita represents a soul-bound identity native contract. -type Vita struct { - interop.ContractMD - Tutus ITutus - RoleRegistry IRoleRegistry - Lex ILex - Annos IAnnos -} - -// VitaCache represents the cached state for Vita contract. -type VitaCache struct { - tokenCount uint64 -} - -// Storage key prefixes for Vita. -const ( - prefixTokenByOwner = 0x01 // owner (Uint160) -> tokenID (uint64) - prefixTokenByID = 0x02 // tokenID (uint64) -> Vita token - prefixPersonHash = 0x03 // personHash -> tokenID (uniqueness) - prefixAttribute = 0x04 // tokenID + attrKey -> Attribute - prefixChallenge = 0x05 // challengeID -> AuthChallenge - prefixRecovery = 0x06 // requestID -> RecoveryRequest - prefixActiveRecovery = 0x07 // tokenID + "recovery" -> requestID - prefixTokenCounter = 0x10 // -> uint64 - prefixConfig = 0x11 // -> VitaConfig -) - -// Event names for Vita. -const ( - VitaCreatedEvent = "VitaCreated" - VitaSuspendedEvent = "VitaSuspended" - VitaReinstatedEvent = "VitaReinstated" - VitaRevokedEvent = "VitaRevoked" - AttributeSetEvent = "AttributeSet" - AttributeRevokedEvent = "AttributeRevoked" - AuthenticationSuccessEvent = "AuthenticationSuccess" - AuthChallengeCreatedEvent = "AuthChallengeCreated" - RecoveryInitiatedEvent = "RecoveryInitiated" - RecoveryApprovalEvent = "RecoveryApproval" - RecoveryExecutedEvent = "RecoveryExecuted" - RecoveryCancelledEvent = "RecoveryCancelled" - VestingUpdatedEvent = "VestingUpdated" -) - -// Default vesting period in blocks (approximately 30 days at 1 second per block). -// New Vita tokens must vest before having full rights (Sybil resistance). -const defaultVestingPeriod uint32 = 2592000 // 30 days * 24 hours * 60 minutes * 60 seconds - -// Various errors. -var ( - ErrTokenAlreadyExists = errors.New("token already exists for this owner") - ErrPersonHashExists = errors.New("person hash already linked to another token") - ErrTokenNotFound = errors.New("token not found") - ErrTokenSuspended = errors.New("token is suspended") - ErrTokenRevoked = errors.New("token is revoked") - ErrTokenNotSuspended = errors.New("token is not suspended") - ErrInvalidOwner = errors.New("invalid owner") - ErrInvalidPersonHash = errors.New("invalid person hash") - ErrNotCommittee = errors.New("invalid committee signature") - ErrVitaInvalidWitness = errors.New("invalid witness") - ErrVitaNotFullyVested = errors.New("vita is not fully vested") - ErrAttributeNotFound = errors.New("attribute not found") - ErrAttributeRevoked = errors.New("attribute is already revoked") - ErrAttributeExpired = errors.New("attribute has expired") - ErrInvalidAttributeKey = errors.New("invalid attribute key") - ErrInvalidValueHash = errors.New("invalid value hash") - ErrInvalidDisclosureLevel = errors.New("invalid disclosure level") - ErrTokenNotActive = errors.New("token is not active") - ErrChallengeNotFound = errors.New("challenge not found") - ErrChallengeExpired = errors.New("challenge has expired") - ErrChallengeAlreadyFulfilled = errors.New("challenge already fulfilled") - ErrInvalidSignature = errors.New("invalid signature") - ErrInvalidPurpose = errors.New("invalid purpose") - ErrNoRecentAuth = errors.New("no recent authentication found") - ErrRecoveryNotFound = errors.New("recovery request not found") - ErrRecoveryAlreadyActive = errors.New("recovery already active for this token") - ErrRecoveryNotPending = errors.New("recovery is not in pending state") - ErrRecoveryDelayNotPassed = errors.New("recovery delay period has not passed") - ErrRecoveryExpired = errors.New("recovery request has expired") - ErrAlreadyApproved = errors.New("already approved this recovery") - ErrInvalidNewOwner = errors.New("invalid new owner") - ErrTokenInRecovery = errors.New("token is already in recovery") - ErrNotRecoveryRequester = errors.New("not the recovery requester") - ErrCallerHasNoVita = errors.New("caller does not have a Vita") - ErrRoleNotAssigned = errors.New("required role not assigned") - ErrPermissionDenied = errors.New("permission denied") - ErrVitaInvalidRole = errors.New("invalid core role") - ErrVitaInvalidResource = errors.New("invalid resource") - ErrVitaInvalidAction = errors.New("invalid action") -) - -// CoreRole represents built-in roles for cross-contract access control. -type CoreRole uint8 - -// Core roles for Vita holders. -const ( - CoreRoleNone CoreRole = 0 - CoreRoleUser CoreRole = 1 // Basic authenticated user - CoreRoleVerified CoreRole = 2 // User with verified attributes - CoreRoleCommittee CoreRole = 3 // Committee member - CoreRoleAttestor CoreRole = 4 // Can attest attributes for others - CoreRoleRecovery CoreRole = 5 // Can participate in recovery -) - -var ( - _ interop.Contract = (*Vita)(nil) - _ dao.NativeContractCache = (*VitaCache)(nil) -) - -// Copy implements NativeContractCache interface. -func (c *VitaCache) Copy() dao.NativeContractCache { - return &VitaCache{ - tokenCount: c.tokenCount, - } -} - -// checkCommittee checks if the caller has committee authority. -// Uses RoleRegistry if available, falls back to NEO.CheckCommittee(). -func (v *Vita) checkCommittee(ic *interop.Context) bool { - if v.RoleRegistry != nil { - return v.RoleRegistry.CheckCommittee(ic) - } - // Fallback to Tutus for backwards compatibility - return v.Tutus.CheckCommittee(ic) -} - -// newVita creates a new Vita native contract. -func newVita() *Vita { - v := &Vita{ - ContractMD: *interop.NewContractMD(nativenames.Vita, nativeids.Vita), - } - defer v.BuildHFSpecificMD(v.ActiveIn()) - - // Register method - desc := NewDescriptor("register", smartcontract.ByteArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("personHash", smartcontract.ByteArrayType), - manifest.NewParameter("isEntity", smartcontract.BoolType), - manifest.NewParameter("recoveryHash", smartcontract.ByteArrayType), - manifest.NewParameter("birthTimestamp", smartcontract.IntegerType)) - md := NewMethodAndPrice(v.register, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // GetToken method - desc = NewDescriptor("getToken", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.getToken, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // GetTokenByID method - desc = NewDescriptor("getTokenByID", smartcontract.ArrayType, - manifest.NewParameter("tokenId", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.getTokenByID, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // Exists method - desc = NewDescriptor("exists", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.exists, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // TotalSupply method - desc = NewDescriptor("totalSupply", smartcontract.IntegerType) - md = NewMethodAndPrice(v.totalSupply, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // Suspend method - desc = NewDescriptor("suspend", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(v.suspend, 1<<16, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // Reinstate method - desc = NewDescriptor("reinstate", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.reinstate, 1<<16, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // Revoke method - desc = NewDescriptor("revoke", smartcontract.BoolType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(v.revoke, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // SetAttribute method - desc = NewDescriptor("setAttribute", smartcontract.BoolType, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("key", smartcontract.StringType), - manifest.NewParameter("valueHash", smartcontract.ByteArrayType), - manifest.NewParameter("valueEnc", smartcontract.ByteArrayType), - manifest.NewParameter("expiresAt", smartcontract.IntegerType), - manifest.NewParameter("disclosureLevel", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.setAttribute, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // GetAttribute method - desc = NewDescriptor("getAttribute", smartcontract.ArrayType, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("key", smartcontract.StringType)) - md = NewMethodAndPrice(v.getAttribute, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // RevokeAttribute method - desc = NewDescriptor("revokeAttribute", smartcontract.BoolType, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("key", smartcontract.StringType), - manifest.NewParameter("reason", smartcontract.StringType)) - md = NewMethodAndPrice(v.revokeAttribute, 1<<16, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // VerifyAttribute method - desc = NewDescriptor("verifyAttribute", smartcontract.BoolType, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("key", smartcontract.StringType), - manifest.NewParameter("expectedHash", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.verifyAttribute, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // CreateChallenge method - desc = NewDescriptor("createChallenge", smartcontract.ArrayType, - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("purpose", smartcontract.StringType)) - md = NewMethodAndPrice(v.createChallenge, 1<<16, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // GetChallenge method - desc = NewDescriptor("getChallenge", smartcontract.ArrayType, - manifest.NewParameter("challengeId", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.getChallenge, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // FulfillChallenge method - desc = NewDescriptor("fulfillChallenge", smartcontract.BoolType, - manifest.NewParameter("challengeId", smartcontract.ByteArrayType), - manifest.NewParameter("signature", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.fulfillChallenge, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // VerifyAuth method - desc = NewDescriptor("verifyAuth", smartcontract.BoolType, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("purpose", smartcontract.StringType), - manifest.NewParameter("maxAge", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.verifyAuth, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // InitiateRecovery method - desc = NewDescriptor("initiateRecovery", smartcontract.ByteArrayType, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("newOwner", smartcontract.Hash160Type), - manifest.NewParameter("evidence", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.initiateRecovery, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // ApproveRecovery method - desc = NewDescriptor("approveRecovery", smartcontract.BoolType, - manifest.NewParameter("requestId", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.approveRecovery, 1<<16, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // ExecuteRecovery method - desc = NewDescriptor("executeRecovery", smartcontract.BoolType, - manifest.NewParameter("requestId", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.executeRecovery, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // CancelRecovery method - desc = NewDescriptor("cancelRecovery", smartcontract.BoolType, - manifest.NewParameter("requestId", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.cancelRecovery, 1<<16, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // GetRecoveryRequest method - desc = NewDescriptor("getRecoveryRequest", smartcontract.ArrayType, - manifest.NewParameter("requestId", smartcontract.ByteArrayType)) - md = NewMethodAndPrice(v.getRecoveryRequest, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // ValidateCaller method - desc = NewDescriptor("validateCaller", smartcontract.ArrayType) - md = NewMethodAndPrice(v.validateCaller, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // RequireRole method - desc = NewDescriptor("requireRole", smartcontract.IntegerType, - manifest.NewParameter("roleId", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.requireRole, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // RequireCoreRole method - desc = NewDescriptor("requireCoreRole", smartcontract.IntegerType, - manifest.NewParameter("coreRole", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.requireCoreRole, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // RequirePermission method - desc = NewDescriptor("requirePermission", smartcontract.IntegerType, - manifest.NewParameter("resource", smartcontract.StringType), - manifest.NewParameter("action", smartcontract.StringType), - manifest.NewParameter("scope", smartcontract.StringType)) - md = NewMethodAndPrice(v.requirePermission, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // SetVesting method (committee only) - desc = NewDescriptor("setVesting", smartcontract.BoolType, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("vestedUntil", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.setVesting, 1<<16, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // IsFullyVested method - desc = NewDescriptor("isFullyVested", smartcontract.BoolType, - manifest.NewParameter("tokenId", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.isFullyVested, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // GetVestingInfo method - desc = NewDescriptor("getVestingInfo", smartcontract.ArrayType, - manifest.NewParameter("tokenId", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.getVestingInfo, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // Events - eDesc := NewEventDescriptor(VitaCreatedEvent, - manifest.NewParameter("tokenId", smartcontract.ByteArrayType), - manifest.NewParameter("owner", smartcontract.Hash160Type), - manifest.NewParameter("createdAt", smartcontract.IntegerType)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VitaSuspendedEvent, - manifest.NewParameter("tokenId", smartcontract.ByteArrayType), - manifest.NewParameter("reason", smartcontract.StringType), - manifest.NewParameter("suspendedBy", smartcontract.Hash160Type)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VitaReinstatedEvent, - manifest.NewParameter("tokenId", smartcontract.ByteArrayType), - manifest.NewParameter("reinstatedBy", smartcontract.Hash160Type)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VitaRevokedEvent, - manifest.NewParameter("tokenId", smartcontract.ByteArrayType), - manifest.NewParameter("reason", smartcontract.StringType), - manifest.NewParameter("revokedBy", smartcontract.Hash160Type)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AttributeSetEvent, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("key", smartcontract.StringType), - manifest.NewParameter("attestor", smartcontract.Hash160Type), - manifest.NewParameter("expiresAt", smartcontract.IntegerType)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AttributeRevokedEvent, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("key", smartcontract.StringType), - manifest.NewParameter("revokedBy", smartcontract.Hash160Type), - manifest.NewParameter("reason", smartcontract.StringType)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AuthChallengeCreatedEvent, - manifest.NewParameter("challengeId", smartcontract.ByteArrayType), - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("purpose", smartcontract.StringType), - manifest.NewParameter("expiresAt", smartcontract.IntegerType)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(AuthenticationSuccessEvent, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("purpose", smartcontract.StringType), - manifest.NewParameter("timestamp", smartcontract.IntegerType)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(RecoveryInitiatedEvent, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("requestId", smartcontract.ByteArrayType), - manifest.NewParameter("delayUntil", smartcontract.IntegerType)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(RecoveryApprovalEvent, - manifest.NewParameter("requestId", smartcontract.ByteArrayType), - manifest.NewParameter("approver", smartcontract.Hash160Type), - manifest.NewParameter("approvalCount", smartcontract.IntegerType), - manifest.NewParameter("required", smartcontract.IntegerType)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(RecoveryExecutedEvent, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("oldOwner", smartcontract.Hash160Type), - manifest.NewParameter("newOwner", smartcontract.Hash160Type)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(RecoveryCancelledEvent, - manifest.NewParameter("requestId", smartcontract.ByteArrayType), - manifest.NewParameter("cancelledBy", smartcontract.Hash160Type)) - v.AddEvent(NewEvent(eDesc)) - - eDesc = NewEventDescriptor(VestingUpdatedEvent, - manifest.NewParameter("tokenId", smartcontract.IntegerType), - manifest.NewParameter("vestedUntil", smartcontract.IntegerType), - manifest.NewParameter("updatedBy", smartcontract.Hash160Type)) - v.AddEvent(NewEvent(eDesc)) - - return v -} - -// Metadata returns contract metadata. -func (v *Vita) Metadata() *interop.ContractMD { - return &v.ContractMD -} - -// Initialize initializes Vita contract at the specified hardfork. -func (v *Vita) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != v.ActiveIn() { - return nil - } - - cache := &VitaCache{ - tokenCount: 0, - } - ic.DAO.SetCache(v.ID, cache) - - // Initialize token counter to 0 - v.setTokenCounter(ic.DAO, 0) - - return nil -} - -// InitializeCache fills native Vita cache from DAO on node restart. -func (v *Vita) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cache := &VitaCache{} - - // Load token counter from storage - counter := v.getTokenCounter(d) - cache.tokenCount = counter - - d.SetCache(v.ID, cache) - return nil -} - -// OnPersist implements the Contract interface. -func (v *Vita) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (v *Vita) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn returns the hardfork this contract activates in. -// Vita is always active (returns nil). -func (v *Vita) ActiveIn() *config.Hardfork { - return nil -} - -// Storage key helpers - -func makeTokenByOwnerKey(owner util.Uint160) []byte { - key := make([]byte, 1+util.Uint160Size) - key[0] = prefixTokenByOwner - copy(key[1:], owner.BytesBE()) - return key -} - -func makeTokenByIDKey(tokenID uint64) []byte { - key := make([]byte, 9) - key[0] = prefixTokenByID - binary.BigEndian.PutUint64(key[1:], tokenID) - return key -} - -func makePersonHashKey(personHash []byte) []byte { - key := make([]byte, 1+len(personHash)) - key[0] = prefixPersonHash - copy(key[1:], personHash) - return key -} - -func makeAttributeKey(tokenID uint64, attrKey string) []byte { - keyBytes := []byte(attrKey) - key := make([]byte, 9+len(keyBytes)) - key[0] = prefixAttribute - binary.BigEndian.PutUint64(key[1:9], tokenID) - copy(key[9:], keyBytes) - return key -} - -func makeChallengeKey(challengeID util.Uint256) []byte { - key := make([]byte, 1+util.Uint256Size) - key[0] = prefixChallenge - copy(key[1:], challengeID.BytesBE()) - return key -} - -func makeLastAuthKey(tokenID uint64, purpose string) []byte { - purposeBytes := []byte(purpose) - key := make([]byte, 9+len(purposeBytes)) - key[0] = prefixConfig // Using config prefix with tokenID+purpose for last auth tracking - binary.BigEndian.PutUint64(key[1:9], tokenID) - copy(key[9:], purposeBytes) - return key -} - -func makeRecoveryKey(requestID util.Uint256) []byte { - key := make([]byte, 1+util.Uint256Size) - key[0] = prefixRecovery - copy(key[1:], requestID.BytesBE()) - return key -} - -func makeActiveRecoveryKey(tokenID uint64) []byte { - key := make([]byte, 9) - key[0] = prefixActiveRecovery - binary.BigEndian.PutUint64(key[1:], tokenID) - return key -} - -// Token counter methods - -func (v *Vita) getTokenCounter(d *dao.Simple) uint64 { - si := d.GetStorageItem(v.ID, []byte{prefixTokenCounter}) - if si == nil { - return 0 - } - return binary.BigEndian.Uint64(si) -} - -func (v *Vita) setTokenCounter(d *dao.Simple, count uint64) { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, count) - d.PutStorageItem(v.ID, []byte{prefixTokenCounter}, buf) -} - -func (v *Vita) getAndIncrementTokenCounter(d *dao.Simple) uint64 { - cache := d.GetRWCache(v.ID).(*VitaCache) - tokenID := cache.tokenCount - cache.tokenCount++ - v.setTokenCounter(d, cache.tokenCount) - return tokenID -} - -// Token storage methods - -func (v *Vita) putToken(d *dao.Simple, token *state.Vita) error { - item, err := token.ToStackItem() - if err != nil { - return err - } - data, err := stackitem.Serialize(item) - if err != nil { - return err - } - // Store by ID - d.PutStorageItem(v.ID, makeTokenByIDKey(token.TokenID), data) - // Store owner -> tokenID mapping - tokenIDBytes := make([]byte, 8) - binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) - d.PutStorageItem(v.ID, makeTokenByOwnerKey(token.Owner), tokenIDBytes) - // Store personHash -> tokenID mapping for uniqueness - if len(token.PersonHash) > 0 { - d.PutStorageItem(v.ID, makePersonHashKey(token.PersonHash), tokenIDBytes) - } - return nil -} - -func (v *Vita) getTokenByOwnerInternal(d *dao.Simple, owner util.Uint160) (*state.Vita, error) { - si := d.GetStorageItem(v.ID, makeTokenByOwnerKey(owner)) - if si == nil { - return nil, nil - } - tokenID := binary.BigEndian.Uint64(si) - return v.getTokenByIDInternal(d, tokenID) -} - -func (v *Vita) getTokenByIDInternal(d *dao.Simple, tokenID uint64) (*state.Vita, error) { - si := d.GetStorageItem(v.ID, makeTokenByIDKey(tokenID)) - if si == nil { - return nil, nil - } - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - token := new(state.Vita) - if err := token.FromStackItem(item); err != nil { - return nil, err - } - return token, nil -} - -func (v *Vita) tokenExistsForOwner(d *dao.Simple, owner util.Uint160) bool { - si := d.GetStorageItem(v.ID, makeTokenByOwnerKey(owner)) - return si != nil -} - -func (v *Vita) personHashExists(d *dao.Simple, personHash []byte) bool { - if len(personHash) == 0 { - return false - } - si := d.GetStorageItem(v.ID, makePersonHashKey(personHash)) - return si != nil -} - -// Contract methods - -// register creates a new Vita. -func (v *Vita) register(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - personHash := toBytes(args[1]) - isEntity, err := args[2].TryBool() - if err != nil { - panic(fmt.Errorf("invalid isEntity: %w", err)) - } - recoveryHash := toBytes(args[3]) - birthTimestamp := toUint64(args[4]) - - // Validate owner - if owner.Equals(util.Uint160{}) { - panic(ErrInvalidOwner) - } - - // Check witness for the owner - ok, err := runtime.CheckHashedWitness(ic, owner) - if err != nil || !ok { - panic(ErrVitaInvalidWitness) - } - - // Check if token already exists for this owner - if v.tokenExistsForOwner(ic.DAO, owner) { - panic(ErrTokenAlreadyExists) - } - - // Check if person hash is already linked to another token - if v.personHashExists(ic.DAO, personHash) { - panic(ErrPersonHashExists) - } - - // Get next token ID - tokenID := v.getAndIncrementTokenCounter(ic.DAO) - - // Create token with vesting period for Sybil resistance - // New tokens must vest before having full rights - vestedUntil := ic.Block.Index + defaultVestingPeriod - - token := &state.Vita{ - TokenID: tokenID, - Owner: owner, - PersonHash: personHash, - IsEntity: isEntity, - CreatedAt: ic.Block.Index, - UpdatedAt: ic.Block.Index, - Status: state.TokenStatusActive, - StatusReason: "", - RecoveryHash: recoveryHash, - VestedUntil: vestedUntil, - } - - // Store token - if err := v.putToken(ic.DAO, token); err != nil { - panic(err) - } - - // Register birth in Annos contract for lifespan tracking - // birthTimestamp is the actual birth date (Unix timestamp in seconds) - // This allows existing adults to register with their real birth date - // For newborns being registered at birth, use current block timestamp - if v.Annos != nil { - if err := v.Annos.RegisterBirthInternal(ic.DAO, ic, tokenID, owner, birthTimestamp); err != nil { - panic(fmt.Errorf("failed to register birth in Annos: %w", err)) - } - } - - // Generate token ID bytes for return and event - tokenIDBytes := hash.Sha256(append(owner.BytesBE(), personHash...)).BytesBE() - - // Emit event - err = ic.AddNotification(v.Hash, VitaCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(tokenIDBytes), - stackitem.NewByteArray(owner.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewByteArray(tokenIDBytes) -} - -// getToken returns the token for the given owner. -func (v *Vita) getToken(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - token, err := v.getTokenByOwnerInternal(ic.DAO, owner) - if err != nil { - panic(err) - } - if token == nil { - return stackitem.Null{} - } - - item, err := token.ToStackItem() - if err != nil { - panic(err) - } - return item -} - -// getTokenByID returns the token for the given token ID. -func (v *Vita) getTokenByID(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - - token, err := v.getTokenByIDInternal(ic.DAO, tokenID) - if err != nil { - panic(err) - } - if token == nil { - return stackitem.Null{} - } - - item, err := token.ToStackItem() - if err != nil { - panic(err) - } - return item -} - -// exists checks if a token exists for the given owner. -func (v *Vita) exists(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - return stackitem.NewBool(v.tokenExistsForOwner(ic.DAO, owner)) -} - -// totalSupply returns the total number of tokens. -func (v *Vita) totalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - cache := ic.DAO.GetROCache(v.ID).(*VitaCache) - return stackitem.NewBigInteger(big.NewInt(int64(cache.tokenCount))) -} - -// suspend temporarily suspends a token (committee only). -func (v *Vita) suspend(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - reason := toString(args[1]) - - // Check committee - if !v.checkCommittee(ic) { - panic(ErrNotCommittee) - } - - // Require liberty restriction order from Lex (due process protection) - // Suspending a Vita is a restriction of liberty, which requires judicial authority - if v.Lex != nil && !v.Lex.IsRestrictedInternal(ic.DAO, owner, state.RightLiberty, ic.Block.Index) { - panic("liberty restriction order required via Lex (due process)") - } - - // Get token - token, err := v.getTokenByOwnerInternal(ic.DAO, owner) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Check if already revoked - if token.Status == state.TokenStatusRevoked { - panic(ErrTokenRevoked) - } - - // Update status - token.Status = state.TokenStatusSuspended - token.StatusReason = reason - token.UpdatedAt = ic.Block.Index - - // Store updated token - if err := v.putToken(ic.DAO, token); err != nil { - panic(err) - } - - // Get caller for event - caller := ic.VM.GetCallingScriptHash() - - // Generate token ID bytes - tokenIDBytes := make([]byte, 8) - binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) - - // Emit event - err = ic.AddNotification(v.Hash, VitaSuspendedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(tokenIDBytes), - stackitem.NewByteArray([]byte(reason)), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// reinstate reinstates a suspended token (committee only). -func (v *Vita) reinstate(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - - // Check committee - if !v.checkCommittee(ic) { - panic(ErrNotCommittee) - } - - // Get token - token, err := v.getTokenByOwnerInternal(ic.DAO, owner) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Check if suspended - if token.Status != state.TokenStatusSuspended { - panic(ErrTokenNotSuspended) - } - - // Update status - token.Status = state.TokenStatusActive - token.StatusReason = "" - token.UpdatedAt = ic.Block.Index - - // Store updated token - if err := v.putToken(ic.DAO, token); err != nil { - panic(err) - } - - // Get caller for event - caller := ic.VM.GetCallingScriptHash() - - // Generate token ID bytes - tokenIDBytes := make([]byte, 8) - binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) - - // Emit event - err = ic.AddNotification(v.Hash, VitaReinstatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(tokenIDBytes), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// revoke permanently revokes a token (committee only). -func (v *Vita) revoke(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - reason := toString(args[1]) - - // Check committee - if !v.checkCommittee(ic) { - panic(ErrNotCommittee) - } - - // Require liberty restriction order from Lex (due process protection) - // Revoking a Vita is a permanent restriction of liberty, requiring judicial authority - if v.Lex != nil && !v.Lex.IsRestrictedInternal(ic.DAO, owner, state.RightLiberty, ic.Block.Index) { - panic("liberty restriction order required via Lex (due process)") - } - - // Get token - token, err := v.getTokenByOwnerInternal(ic.DAO, owner) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Check if already revoked - if token.Status == state.TokenStatusRevoked { - panic(ErrTokenRevoked) - } - - // Update status - token.Status = state.TokenStatusRevoked - token.StatusReason = reason - token.UpdatedAt = ic.Block.Index - - // Store updated token - if err := v.putToken(ic.DAO, token); err != nil { - panic(err) - } - - // Get caller for event - caller := ic.VM.GetCallingScriptHash() - - // Generate token ID bytes - tokenIDBytes := make([]byte, 8) - binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) - - // Emit event - err = ic.AddNotification(v.Hash, VitaRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(tokenIDBytes), - stackitem.NewByteArray([]byte(reason)), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// Public methods for cross-native access - -// GetTokenByOwner returns the token for the given owner (for cross-native access). -func (v *Vita) GetTokenByOwner(d *dao.Simple, owner util.Uint160) (*state.Vita, error) { - return v.getTokenByOwnerInternal(d, owner) -} - -// GetTokenByIDPublic returns the token for the given ID (for cross-native access). -func (v *Vita) GetTokenByIDPublic(d *dao.Simple, tokenID uint64) (*state.Vita, error) { - return v.getTokenByIDInternal(d, tokenID) -} - -// TokenExists returns true if a token exists for the given owner. -func (v *Vita) TokenExists(d *dao.Simple, owner util.Uint160) bool { - return v.tokenExistsForOwner(d, owner) -} - -// GetTotalTokenCount returns the total number of tokens issued (for quorum calculations). -func (v *Vita) GetTotalTokenCount(d *dao.Simple) uint64 { - return v.getTokenCounter(d) -} - -// ExistsInternal checks if a Vita token with the given ID exists. -func (v *Vita) ExistsInternal(d *dao.Simple, vitaID uint64) bool { - token, err := v.getTokenByIDInternal(d, vitaID) - return err == nil && token != nil -} - -// OwnerOfInternal returns the owner of a Vita token by ID. -// Returns empty Uint160 if token doesn't exist. -func (v *Vita) OwnerOfInternal(d *dao.Simple, vitaID uint64) util.Uint160 { - token, err := v.getTokenByIDInternal(d, vitaID) - if err != nil || token == nil { - return util.Uint160{} - } - return token.Owner -} - -// IsAdultVerified checks if the owner has a verified "age_verified" attribute -// indicating they are 18+ years old. Used for age-restricted purchases. -// The attribute must be non-revoked and not expired. -func (v *Vita) IsAdultVerified(d *dao.Simple, owner util.Uint160) bool { - token, err := v.getTokenByOwnerInternal(d, owner) - if err != nil || token == nil { - return false - } - - // Check for "age_verified" attribute - attr, err := v.getAttributeInternal(d, token.TokenID, "age_verified") - if err != nil || attr == nil { - return false - } - - // Check attribute is not revoked - if attr.Revoked { - return false - } - - // Note: Expiration check would require current block height - // For now, we check if ExpiresAt is set and > 0 means it could expire - // In production, pass block height and compare - return true -} - -// Attribute storage methods - -func (v *Vita) putAttribute(d *dao.Simple, tokenID uint64, attr *state.Attribute) error { - item, err := attr.ToStackItem() - if err != nil { - return err - } - data, err := stackitem.Serialize(item) - if err != nil { - return err - } - d.PutStorageItem(v.ID, makeAttributeKey(tokenID, attr.Key), data) - return nil -} - -func (v *Vita) getAttributeInternal(d *dao.Simple, tokenID uint64, key string) (*state.Attribute, error) { - si := d.GetStorageItem(v.ID, makeAttributeKey(tokenID, key)) - if si == nil { - return nil, nil - } - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - attr := new(state.Attribute) - if err := attr.FromStackItem(item); err != nil { - return nil, err - } - return attr, nil -} - -// setAttribute sets or updates an attribute on a token. -// The caller must be either the token owner (self-attestation) or any other account (third-party attestation). -func (v *Vita) setAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - key := toString(args[1]) - valueHash := toBytes(args[2]) - valueEnc := toBytes(args[3]) - expiresAt := uint32(toBigInt(args[4]).Int64()) - disclosureLevel := state.DisclosureLevel(toBigInt(args[5]).Int64()) - - // Validate inputs - if len(key) == 0 || len(key) > 64 { - panic(ErrInvalidAttributeKey) - } - if len(valueHash) == 0 || len(valueHash) > 64 { - panic(ErrInvalidValueHash) - } - if disclosureLevel > state.DisclosurePublic { - panic(ErrInvalidDisclosureLevel) - } - - // Get token - token, err := v.getTokenByIDInternal(ic.DAO, tokenID) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Check token is active - if token.Status != state.TokenStatusActive { - panic(ErrTokenNotActive) - } - - // Get the attestor (caller) - attestor := ic.VM.GetCallingScriptHash() - - // If attestor is the owner, check witness - // Otherwise, any contract/account can attest (third-party attestation) - if attestor.Equals(token.Owner) { - ok, err := runtime.CheckHashedWitness(ic, token.Owner) - if err != nil || !ok { - panic(ErrVitaInvalidWitness) - } - } - - // Create attribute - attr := &state.Attribute{ - Key: key, - ValueHash: valueHash, - ValueEnc: valueEnc, - Attestor: attestor, - AttestedAt: ic.Block.Index, - ExpiresAt: expiresAt, - Revoked: false, - RevokedAt: 0, - DisclosureLevel: disclosureLevel, - } - - // Store attribute - if err := v.putAttribute(ic.DAO, tokenID, attr); err != nil { - panic(err) - } - - // Emit event - err = ic.AddNotification(v.Hash, AttributeSetEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(tokenID))), - stackitem.NewByteArray([]byte(key)), - stackitem.NewByteArray(attestor.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(expiresAt))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// getAttribute returns an attribute for a token. -func (v *Vita) getAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - key := toString(args[1]) - - attr, err := v.getAttributeInternal(ic.DAO, tokenID, key) - if err != nil { - panic(err) - } - if attr == nil { - return stackitem.Null{} - } - - item, err := attr.ToStackItem() - if err != nil { - panic(err) - } - return item -} - -// revokeAttribute revokes an attribute. -// Can be called by the token owner, the original attestor, or committee. -func (v *Vita) revokeAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - key := toString(args[1]) - reason := toString(args[2]) - - // Get token - token, err := v.getTokenByIDInternal(ic.DAO, tokenID) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Get attribute - attr, err := v.getAttributeInternal(ic.DAO, tokenID, key) - if err != nil { - panic(err) - } - if attr == nil { - panic(ErrAttributeNotFound) - } - - // Check if already revoked - if attr.Revoked { - panic(ErrAttributeRevoked) - } - - // Check authorization: owner, attestor, or committee - caller := ic.VM.GetCallingScriptHash() - isOwner := caller.Equals(token.Owner) - isAttestor := caller.Equals(attr.Attestor) - isCommittee := v.checkCommittee(ic) - - if isOwner { - ok, err := runtime.CheckHashedWitness(ic, token.Owner) - if err != nil || !ok { - panic(ErrVitaInvalidWitness) - } - } else if !isAttestor && !isCommittee { - panic(ErrVitaInvalidWitness) - } - - // Revoke attribute - attr.Revoked = true - attr.RevokedAt = ic.Block.Index - - // Store updated attribute - if err := v.putAttribute(ic.DAO, tokenID, attr); err != nil { - panic(err) - } - - // Emit event - err = ic.AddNotification(v.Hash, AttributeRevokedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(tokenID))), - stackitem.NewByteArray([]byte(key)), - stackitem.NewByteArray(caller.BytesBE()), - stackitem.NewByteArray([]byte(reason)), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// verifyAttribute verifies that an attribute exists and its hash matches. -// Returns true if the attribute exists, is not revoked, not expired, and hash matches. -func (v *Vita) verifyAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - key := toString(args[1]) - expectedHash := toBytes(args[2]) - - // Get attribute - attr, err := v.getAttributeInternal(ic.DAO, tokenID, key) - if err != nil { - panic(err) - } - if attr == nil { - return stackitem.NewBool(false) - } - - // Check if revoked - if attr.Revoked { - return stackitem.NewBool(false) - } - - // Check if expired (0 means never expires) - if attr.ExpiresAt > 0 && ic.Block.Index > attr.ExpiresAt { - return stackitem.NewBool(false) - } - - // Compare hashes - if len(attr.ValueHash) != len(expectedHash) { - return stackitem.NewBool(false) - } - for i := range attr.ValueHash { - if attr.ValueHash[i] != expectedHash[i] { - return stackitem.NewBool(false) - } - } - - return stackitem.NewBool(true) -} - -// Public methods for cross-native attribute access - -// GetAttribute returns an attribute for the given token and key (for cross-native access). -func (v *Vita) GetAttribute(d *dao.Simple, tokenID uint64, key string) (*state.Attribute, error) { - return v.getAttributeInternal(d, tokenID, key) -} - -// Challenge storage methods - -func (v *Vita) putChallenge(d *dao.Simple, challenge *state.AuthChallenge) error { - item, err := challenge.ToStackItem() - if err != nil { - return err - } - data, err := stackitem.Serialize(item) - if err != nil { - return err - } - d.PutStorageItem(v.ID, makeChallengeKey(challenge.ChallengeID), data) - return nil -} - -func (v *Vita) getChallengeInternal(d *dao.Simple, challengeID util.Uint256) (*state.AuthChallenge, error) { - si := d.GetStorageItem(v.ID, makeChallengeKey(challengeID)) - if si == nil { - return nil, nil - } - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - challenge := new(state.AuthChallenge) - if err := challenge.FromStackItem(item); err != nil { - return nil, err - } - return challenge, nil -} - -func (v *Vita) setLastAuth(d *dao.Simple, tokenID uint64, purpose string, blockHeight uint32) { - buf := make([]byte, 4) - binary.BigEndian.PutUint32(buf, blockHeight) - d.PutStorageItem(v.ID, makeLastAuthKey(tokenID, purpose), buf) -} - -func (v *Vita) getLastAuth(d *dao.Simple, tokenID uint64, purpose string) uint32 { - si := d.GetStorageItem(v.ID, makeLastAuthKey(tokenID, purpose)) - if si == nil { - return 0 - } - return binary.BigEndian.Uint32(si) -} - -// Default challenge expiry in blocks (approximately 5 minutes at 15 seconds per block) -const defaultChallengeExpiry uint32 = 20 - -// createChallenge creates a new authentication challenge for a token owner. -// Anyone can create a challenge for any token owner. -func (v *Vita) createChallenge(ic *interop.Context, args []stackitem.Item) stackitem.Item { - owner := toUint160(args[0]) - purpose := toString(args[1]) - - // Validate purpose - if len(purpose) == 0 || len(purpose) > 32 { - panic(ErrInvalidPurpose) - } - - // Get token - token, err := v.getTokenByOwnerInternal(ic.DAO, owner) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Check token is active - if token.Status != state.TokenStatusActive { - panic(ErrTokenNotActive) - } - - // Generate nonce (use transaction hash + block index + token ID for uniqueness) - nonceData := append(ic.Tx.Hash().BytesBE(), make([]byte, 8)...) - binary.BigEndian.PutUint64(nonceData[util.Uint256Size:], token.TokenID) - nonce := hash.Sha256(nonceData).BytesBE() - - // Generate challenge ID - challengeIDData := append(nonce, owner.BytesBE()...) - challengeIDData = append(challengeIDData, []byte(purpose)...) - challengeID := hash.Sha256(challengeIDData) - - // Create challenge - challenge := &state.AuthChallenge{ - ChallengeID: challengeID, - TokenID: token.TokenID, - Nonce: nonce, - CreatedAt: ic.Block.Index, - ExpiresAt: ic.Block.Index + defaultChallengeExpiry, - Purpose: purpose, - Fulfilled: false, - FulfilledAt: 0, - } - - // Store challenge - if err := v.putChallenge(ic.DAO, challenge); err != nil { - panic(err) - } - - // Emit event - err = ic.AddNotification(v.Hash, AuthChallengeCreatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(challengeID.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))), - stackitem.NewByteArray([]byte(purpose)), - stackitem.NewBigInteger(big.NewInt(int64(challenge.ExpiresAt))), - })) - if err != nil { - panic(err) - } - - // Return challenge as struct - item, err := challenge.ToStackItem() - if err != nil { - panic(err) - } - return item -} - -// getChallenge returns a challenge by ID. -func (v *Vita) getChallenge(ic *interop.Context, args []stackitem.Item) stackitem.Item { - challengeIDBytes := toBytes(args[0]) - if len(challengeIDBytes) != util.Uint256Size { - panic("invalid challenge ID length") - } - challengeID, err := util.Uint256DecodeBytesBE(challengeIDBytes) - if err != nil { - panic(err) - } - - challenge, err := v.getChallengeInternal(ic.DAO, challengeID) - if err != nil { - panic(err) - } - if challenge == nil { - return stackitem.Null{} - } - - item, err := challenge.ToStackItem() - if err != nil { - panic(err) - } - return item -} - -// fulfillChallenge fulfills an authentication challenge by providing a valid signature. -// The signature must be from the token owner over the challenge nonce. -func (v *Vita) fulfillChallenge(ic *interop.Context, args []stackitem.Item) stackitem.Item { - challengeIDBytes := toBytes(args[0]) - if len(challengeIDBytes) != util.Uint256Size { - panic("invalid challenge ID length") - } - challengeID, err := util.Uint256DecodeBytesBE(challengeIDBytes) - if err != nil { - panic(err) - } - _ = toBytes(args[1]) // signature - validation happens via witness - - // Get challenge - challenge, err := v.getChallengeInternal(ic.DAO, challengeID) - if err != nil { - panic(err) - } - if challenge == nil { - panic(ErrChallengeNotFound) - } - - // Check if already fulfilled - if challenge.Fulfilled { - panic(ErrChallengeAlreadyFulfilled) - } - - // Check if expired - if ic.Block.Index > challenge.ExpiresAt { - panic(ErrChallengeExpired) - } - - // Get token - token, err := v.getTokenByIDInternal(ic.DAO, challenge.TokenID) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Verify the caller has witness for the token owner - // This ensures the owner is actually signing this transaction - ok, err := runtime.CheckHashedWitness(ic, token.Owner) - if err != nil || !ok { - panic(ErrInvalidSignature) - } - - // Mark challenge as fulfilled - challenge.Fulfilled = true - challenge.FulfilledAt = ic.Block.Index - - // Store updated challenge - if err := v.putChallenge(ic.DAO, challenge); err != nil { - panic(err) - } - - // Record last auth time for this token and purpose - v.setLastAuth(ic.DAO, challenge.TokenID, challenge.Purpose, ic.Block.Index) - - // Emit success event - err = ic.AddNotification(v.Hash, AuthenticationSuccessEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(challenge.TokenID))), - stackitem.NewByteArray([]byte(challenge.Purpose)), - stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// verifyAuth checks if a token has authenticated recently for a given purpose. -// Returns true if the last successful authentication for the purpose was within maxAge blocks. -func (v *Vita) verifyAuth(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - purpose := toString(args[1]) - maxAge := uint32(toBigInt(args[2]).Int64()) - - // Get last auth time - lastAuth := v.getLastAuth(ic.DAO, tokenID, purpose) - if lastAuth == 0 { - return stackitem.NewBool(false) - } - - // Check if within maxAge - if ic.Block.Index > lastAuth+maxAge { - return stackitem.NewBool(false) - } - - return stackitem.NewBool(true) -} - -// Public methods for cross-native authentication access - -// GetLastAuth returns the last authentication block height for a token and purpose. -func (v *Vita) GetLastAuth(d *dao.Simple, tokenID uint64, purpose string) uint32 { - return v.getLastAuth(d, tokenID, purpose) -} - -// Recovery configuration constants -const ( - // defaultRecoveryDelay is the number of blocks before recovery can be executed (approximately 24 hours at 15s blocks) - defaultRecoveryDelay uint32 = 5760 - // defaultRecoveryExpiry is the number of blocks until a recovery request expires (approximately 7 days) - defaultRecoveryExpiry uint32 = 40320 - // defaultRequiredApprovals is the number of committee approvals needed for recovery - defaultRequiredApprovals int = 2 -) - -// Recovery storage methods - -func (v *Vita) putRecoveryRequest(d *dao.Simple, req *state.RecoveryRequest) error { - item, err := req.ToStackItem() - if err != nil { - return err - } - data, err := stackitem.Serialize(item) - if err != nil { - return err - } - d.PutStorageItem(v.ID, makeRecoveryKey(req.RequestID), data) - return nil -} - -func (v *Vita) getRecoveryRequestInternal(d *dao.Simple, requestID util.Uint256) (*state.RecoveryRequest, error) { - si := d.GetStorageItem(v.ID, makeRecoveryKey(requestID)) - if si == nil { - return nil, nil - } - item, err := stackitem.Deserialize(si) - if err != nil { - return nil, err - } - req := new(state.RecoveryRequest) - if err := req.FromStackItem(item); err != nil { - return nil, err - } - return req, nil -} - -func (v *Vita) setActiveRecovery(d *dao.Simple, tokenID uint64, requestID util.Uint256) { - d.PutStorageItem(v.ID, makeActiveRecoveryKey(tokenID), requestID.BytesBE()) -} - -func (v *Vita) getActiveRecovery(d *dao.Simple, tokenID uint64) *util.Uint256 { - si := d.GetStorageItem(v.ID, makeActiveRecoveryKey(tokenID)) - if si == nil { - return nil - } - requestID, err := util.Uint256DecodeBytesBE(si) - if err != nil { - return nil - } - return &requestID -} - -func (v *Vita) clearActiveRecovery(d *dao.Simple, tokenID uint64) { - d.DeleteStorageItem(v.ID, makeActiveRecoveryKey(tokenID)) -} - -// initiateRecovery starts a key recovery process for a token. -// Anyone can initiate recovery for any token. -func (v *Vita) initiateRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - newOwner := toUint160(args[1]) - evidence := toBytes(args[2]) - - // Validate new owner - if newOwner.Equals(util.Uint160{}) { - panic(ErrInvalidNewOwner) - } - - // Get token - token, err := v.getTokenByIDInternal(ic.DAO, tokenID) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Check token is not revoked - if token.Status == state.TokenStatusRevoked { - panic(ErrTokenRevoked) - } - - // Check token is not already in recovery - if token.Status == state.TokenStatusRecovering { - panic(ErrTokenInRecovery) - } - - // Check no active recovery exists - if v.getActiveRecovery(ic.DAO, tokenID) != nil { - panic(ErrRecoveryAlreadyActive) - } - - // Get requester (caller) - requester := ic.VM.GetCallingScriptHash() - - // Generate request ID - requestIDData := append(ic.Tx.Hash().BytesBE(), make([]byte, 8)...) - binary.BigEndian.PutUint64(requestIDData[util.Uint256Size:], tokenID) - requestIDData = append(requestIDData, newOwner.BytesBE()...) - requestID := hash.Sha256(requestIDData) - - // Create recovery request - req := &state.RecoveryRequest{ - RequestID: requestID, - TokenID: tokenID, - NewOwner: newOwner, - Requester: requester, - Evidence: evidence, - Approvals: []util.Uint160{}, - RequiredApprovals: defaultRequiredApprovals, - CreatedAt: ic.Block.Index, - DelayUntil: ic.Block.Index + defaultRecoveryDelay, - ExpiresAt: ic.Block.Index + defaultRecoveryExpiry, - Status: state.RecoveryStatusPending, - } - - // Store recovery request - if err := v.putRecoveryRequest(ic.DAO, req); err != nil { - panic(err) - } - - // Mark active recovery for token - v.setActiveRecovery(ic.DAO, tokenID, requestID) - - // Update token status to recovering - token.Status = state.TokenStatusRecovering - token.StatusReason = "recovery initiated" - token.UpdatedAt = ic.Block.Index - if err := v.putToken(ic.DAO, token); err != nil { - panic(err) - } - - // Emit event - err = ic.AddNotification(v.Hash, RecoveryInitiatedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(tokenID))), - stackitem.NewByteArray(requestID.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(req.DelayUntil))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewByteArray(requestID.BytesBE()) -} - -// approveRecovery approves a recovery request (committee member only). -func (v *Vita) approveRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestIDBytes := toBytes(args[0]) - if len(requestIDBytes) != util.Uint256Size { - panic("invalid request ID length") - } - requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) - if err != nil { - panic(err) - } - - // Check committee - if !v.checkCommittee(ic) { - panic(ErrNotCommittee) - } - - // Get recovery request - req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) - if err != nil { - panic(err) - } - if req == nil { - panic(ErrRecoveryNotFound) - } - - // Check status is pending - if req.Status != state.RecoveryStatusPending { - panic(ErrRecoveryNotPending) - } - - // Check not expired - if ic.Block.Index > req.ExpiresAt { - panic(ErrRecoveryExpired) - } - - // Get approver (caller) - approver := ic.VM.GetCallingScriptHash() - - // Check not already approved by this approver - for _, a := range req.Approvals { - if a.Equals(approver) { - panic(ErrAlreadyApproved) - } - } - - // Add approval - req.Approvals = append(req.Approvals, approver) - - // Check if sufficient approvals - if len(req.Approvals) >= req.RequiredApprovals { - req.Status = state.RecoveryStatusApproved - } - - // Store updated request - if err := v.putRecoveryRequest(ic.DAO, req); err != nil { - panic(err) - } - - // Emit event - err = ic.AddNotification(v.Hash, RecoveryApprovalEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(requestID.BytesBE()), - stackitem.NewByteArray(approver.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(len(req.Approvals)))), - stackitem.NewBigInteger(big.NewInt(int64(req.RequiredApprovals))), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// executeRecovery executes an approved recovery request after the delay period. -func (v *Vita) executeRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestIDBytes := toBytes(args[0]) - if len(requestIDBytes) != util.Uint256Size { - panic("invalid request ID length") - } - requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) - if err != nil { - panic(err) - } - - // Get recovery request - req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) - if err != nil { - panic(err) - } - if req == nil { - panic(ErrRecoveryNotFound) - } - - // Check status is approved - if req.Status != state.RecoveryStatusApproved { - panic(ErrRecoveryNotPending) - } - - // Check delay period has passed - if ic.Block.Index < req.DelayUntil { - panic(ErrRecoveryDelayNotPassed) - } - - // Check not expired - if ic.Block.Index > req.ExpiresAt { - panic(ErrRecoveryExpired) - } - - // Get token - token, err := v.getTokenByIDInternal(ic.DAO, req.TokenID) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Store old owner for event - oldOwner := token.Owner - - // Delete old owner -> tokenID mapping - ic.DAO.DeleteStorageItem(v.ID, makeTokenByOwnerKey(oldOwner)) - - // Update token owner - token.Owner = req.NewOwner - token.Status = state.TokenStatusActive - token.StatusReason = "" - token.UpdatedAt = ic.Block.Index - - // Store updated token (this will create new owner -> tokenID mapping) - if err := v.putToken(ic.DAO, token); err != nil { - panic(err) - } - - // Update request status - req.Status = state.RecoveryStatusExecuted - if err := v.putRecoveryRequest(ic.DAO, req); err != nil { - panic(err) - } - - // Clear active recovery - v.clearActiveRecovery(ic.DAO, req.TokenID) - - // Emit event - err = ic.AddNotification(v.Hash, RecoveryExecutedEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(req.TokenID))), - stackitem.NewByteArray(oldOwner.BytesBE()), - stackitem.NewByteArray(req.NewOwner.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// cancelRecovery cancels an active recovery request. -// Can be called by the original token owner or the requester. -func (v *Vita) cancelRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestIDBytes := toBytes(args[0]) - if len(requestIDBytes) != util.Uint256Size { - panic("invalid request ID length") - } - requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) - if err != nil { - panic(err) - } - - // Get recovery request - req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) - if err != nil { - panic(err) - } - if req == nil { - panic(ErrRecoveryNotFound) - } - - // Check status is pending or approved (not executed or denied) - if req.Status == state.RecoveryStatusExecuted || req.Status == state.RecoveryStatusDenied { - panic(ErrRecoveryNotPending) - } - - // Get token - token, err := v.getTokenByIDInternal(ic.DAO, req.TokenID) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Check authorization: owner, requester, or committee - caller := ic.VM.GetCallingScriptHash() - isOwner := caller.Equals(token.Owner) - isRequester := caller.Equals(req.Requester) - isCommittee := v.checkCommittee(ic) - - if isOwner { - ok, err := runtime.CheckHashedWitness(ic, token.Owner) - if err != nil || !ok { - panic(ErrVitaInvalidWitness) - } - } else if !isRequester && !isCommittee { - panic(ErrVitaInvalidWitness) - } - - // Update request status - req.Status = state.RecoveryStatusDenied - if err := v.putRecoveryRequest(ic.DAO, req); err != nil { - panic(err) - } - - // Restore token status - token.Status = state.TokenStatusActive - token.StatusReason = "" - token.UpdatedAt = ic.Block.Index - if err := v.putToken(ic.DAO, token); err != nil { - panic(err) - } - - // Clear active recovery - v.clearActiveRecovery(ic.DAO, req.TokenID) - - // Emit event - err = ic.AddNotification(v.Hash, RecoveryCancelledEvent, stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(requestID.BytesBE()), - stackitem.NewByteArray(caller.BytesBE()), - })) - if err != nil { - panic(err) - } - - return stackitem.NewBool(true) -} - -// getRecoveryRequest returns a recovery request by ID. -func (v *Vita) getRecoveryRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { - requestIDBytes := toBytes(args[0]) - if len(requestIDBytes) != util.Uint256Size { - panic("invalid request ID length") - } - requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) - if err != nil { - panic(err) - } - - req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) - if err != nil { - panic(err) - } - if req == nil { - return stackitem.Null{} - } - - item, err := req.ToStackItem() - if err != nil { - panic(err) - } - return item -} - -// Public methods for cross-native recovery access - -// GetRecoveryRequest returns a recovery request by ID (for cross-native access). -func (v *Vita) GetRecoveryRequest(d *dao.Simple, requestID util.Uint256) (*state.RecoveryRequest, error) { - return v.getRecoveryRequestInternal(d, requestID) -} - -// GetActiveRecoveryForToken returns the active recovery request ID for a token (for cross-native access). -func (v *Vita) GetActiveRecoveryForToken(d *dao.Simple, tokenID uint64) *util.Uint256 { - return v.getActiveRecovery(d, tokenID) -} - -// Cross-contract integration methods - -// validateCaller validates that the calling contract's owner has a valid Vita. -// Returns the tokenID and core roles for the caller. -func (v *Vita) validateCaller(ic *interop.Context, args []stackitem.Item) stackitem.Item { - caller := ic.VM.GetCallingScriptHash() - - // Get token by caller address - token, err := v.getTokenByOwnerInternal(ic.DAO, caller) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrCallerHasNoVita) - } - - // Check token is active - if token.Status != state.TokenStatusActive { - if token.Status == state.TokenStatusSuspended { - panic(ErrTokenSuspended) - } - if token.Status == state.TokenStatusRevoked { - panic(ErrTokenRevoked) - } - if token.Status == state.TokenStatusRecovering { - panic(ErrTokenInRecovery) - } - panic(ErrTokenNotActive) - } - - // Determine core roles - roles := v.getCoreRoles(ic, token) - - // Return struct with tokenID and roles - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))), - stackitem.NewBigInteger(big.NewInt(int64(roles))), - }) -} - -// getCoreRoles determines the core roles for a token holder. -func (v *Vita) getCoreRoles(ic *interop.Context, token *state.Vita) uint64 { - var roles uint64 - - // All active token holders have User role - roles |= 1 << uint64(CoreRoleUser) - - // Check if user is verified (has non-expired, non-revoked attributes) - if v.hasVerifiedAttributes(ic.DAO, token.TokenID) { - roles |= 1 << uint64(CoreRoleVerified) - } - - // Check if user is a committee member - if v.checkCommittee(ic) { - roles |= 1 << uint64(CoreRoleCommittee) - roles |= 1 << uint64(CoreRoleAttestor) // Committee members can attest - roles |= 1 << uint64(CoreRoleRecovery) // Committee members participate in recovery - } - - return roles -} - -// hasVerifiedAttributes checks if a token has any valid (non-expired, non-revoked) attributes. -func (v *Vita) hasVerifiedAttributes(d *dao.Simple, tokenID uint64) bool { - // For now, just check if any attribute exists - // In a full implementation, this would scan attributes and check expiry/revocation - prefix := make([]byte, 9) - prefix[0] = prefixAttribute - binary.BigEndian.PutUint64(prefix[1:], tokenID) - - // Check if any attribute storage items exist for this token - var found bool - d.Seek(v.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { - found = true - return false // Stop after finding first - }) - return found -} - -// requireRole checks if the caller has a specific role ID (for RoleRegistry integration). -// This is a stub that will be extended when RoleRegistry is implemented. -func (v *Vita) requireRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { - roleID := toBigInt(args[0]).Uint64() - - caller := ic.VM.GetCallingScriptHash() - - // Get token by caller address - token, err := v.getTokenByOwnerInternal(ic.DAO, caller) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrCallerHasNoVita) - } - - // Check token is active - if token.Status != state.TokenStatusActive { - panic(ErrTokenNotActive) - } - - // Stub: In the future, this will check against RoleRegistry - // For now, just validate the token exists and is active - // Role 0 means "any authenticated user" - if roleID == 0 { - return stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))) - } - - // For non-zero roles, check if it maps to a core role - if roleID <= uint64(CoreRoleRecovery) { - roles := v.getCoreRoles(ic, token) - if roles&(1< CoreRoleRecovery { - panic(ErrVitaInvalidRole) - } - - caller := ic.VM.GetCallingScriptHash() - - // Get token by caller address - token, err := v.getTokenByOwnerInternal(ic.DAO, caller) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrCallerHasNoVita) - } - - // Check token is active - if token.Status != state.TokenStatusActive { - panic(ErrTokenNotActive) - } - - // CoreRoleNone means any authenticated user - if coreRole == CoreRoleNone { - return stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))) - } - - // Check if user has the required core role - roles := v.getCoreRoles(ic, token) - if roles&(1<= vestedUntil. -func (v *Vita) isFullyVested(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - - token, err := v.getTokenByIDInternal(ic.DAO, tokenID) - if err != nil { - panic(err) - } - if token == nil { - panic(ErrTokenNotFound) - } - - // Token is fully vested if current block >= vestedUntil - // VestedUntil of 0 means immediately vested (legacy tokens) - isVested := token.VestedUntil == 0 || ic.Block.Index >= token.VestedUntil - return stackitem.NewBool(isVested) -} - -// getVestingInfo returns vesting information for a Vita token. -// Returns [vestedUntil, isFullyVested, remainingBlocks] or null if token not found. -func (v *Vita) getVestingInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tokenID := toBigInt(args[0]).Uint64() - - token, err := v.getTokenByIDInternal(ic.DAO, tokenID) - if err != nil { - panic(err) - } - if token == nil { - return stackitem.Null{} - } - - isVested := token.VestedUntil == 0 || ic.Block.Index >= token.VestedUntil - - var remainingBlocks uint32 - if !isVested && token.VestedUntil > ic.Block.Index { - remainingBlocks = token.VestedUntil - ic.Block.Index - } - - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(token.VestedUntil))), - stackitem.NewBool(isVested), - stackitem.NewBigInteger(big.NewInt(int64(remainingBlocks))), - }) -} - -// Public methods for cross-native vesting access - -// IsFullyVestedInternal checks if a Vita token has completed its vesting period. -// For cross-contract use by other native contracts like Collocatio and Eligere. -func (v *Vita) IsFullyVestedInternal(d *dao.Simple, tokenID uint64, currentBlock uint32) bool { - token, err := v.getTokenByIDInternal(d, tokenID) - if err != nil || token == nil { - return false - } - // VestedUntil of 0 means immediately vested (legacy tokens) - return token.VestedUntil == 0 || currentBlock >= token.VestedUntil -} - -// GetVestedUntil returns the vesting block height for a Vita token. -// Returns 0 if token not found. -func (v *Vita) GetVestedUntil(d *dao.Simple, tokenID uint64) uint32 { - token, err := v.getTokenByIDInternal(d, tokenID) - if err != nil || token == nil { - return 0 - } - return token.VestedUntil -} +package native + +import ( + "encoding/binary" + "errors" + "fmt" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Vita represents a soul-bound identity native contract. +type Vita struct { + interop.ContractMD + Tutus ITutus + RoleRegistry IRoleRegistry + Lex ILex + Annos IAnnos +} + +// VitaCache represents the cached state for Vita contract. +type VitaCache struct { + tokenCount uint64 +} + +// Storage key prefixes for Vita. +const ( + prefixTokenByOwner = 0x01 // owner (Uint160) -> tokenID (uint64) + prefixTokenByID = 0x02 // tokenID (uint64) -> Vita token + prefixPersonHash = 0x03 // personHash -> tokenID (uniqueness) + prefixAttribute = 0x04 // tokenID + attrKey -> Attribute + prefixChallenge = 0x05 // challengeID -> AuthChallenge + prefixRecovery = 0x06 // requestID -> RecoveryRequest + prefixActiveRecovery = 0x07 // tokenID + "recovery" -> requestID + prefixTokenCounter = 0x10 // -> uint64 + prefixConfig = 0x11 // -> VitaConfig +) + +// Event names for Vita. +const ( + VitaCreatedEvent = "VitaCreated" + VitaSuspendedEvent = "VitaSuspended" + VitaReinstatedEvent = "VitaReinstated" + VitaRevokedEvent = "VitaRevoked" + AttributeSetEvent = "AttributeSet" + AttributeRevokedEvent = "AttributeRevoked" + AuthenticationSuccessEvent = "AuthenticationSuccess" + AuthChallengeCreatedEvent = "AuthChallengeCreated" + RecoveryInitiatedEvent = "RecoveryInitiated" + RecoveryApprovalEvent = "RecoveryApproval" + RecoveryExecutedEvent = "RecoveryExecuted" + RecoveryCancelledEvent = "RecoveryCancelled" + VestingUpdatedEvent = "VestingUpdated" +) + +// Default vesting period in blocks (approximately 30 days at 1 second per block). +// New Vita tokens must vest before having full rights (Sybil resistance). +const defaultVestingPeriod uint32 = 2592000 // 30 days * 24 hours * 60 minutes * 60 seconds + +// Various errors. +var ( + ErrTokenAlreadyExists = errors.New("token already exists for this owner") + ErrPersonHashExists = errors.New("person hash already linked to another token") + ErrTokenNotFound = errors.New("token not found") + ErrTokenSuspended = errors.New("token is suspended") + ErrTokenRevoked = errors.New("token is revoked") + ErrTokenNotSuspended = errors.New("token is not suspended") + ErrInvalidOwner = errors.New("invalid owner") + ErrInvalidPersonHash = errors.New("invalid person hash") + ErrNotCommittee = errors.New("invalid committee signature") + ErrVitaInvalidWitness = errors.New("invalid witness") + ErrVitaNotFullyVested = errors.New("vita is not fully vested") + ErrAttributeNotFound = errors.New("attribute not found") + ErrAttributeRevoked = errors.New("attribute is already revoked") + ErrAttributeExpired = errors.New("attribute has expired") + ErrInvalidAttributeKey = errors.New("invalid attribute key") + ErrInvalidValueHash = errors.New("invalid value hash") + ErrInvalidDisclosureLevel = errors.New("invalid disclosure level") + ErrTokenNotActive = errors.New("token is not active") + ErrChallengeNotFound = errors.New("challenge not found") + ErrChallengeExpired = errors.New("challenge has expired") + ErrChallengeAlreadyFulfilled = errors.New("challenge already fulfilled") + ErrInvalidSignature = errors.New("invalid signature") + ErrInvalidPurpose = errors.New("invalid purpose") + ErrNoRecentAuth = errors.New("no recent authentication found") + ErrRecoveryNotFound = errors.New("recovery request not found") + ErrRecoveryAlreadyActive = errors.New("recovery already active for this token") + ErrRecoveryNotPending = errors.New("recovery is not in pending state") + ErrRecoveryDelayNotPassed = errors.New("recovery delay period has not passed") + ErrRecoveryExpired = errors.New("recovery request has expired") + ErrAlreadyApproved = errors.New("already approved this recovery") + ErrInvalidNewOwner = errors.New("invalid new owner") + ErrTokenInRecovery = errors.New("token is already in recovery") + ErrNotRecoveryRequester = errors.New("not the recovery requester") + ErrCallerHasNoVita = errors.New("caller does not have a Vita") + ErrRoleNotAssigned = errors.New("required role not assigned") + ErrPermissionDenied = errors.New("permission denied") + ErrVitaInvalidRole = errors.New("invalid core role") + ErrVitaInvalidResource = errors.New("invalid resource") + ErrVitaInvalidAction = errors.New("invalid action") +) + +// CoreRole represents built-in roles for cross-contract access control. +type CoreRole uint8 + +// Core roles for Vita holders. +const ( + CoreRoleNone CoreRole = 0 + CoreRoleUser CoreRole = 1 // Basic authenticated user + CoreRoleVerified CoreRole = 2 // User with verified attributes + CoreRoleCommittee CoreRole = 3 // Committee member + CoreRoleAttestor CoreRole = 4 // Can attest attributes for others + CoreRoleRecovery CoreRole = 5 // Can participate in recovery +) + +var ( + _ interop.Contract = (*Vita)(nil) + _ dao.NativeContractCache = (*VitaCache)(nil) +) + +// Copy implements NativeContractCache interface. +func (c *VitaCache) Copy() dao.NativeContractCache { + return &VitaCache{ + tokenCount: c.tokenCount, + } +} + +// checkCommittee checks if the caller has committee authority. +// Uses RoleRegistry if available, falls back to NEO.CheckCommittee(). +func (v *Vita) checkCommittee(ic *interop.Context) bool { + if v.RoleRegistry != nil { + return v.RoleRegistry.CheckCommittee(ic) + } + // Fallback to Tutus for backwards compatibility + return v.Tutus.CheckCommittee(ic) +} + +// newVita creates a new Vita native contract. +func newVita() *Vita { + v := &Vita{ + ContractMD: *interop.NewContractMD(nativenames.Vita, nativeids.Vita), + } + defer v.BuildHFSpecificMD(v.ActiveIn()) + + // Register method + desc := NewDescriptor("register", smartcontract.ByteArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("personHash", smartcontract.ByteArrayType), + manifest.NewParameter("isEntity", smartcontract.BoolType), + manifest.NewParameter("recoveryHash", smartcontract.ByteArrayType), + manifest.NewParameter("birthTimestamp", smartcontract.IntegerType)) + md := NewMethodAndPrice(v.register, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // GetToken method + desc = NewDescriptor("getToken", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.getToken, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // GetTokenByID method + desc = NewDescriptor("getTokenByID", smartcontract.ArrayType, + manifest.NewParameter("tokenId", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.getTokenByID, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // Exists method + desc = NewDescriptor("exists", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.exists, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // TotalSupply method + desc = NewDescriptor("totalSupply", smartcontract.IntegerType) + md = NewMethodAndPrice(v.totalSupply, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // Suspend method + desc = NewDescriptor("suspend", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(v.suspend, 1<<16, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // Reinstate method + desc = NewDescriptor("reinstate", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.reinstate, 1<<16, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // Revoke method + desc = NewDescriptor("revoke", smartcontract.BoolType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(v.revoke, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // SetAttribute method + desc = NewDescriptor("setAttribute", smartcontract.BoolType, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("key", smartcontract.StringType), + manifest.NewParameter("valueHash", smartcontract.ByteArrayType), + manifest.NewParameter("valueEnc", smartcontract.ByteArrayType), + manifest.NewParameter("expiresAt", smartcontract.IntegerType), + manifest.NewParameter("disclosureLevel", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.setAttribute, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // GetAttribute method + desc = NewDescriptor("getAttribute", smartcontract.ArrayType, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("key", smartcontract.StringType)) + md = NewMethodAndPrice(v.getAttribute, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // RevokeAttribute method + desc = NewDescriptor("revokeAttribute", smartcontract.BoolType, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("key", smartcontract.StringType), + manifest.NewParameter("reason", smartcontract.StringType)) + md = NewMethodAndPrice(v.revokeAttribute, 1<<16, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // VerifyAttribute method + desc = NewDescriptor("verifyAttribute", smartcontract.BoolType, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("key", smartcontract.StringType), + manifest.NewParameter("expectedHash", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.verifyAttribute, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // CreateChallenge method + desc = NewDescriptor("createChallenge", smartcontract.ArrayType, + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("purpose", smartcontract.StringType)) + md = NewMethodAndPrice(v.createChallenge, 1<<16, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // GetChallenge method + desc = NewDescriptor("getChallenge", smartcontract.ArrayType, + manifest.NewParameter("challengeId", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.getChallenge, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // FulfillChallenge method + desc = NewDescriptor("fulfillChallenge", smartcontract.BoolType, + manifest.NewParameter("challengeId", smartcontract.ByteArrayType), + manifest.NewParameter("signature", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.fulfillChallenge, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // VerifyAuth method + desc = NewDescriptor("verifyAuth", smartcontract.BoolType, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("purpose", smartcontract.StringType), + manifest.NewParameter("maxAge", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.verifyAuth, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // InitiateRecovery method + desc = NewDescriptor("initiateRecovery", smartcontract.ByteArrayType, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("newOwner", smartcontract.Hash160Type), + manifest.NewParameter("evidence", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.initiateRecovery, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // ApproveRecovery method + desc = NewDescriptor("approveRecovery", smartcontract.BoolType, + manifest.NewParameter("requestId", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.approveRecovery, 1<<16, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // ExecuteRecovery method + desc = NewDescriptor("executeRecovery", smartcontract.BoolType, + manifest.NewParameter("requestId", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.executeRecovery, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // CancelRecovery method + desc = NewDescriptor("cancelRecovery", smartcontract.BoolType, + manifest.NewParameter("requestId", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.cancelRecovery, 1<<16, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // GetRecoveryRequest method + desc = NewDescriptor("getRecoveryRequest", smartcontract.ArrayType, + manifest.NewParameter("requestId", smartcontract.ByteArrayType)) + md = NewMethodAndPrice(v.getRecoveryRequest, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // ValidateCaller method + desc = NewDescriptor("validateCaller", smartcontract.ArrayType) + md = NewMethodAndPrice(v.validateCaller, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // RequireRole method + desc = NewDescriptor("requireRole", smartcontract.IntegerType, + manifest.NewParameter("roleId", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.requireRole, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // RequireCoreRole method + desc = NewDescriptor("requireCoreRole", smartcontract.IntegerType, + manifest.NewParameter("coreRole", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.requireCoreRole, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // RequirePermission method + desc = NewDescriptor("requirePermission", smartcontract.IntegerType, + manifest.NewParameter("resource", smartcontract.StringType), + manifest.NewParameter("action", smartcontract.StringType), + manifest.NewParameter("scope", smartcontract.StringType)) + md = NewMethodAndPrice(v.requirePermission, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // SetVesting method (committee only) + desc = NewDescriptor("setVesting", smartcontract.BoolType, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("vestedUntil", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.setVesting, 1<<16, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // IsFullyVested method + desc = NewDescriptor("isFullyVested", smartcontract.BoolType, + manifest.NewParameter("tokenId", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.isFullyVested, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // GetVestingInfo method + desc = NewDescriptor("getVestingInfo", smartcontract.ArrayType, + manifest.NewParameter("tokenId", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.getVestingInfo, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // Events + eDesc := NewEventDescriptor(VitaCreatedEvent, + manifest.NewParameter("tokenId", smartcontract.ByteArrayType), + manifest.NewParameter("owner", smartcontract.Hash160Type), + manifest.NewParameter("createdAt", smartcontract.IntegerType)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VitaSuspendedEvent, + manifest.NewParameter("tokenId", smartcontract.ByteArrayType), + manifest.NewParameter("reason", smartcontract.StringType), + manifest.NewParameter("suspendedBy", smartcontract.Hash160Type)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VitaReinstatedEvent, + manifest.NewParameter("tokenId", smartcontract.ByteArrayType), + manifest.NewParameter("reinstatedBy", smartcontract.Hash160Type)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VitaRevokedEvent, + manifest.NewParameter("tokenId", smartcontract.ByteArrayType), + manifest.NewParameter("reason", smartcontract.StringType), + manifest.NewParameter("revokedBy", smartcontract.Hash160Type)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AttributeSetEvent, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("key", smartcontract.StringType), + manifest.NewParameter("attestor", smartcontract.Hash160Type), + manifest.NewParameter("expiresAt", smartcontract.IntegerType)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AttributeRevokedEvent, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("key", smartcontract.StringType), + manifest.NewParameter("revokedBy", smartcontract.Hash160Type), + manifest.NewParameter("reason", smartcontract.StringType)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AuthChallengeCreatedEvent, + manifest.NewParameter("challengeId", smartcontract.ByteArrayType), + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("purpose", smartcontract.StringType), + manifest.NewParameter("expiresAt", smartcontract.IntegerType)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(AuthenticationSuccessEvent, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("purpose", smartcontract.StringType), + manifest.NewParameter("timestamp", smartcontract.IntegerType)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(RecoveryInitiatedEvent, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("requestId", smartcontract.ByteArrayType), + manifest.NewParameter("delayUntil", smartcontract.IntegerType)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(RecoveryApprovalEvent, + manifest.NewParameter("requestId", smartcontract.ByteArrayType), + manifest.NewParameter("approver", smartcontract.Hash160Type), + manifest.NewParameter("approvalCount", smartcontract.IntegerType), + manifest.NewParameter("required", smartcontract.IntegerType)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(RecoveryExecutedEvent, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("oldOwner", smartcontract.Hash160Type), + manifest.NewParameter("newOwner", smartcontract.Hash160Type)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(RecoveryCancelledEvent, + manifest.NewParameter("requestId", smartcontract.ByteArrayType), + manifest.NewParameter("cancelledBy", smartcontract.Hash160Type)) + v.AddEvent(NewEvent(eDesc)) + + eDesc = NewEventDescriptor(VestingUpdatedEvent, + manifest.NewParameter("tokenId", smartcontract.IntegerType), + manifest.NewParameter("vestedUntil", smartcontract.IntegerType), + manifest.NewParameter("updatedBy", smartcontract.Hash160Type)) + v.AddEvent(NewEvent(eDesc)) + + return v +} + +// Metadata returns contract metadata. +func (v *Vita) Metadata() *interop.ContractMD { + return &v.ContractMD +} + +// Initialize initializes Vita contract at the specified hardfork. +func (v *Vita) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != v.ActiveIn() { + return nil + } + + cache := &VitaCache{ + tokenCount: 0, + } + ic.DAO.SetCache(v.ID, cache) + + // Initialize token counter to 0 + v.setTokenCounter(ic.DAO, 0) + + return nil +} + +// InitializeCache fills native Vita cache from DAO on node restart. +func (v *Vita) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cache := &VitaCache{} + + // Load token counter from storage + counter := v.getTokenCounter(d) + cache.tokenCount = counter + + d.SetCache(v.ID, cache) + return nil +} + +// OnPersist implements the Contract interface. +func (v *Vita) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (v *Vita) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn returns the hardfork this contract activates in. +// Vita is always active (returns nil). +func (v *Vita) ActiveIn() *config.Hardfork { + return nil +} + +// Storage key helpers + +func makeTokenByOwnerKey(owner util.Uint160) []byte { + key := make([]byte, 1+util.Uint160Size) + key[0] = prefixTokenByOwner + copy(key[1:], owner.BytesBE()) + return key +} + +func makeTokenByIDKey(tokenID uint64) []byte { + key := make([]byte, 9) + key[0] = prefixTokenByID + binary.BigEndian.PutUint64(key[1:], tokenID) + return key +} + +func makePersonHashKey(personHash []byte) []byte { + key := make([]byte, 1+len(personHash)) + key[0] = prefixPersonHash + copy(key[1:], personHash) + return key +} + +func makeAttributeKey(tokenID uint64, attrKey string) []byte { + keyBytes := []byte(attrKey) + key := make([]byte, 9+len(keyBytes)) + key[0] = prefixAttribute + binary.BigEndian.PutUint64(key[1:9], tokenID) + copy(key[9:], keyBytes) + return key +} + +func makeChallengeKey(challengeID util.Uint256) []byte { + key := make([]byte, 1+util.Uint256Size) + key[0] = prefixChallenge + copy(key[1:], challengeID.BytesBE()) + return key +} + +func makeLastAuthKey(tokenID uint64, purpose string) []byte { + purposeBytes := []byte(purpose) + key := make([]byte, 9+len(purposeBytes)) + key[0] = prefixConfig // Using config prefix with tokenID+purpose for last auth tracking + binary.BigEndian.PutUint64(key[1:9], tokenID) + copy(key[9:], purposeBytes) + return key +} + +func makeRecoveryKey(requestID util.Uint256) []byte { + key := make([]byte, 1+util.Uint256Size) + key[0] = prefixRecovery + copy(key[1:], requestID.BytesBE()) + return key +} + +func makeActiveRecoveryKey(tokenID uint64) []byte { + key := make([]byte, 9) + key[0] = prefixActiveRecovery + binary.BigEndian.PutUint64(key[1:], tokenID) + return key +} + +// Token counter methods + +func (v *Vita) getTokenCounter(d *dao.Simple) uint64 { + si := d.GetStorageItem(v.ID, []byte{prefixTokenCounter}) + if si == nil { + return 0 + } + return binary.BigEndian.Uint64(si) +} + +func (v *Vita) setTokenCounter(d *dao.Simple, count uint64) { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, count) + d.PutStorageItem(v.ID, []byte{prefixTokenCounter}, buf) +} + +func (v *Vita) getAndIncrementTokenCounter(d *dao.Simple) uint64 { + cache := d.GetRWCache(v.ID).(*VitaCache) + tokenID := cache.tokenCount + cache.tokenCount++ + v.setTokenCounter(d, cache.tokenCount) + return tokenID +} + +// Token storage methods + +func (v *Vita) putToken(d *dao.Simple, token *state.Vita) error { + item, err := token.ToStackItem() + if err != nil { + return err + } + data, err := stackitem.Serialize(item) + if err != nil { + return err + } + // Store by ID + d.PutStorageItem(v.ID, makeTokenByIDKey(token.TokenID), data) + // Store owner -> tokenID mapping + tokenIDBytes := make([]byte, 8) + binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) + d.PutStorageItem(v.ID, makeTokenByOwnerKey(token.Owner), tokenIDBytes) + // Store personHash -> tokenID mapping for uniqueness + if len(token.PersonHash) > 0 { + d.PutStorageItem(v.ID, makePersonHashKey(token.PersonHash), tokenIDBytes) + } + return nil +} + +func (v *Vita) getTokenByOwnerInternal(d *dao.Simple, owner util.Uint160) (*state.Vita, error) { + si := d.GetStorageItem(v.ID, makeTokenByOwnerKey(owner)) + if si == nil { + return nil, nil + } + tokenID := binary.BigEndian.Uint64(si) + return v.getTokenByIDInternal(d, tokenID) +} + +func (v *Vita) getTokenByIDInternal(d *dao.Simple, tokenID uint64) (*state.Vita, error) { + si := d.GetStorageItem(v.ID, makeTokenByIDKey(tokenID)) + if si == nil { + return nil, nil + } + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + token := new(state.Vita) + if err := token.FromStackItem(item); err != nil { + return nil, err + } + return token, nil +} + +func (v *Vita) tokenExistsForOwner(d *dao.Simple, owner util.Uint160) bool { + si := d.GetStorageItem(v.ID, makeTokenByOwnerKey(owner)) + return si != nil +} + +func (v *Vita) personHashExists(d *dao.Simple, personHash []byte) bool { + if len(personHash) == 0 { + return false + } + si := d.GetStorageItem(v.ID, makePersonHashKey(personHash)) + return si != nil +} + +// Contract methods + +// register creates a new Vita. +func (v *Vita) register(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + personHash := toBytes(args[1]) + isEntity, err := args[2].TryBool() + if err != nil { + panic(fmt.Errorf("invalid isEntity: %w", err)) + } + recoveryHash := toBytes(args[3]) + birthTimestamp := toUint64(args[4]) + + // Validate owner + if owner.Equals(util.Uint160{}) { + panic(ErrInvalidOwner) + } + + // Check witness for the owner + ok, err := runtime.CheckHashedWitness(ic, owner) + if err != nil || !ok { + panic(ErrVitaInvalidWitness) + } + + // Check if token already exists for this owner + if v.tokenExistsForOwner(ic.DAO, owner) { + panic(ErrTokenAlreadyExists) + } + + // Check if person hash is already linked to another token + if v.personHashExists(ic.DAO, personHash) { + panic(ErrPersonHashExists) + } + + // Get next token ID + tokenID := v.getAndIncrementTokenCounter(ic.DAO) + + // Create token with vesting period for Sybil resistance + // New tokens must vest before having full rights + vestedUntil := ic.Block.Index + defaultVestingPeriod + + token := &state.Vita{ + TokenID: tokenID, + Owner: owner, + PersonHash: personHash, + IsEntity: isEntity, + CreatedAt: ic.Block.Index, + UpdatedAt: ic.Block.Index, + Status: state.TokenStatusActive, + StatusReason: "", + RecoveryHash: recoveryHash, + VestedUntil: vestedUntil, + } + + // Store token + if err := v.putToken(ic.DAO, token); err != nil { + panic(err) + } + + // Register birth in Annos contract for lifespan tracking + // birthTimestamp is the actual birth date (Unix timestamp in seconds) + // This allows existing adults to register with their real birth date + // For newborns being registered at birth, use current block timestamp + if v.Annos != nil { + if err := v.Annos.RegisterBirthInternal(ic.DAO, ic, tokenID, owner, birthTimestamp); err != nil { + panic(fmt.Errorf("failed to register birth in Annos: %w", err)) + } + } + + // Generate token ID bytes for return and event + tokenIDBytes := hash.Sha256(append(owner.BytesBE(), personHash...)).BytesBE() + + // Emit event + err = ic.AddNotification(v.Hash, VitaCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(tokenIDBytes), + stackitem.NewByteArray(owner.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewByteArray(tokenIDBytes) +} + +// getToken returns the token for the given owner. +func (v *Vita) getToken(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + token, err := v.getTokenByOwnerInternal(ic.DAO, owner) + if err != nil { + panic(err) + } + if token == nil { + return stackitem.Null{} + } + + item, err := token.ToStackItem() + if err != nil { + panic(err) + } + return item +} + +// getTokenByID returns the token for the given token ID. +func (v *Vita) getTokenByID(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + + token, err := v.getTokenByIDInternal(ic.DAO, tokenID) + if err != nil { + panic(err) + } + if token == nil { + return stackitem.Null{} + } + + item, err := token.ToStackItem() + if err != nil { + panic(err) + } + return item +} + +// exists checks if a token exists for the given owner. +func (v *Vita) exists(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + return stackitem.NewBool(v.tokenExistsForOwner(ic.DAO, owner)) +} + +// totalSupply returns the total number of tokens. +func (v *Vita) totalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + cache := ic.DAO.GetROCache(v.ID).(*VitaCache) + return stackitem.NewBigInteger(big.NewInt(int64(cache.tokenCount))) +} + +// suspend temporarily suspends a token (committee only). +func (v *Vita) suspend(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + reason := toString(args[1]) + + // Check committee + if !v.checkCommittee(ic) { + panic(ErrNotCommittee) + } + + // Require liberty restriction order from Lex (due process protection) + // Suspending a Vita is a restriction of liberty, which requires judicial authority + if v.Lex != nil && !v.Lex.IsRestrictedInternal(ic.DAO, owner, state.RightLiberty, ic.Block.Index) { + panic("liberty restriction order required via Lex (due process)") + } + + // Get token + token, err := v.getTokenByOwnerInternal(ic.DAO, owner) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Check if already revoked + if token.Status == state.TokenStatusRevoked { + panic(ErrTokenRevoked) + } + + // Update status + token.Status = state.TokenStatusSuspended + token.StatusReason = reason + token.UpdatedAt = ic.Block.Index + + // Store updated token + if err := v.putToken(ic.DAO, token); err != nil { + panic(err) + } + + // Get caller for event + caller := ic.VM.GetCallingScriptHash() + + // Generate token ID bytes + tokenIDBytes := make([]byte, 8) + binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) + + // Emit event + err = ic.AddNotification(v.Hash, VitaSuspendedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(tokenIDBytes), + stackitem.NewByteArray([]byte(reason)), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// reinstate reinstates a suspended token (committee only). +func (v *Vita) reinstate(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + + // Check committee + if !v.checkCommittee(ic) { + panic(ErrNotCommittee) + } + + // Get token + token, err := v.getTokenByOwnerInternal(ic.DAO, owner) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Check if suspended + if token.Status != state.TokenStatusSuspended { + panic(ErrTokenNotSuspended) + } + + // Update status + token.Status = state.TokenStatusActive + token.StatusReason = "" + token.UpdatedAt = ic.Block.Index + + // Store updated token + if err := v.putToken(ic.DAO, token); err != nil { + panic(err) + } + + // Get caller for event + caller := ic.VM.GetCallingScriptHash() + + // Generate token ID bytes + tokenIDBytes := make([]byte, 8) + binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) + + // Emit event + err = ic.AddNotification(v.Hash, VitaReinstatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(tokenIDBytes), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// revoke permanently revokes a token (committee only). +func (v *Vita) revoke(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + reason := toString(args[1]) + + // Check committee + if !v.checkCommittee(ic) { + panic(ErrNotCommittee) + } + + // Require liberty restriction order from Lex (due process protection) + // Revoking a Vita is a permanent restriction of liberty, requiring judicial authority + if v.Lex != nil && !v.Lex.IsRestrictedInternal(ic.DAO, owner, state.RightLiberty, ic.Block.Index) { + panic("liberty restriction order required via Lex (due process)") + } + + // Get token + token, err := v.getTokenByOwnerInternal(ic.DAO, owner) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Check if already revoked + if token.Status == state.TokenStatusRevoked { + panic(ErrTokenRevoked) + } + + // Update status + token.Status = state.TokenStatusRevoked + token.StatusReason = reason + token.UpdatedAt = ic.Block.Index + + // Store updated token + if err := v.putToken(ic.DAO, token); err != nil { + panic(err) + } + + // Get caller for event + caller := ic.VM.GetCallingScriptHash() + + // Generate token ID bytes + tokenIDBytes := make([]byte, 8) + binary.BigEndian.PutUint64(tokenIDBytes, token.TokenID) + + // Emit event + err = ic.AddNotification(v.Hash, VitaRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(tokenIDBytes), + stackitem.NewByteArray([]byte(reason)), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// Public methods for cross-native access + +// GetTokenByOwner returns the token for the given owner (for cross-native access). +func (v *Vita) GetTokenByOwner(d *dao.Simple, owner util.Uint160) (*state.Vita, error) { + return v.getTokenByOwnerInternal(d, owner) +} + +// GetTokenByIDPublic returns the token for the given ID (for cross-native access). +func (v *Vita) GetTokenByIDPublic(d *dao.Simple, tokenID uint64) (*state.Vita, error) { + return v.getTokenByIDInternal(d, tokenID) +} + +// TokenExists returns true if a token exists for the given owner. +func (v *Vita) TokenExists(d *dao.Simple, owner util.Uint160) bool { + return v.tokenExistsForOwner(d, owner) +} + +// GetTotalTokenCount returns the total number of tokens issued (for quorum calculations). +func (v *Vita) GetTotalTokenCount(d *dao.Simple) uint64 { + return v.getTokenCounter(d) +} + +// ExistsInternal checks if a Vita token with the given ID exists. +func (v *Vita) ExistsInternal(d *dao.Simple, vitaID uint64) bool { + token, err := v.getTokenByIDInternal(d, vitaID) + return err == nil && token != nil +} + +// OwnerOfInternal returns the owner of a Vita token by ID. +// Returns empty Uint160 if token doesn't exist. +func (v *Vita) OwnerOfInternal(d *dao.Simple, vitaID uint64) util.Uint160 { + token, err := v.getTokenByIDInternal(d, vitaID) + if err != nil || token == nil { + return util.Uint160{} + } + return token.Owner +} + +// IsAdultVerified checks if the owner has a verified "age_verified" attribute +// indicating they are 18+ years old. Used for age-restricted purchases. +// The attribute must be non-revoked and not expired. +func (v *Vita) IsAdultVerified(d *dao.Simple, owner util.Uint160) bool { + token, err := v.getTokenByOwnerInternal(d, owner) + if err != nil || token == nil { + return false + } + + // Check for "age_verified" attribute + attr, err := v.getAttributeInternal(d, token.TokenID, "age_verified") + if err != nil || attr == nil { + return false + } + + // Check attribute is not revoked + if attr.Revoked { + return false + } + + // Note: Expiration check would require current block height + // For now, we check if ExpiresAt is set and > 0 means it could expire + // In production, pass block height and compare + return true +} + +// Attribute storage methods + +func (v *Vita) putAttribute(d *dao.Simple, tokenID uint64, attr *state.Attribute) error { + item, err := attr.ToStackItem() + if err != nil { + return err + } + data, err := stackitem.Serialize(item) + if err != nil { + return err + } + d.PutStorageItem(v.ID, makeAttributeKey(tokenID, attr.Key), data) + return nil +} + +func (v *Vita) getAttributeInternal(d *dao.Simple, tokenID uint64, key string) (*state.Attribute, error) { + si := d.GetStorageItem(v.ID, makeAttributeKey(tokenID, key)) + if si == nil { + return nil, nil + } + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + attr := new(state.Attribute) + if err := attr.FromStackItem(item); err != nil { + return nil, err + } + return attr, nil +} + +// setAttribute sets or updates an attribute on a token. +// The caller must be either the token owner (self-attestation) or any other account (third-party attestation). +func (v *Vita) setAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + key := toString(args[1]) + valueHash := toBytes(args[2]) + valueEnc := toBytes(args[3]) + expiresAt := uint32(toBigInt(args[4]).Int64()) + disclosureLevel := state.DisclosureLevel(toBigInt(args[5]).Int64()) + + // Validate inputs + if len(key) == 0 || len(key) > 64 { + panic(ErrInvalidAttributeKey) + } + if len(valueHash) == 0 || len(valueHash) > 64 { + panic(ErrInvalidValueHash) + } + if disclosureLevel > state.DisclosurePublic { + panic(ErrInvalidDisclosureLevel) + } + + // Get token + token, err := v.getTokenByIDInternal(ic.DAO, tokenID) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Check token is active + if token.Status != state.TokenStatusActive { + panic(ErrTokenNotActive) + } + + // Get the attestor (caller) + attestor := ic.VM.GetCallingScriptHash() + + // If attestor is the owner, check witness + // Otherwise, any contract/account can attest (third-party attestation) + if attestor.Equals(token.Owner) { + ok, err := runtime.CheckHashedWitness(ic, token.Owner) + if err != nil || !ok { + panic(ErrVitaInvalidWitness) + } + } + + // Create attribute + attr := &state.Attribute{ + Key: key, + ValueHash: valueHash, + ValueEnc: valueEnc, + Attestor: attestor, + AttestedAt: ic.Block.Index, + ExpiresAt: expiresAt, + Revoked: false, + RevokedAt: 0, + DisclosureLevel: disclosureLevel, + } + + // Store attribute + if err := v.putAttribute(ic.DAO, tokenID, attr); err != nil { + panic(err) + } + + // Emit event + err = ic.AddNotification(v.Hash, AttributeSetEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(tokenID))), + stackitem.NewByteArray([]byte(key)), + stackitem.NewByteArray(attestor.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(expiresAt))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// getAttribute returns an attribute for a token. +func (v *Vita) getAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + key := toString(args[1]) + + attr, err := v.getAttributeInternal(ic.DAO, tokenID, key) + if err != nil { + panic(err) + } + if attr == nil { + return stackitem.Null{} + } + + item, err := attr.ToStackItem() + if err != nil { + panic(err) + } + return item +} + +// revokeAttribute revokes an attribute. +// Can be called by the token owner, the original attestor, or committee. +func (v *Vita) revokeAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + key := toString(args[1]) + reason := toString(args[2]) + + // Get token + token, err := v.getTokenByIDInternal(ic.DAO, tokenID) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Get attribute + attr, err := v.getAttributeInternal(ic.DAO, tokenID, key) + if err != nil { + panic(err) + } + if attr == nil { + panic(ErrAttributeNotFound) + } + + // Check if already revoked + if attr.Revoked { + panic(ErrAttributeRevoked) + } + + // Check authorization: owner, attestor, or committee + caller := ic.VM.GetCallingScriptHash() + isOwner := caller.Equals(token.Owner) + isAttestor := caller.Equals(attr.Attestor) + isCommittee := v.checkCommittee(ic) + + if isOwner { + ok, err := runtime.CheckHashedWitness(ic, token.Owner) + if err != nil || !ok { + panic(ErrVitaInvalidWitness) + } + } else if !isAttestor && !isCommittee { + panic(ErrVitaInvalidWitness) + } + + // Revoke attribute + attr.Revoked = true + attr.RevokedAt = ic.Block.Index + + // Store updated attribute + if err := v.putAttribute(ic.DAO, tokenID, attr); err != nil { + panic(err) + } + + // Emit event + err = ic.AddNotification(v.Hash, AttributeRevokedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(tokenID))), + stackitem.NewByteArray([]byte(key)), + stackitem.NewByteArray(caller.BytesBE()), + stackitem.NewByteArray([]byte(reason)), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// verifyAttribute verifies that an attribute exists and its hash matches. +// Returns true if the attribute exists, is not revoked, not expired, and hash matches. +func (v *Vita) verifyAttribute(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + key := toString(args[1]) + expectedHash := toBytes(args[2]) + + // Get attribute + attr, err := v.getAttributeInternal(ic.DAO, tokenID, key) + if err != nil { + panic(err) + } + if attr == nil { + return stackitem.NewBool(false) + } + + // Check if revoked + if attr.Revoked { + return stackitem.NewBool(false) + } + + // Check if expired (0 means never expires) + if attr.ExpiresAt > 0 && ic.Block.Index > attr.ExpiresAt { + return stackitem.NewBool(false) + } + + // Compare hashes + if len(attr.ValueHash) != len(expectedHash) { + return stackitem.NewBool(false) + } + for i := range attr.ValueHash { + if attr.ValueHash[i] != expectedHash[i] { + return stackitem.NewBool(false) + } + } + + return stackitem.NewBool(true) +} + +// Public methods for cross-native attribute access + +// GetAttribute returns an attribute for the given token and key (for cross-native access). +func (v *Vita) GetAttribute(d *dao.Simple, tokenID uint64, key string) (*state.Attribute, error) { + return v.getAttributeInternal(d, tokenID, key) +} + +// Challenge storage methods + +func (v *Vita) putChallenge(d *dao.Simple, challenge *state.AuthChallenge) error { + item, err := challenge.ToStackItem() + if err != nil { + return err + } + data, err := stackitem.Serialize(item) + if err != nil { + return err + } + d.PutStorageItem(v.ID, makeChallengeKey(challenge.ChallengeID), data) + return nil +} + +func (v *Vita) getChallengeInternal(d *dao.Simple, challengeID util.Uint256) (*state.AuthChallenge, error) { + si := d.GetStorageItem(v.ID, makeChallengeKey(challengeID)) + if si == nil { + return nil, nil + } + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + challenge := new(state.AuthChallenge) + if err := challenge.FromStackItem(item); err != nil { + return nil, err + } + return challenge, nil +} + +func (v *Vita) setLastAuth(d *dao.Simple, tokenID uint64, purpose string, blockHeight uint32) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, blockHeight) + d.PutStorageItem(v.ID, makeLastAuthKey(tokenID, purpose), buf) +} + +func (v *Vita) getLastAuth(d *dao.Simple, tokenID uint64, purpose string) uint32 { + si := d.GetStorageItem(v.ID, makeLastAuthKey(tokenID, purpose)) + if si == nil { + return 0 + } + return binary.BigEndian.Uint32(si) +} + +// Default challenge expiry in blocks (approximately 5 minutes at 15 seconds per block) +const defaultChallengeExpiry uint32 = 20 + +// createChallenge creates a new authentication challenge for a token owner. +// Anyone can create a challenge for any token owner. +func (v *Vita) createChallenge(ic *interop.Context, args []stackitem.Item) stackitem.Item { + owner := toUint160(args[0]) + purpose := toString(args[1]) + + // Validate purpose + if len(purpose) == 0 || len(purpose) > 32 { + panic(ErrInvalidPurpose) + } + + // Get token + token, err := v.getTokenByOwnerInternal(ic.DAO, owner) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Check token is active + if token.Status != state.TokenStatusActive { + panic(ErrTokenNotActive) + } + + // Generate nonce (use transaction hash + block index + token ID for uniqueness) + nonceData := append(ic.Tx.Hash().BytesBE(), make([]byte, 8)...) + binary.BigEndian.PutUint64(nonceData[util.Uint256Size:], token.TokenID) + nonce := hash.Sha256(nonceData).BytesBE() + + // Generate challenge ID + challengeIDData := append(nonce, owner.BytesBE()...) + challengeIDData = append(challengeIDData, []byte(purpose)...) + challengeID := hash.Sha256(challengeIDData) + + // Create challenge + challenge := &state.AuthChallenge{ + ChallengeID: challengeID, + TokenID: token.TokenID, + Nonce: nonce, + CreatedAt: ic.Block.Index, + ExpiresAt: ic.Block.Index + defaultChallengeExpiry, + Purpose: purpose, + Fulfilled: false, + FulfilledAt: 0, + } + + // Store challenge + if err := v.putChallenge(ic.DAO, challenge); err != nil { + panic(err) + } + + // Emit event + err = ic.AddNotification(v.Hash, AuthChallengeCreatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(challengeID.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))), + stackitem.NewByteArray([]byte(purpose)), + stackitem.NewBigInteger(big.NewInt(int64(challenge.ExpiresAt))), + })) + if err != nil { + panic(err) + } + + // Return challenge as struct + item, err := challenge.ToStackItem() + if err != nil { + panic(err) + } + return item +} + +// getChallenge returns a challenge by ID. +func (v *Vita) getChallenge(ic *interop.Context, args []stackitem.Item) stackitem.Item { + challengeIDBytes := toBytes(args[0]) + if len(challengeIDBytes) != util.Uint256Size { + panic("invalid challenge ID length") + } + challengeID, err := util.Uint256DecodeBytesBE(challengeIDBytes) + if err != nil { + panic(err) + } + + challenge, err := v.getChallengeInternal(ic.DAO, challengeID) + if err != nil { + panic(err) + } + if challenge == nil { + return stackitem.Null{} + } + + item, err := challenge.ToStackItem() + if err != nil { + panic(err) + } + return item +} + +// fulfillChallenge fulfills an authentication challenge by providing a valid signature. +// The signature must be from the token owner over the challenge nonce. +func (v *Vita) fulfillChallenge(ic *interop.Context, args []stackitem.Item) stackitem.Item { + challengeIDBytes := toBytes(args[0]) + if len(challengeIDBytes) != util.Uint256Size { + panic("invalid challenge ID length") + } + challengeID, err := util.Uint256DecodeBytesBE(challengeIDBytes) + if err != nil { + panic(err) + } + _ = toBytes(args[1]) // signature - validation happens via witness + + // Get challenge + challenge, err := v.getChallengeInternal(ic.DAO, challengeID) + if err != nil { + panic(err) + } + if challenge == nil { + panic(ErrChallengeNotFound) + } + + // Check if already fulfilled + if challenge.Fulfilled { + panic(ErrChallengeAlreadyFulfilled) + } + + // Check if expired + if ic.Block.Index > challenge.ExpiresAt { + panic(ErrChallengeExpired) + } + + // Get token + token, err := v.getTokenByIDInternal(ic.DAO, challenge.TokenID) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Verify the caller has witness for the token owner + // This ensures the owner is actually signing this transaction + ok, err := runtime.CheckHashedWitness(ic, token.Owner) + if err != nil || !ok { + panic(ErrInvalidSignature) + } + + // Mark challenge as fulfilled + challenge.Fulfilled = true + challenge.FulfilledAt = ic.Block.Index + + // Store updated challenge + if err := v.putChallenge(ic.DAO, challenge); err != nil { + panic(err) + } + + // Record last auth time for this token and purpose + v.setLastAuth(ic.DAO, challenge.TokenID, challenge.Purpose, ic.Block.Index) + + // Emit success event + err = ic.AddNotification(v.Hash, AuthenticationSuccessEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(challenge.TokenID))), + stackitem.NewByteArray([]byte(challenge.Purpose)), + stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// verifyAuth checks if a token has authenticated recently for a given purpose. +// Returns true if the last successful authentication for the purpose was within maxAge blocks. +func (v *Vita) verifyAuth(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + purpose := toString(args[1]) + maxAge := uint32(toBigInt(args[2]).Int64()) + + // Get last auth time + lastAuth := v.getLastAuth(ic.DAO, tokenID, purpose) + if lastAuth == 0 { + return stackitem.NewBool(false) + } + + // Check if within maxAge + if ic.Block.Index > lastAuth+maxAge { + return stackitem.NewBool(false) + } + + return stackitem.NewBool(true) +} + +// Public methods for cross-native authentication access + +// GetLastAuth returns the last authentication block height for a token and purpose. +func (v *Vita) GetLastAuth(d *dao.Simple, tokenID uint64, purpose string) uint32 { + return v.getLastAuth(d, tokenID, purpose) +} + +// Recovery configuration constants +const ( + // defaultRecoveryDelay is the number of blocks before recovery can be executed (approximately 24 hours at 15s blocks) + defaultRecoveryDelay uint32 = 5760 + // defaultRecoveryExpiry is the number of blocks until a recovery request expires (approximately 7 days) + defaultRecoveryExpiry uint32 = 40320 + // defaultRequiredApprovals is the number of committee approvals needed for recovery + defaultRequiredApprovals int = 2 +) + +// Recovery storage methods + +func (v *Vita) putRecoveryRequest(d *dao.Simple, req *state.RecoveryRequest) error { + item, err := req.ToStackItem() + if err != nil { + return err + } + data, err := stackitem.Serialize(item) + if err != nil { + return err + } + d.PutStorageItem(v.ID, makeRecoveryKey(req.RequestID), data) + return nil +} + +func (v *Vita) getRecoveryRequestInternal(d *dao.Simple, requestID util.Uint256) (*state.RecoveryRequest, error) { + si := d.GetStorageItem(v.ID, makeRecoveryKey(requestID)) + if si == nil { + return nil, nil + } + item, err := stackitem.Deserialize(si) + if err != nil { + return nil, err + } + req := new(state.RecoveryRequest) + if err := req.FromStackItem(item); err != nil { + return nil, err + } + return req, nil +} + +func (v *Vita) setActiveRecovery(d *dao.Simple, tokenID uint64, requestID util.Uint256) { + d.PutStorageItem(v.ID, makeActiveRecoveryKey(tokenID), requestID.BytesBE()) +} + +func (v *Vita) getActiveRecovery(d *dao.Simple, tokenID uint64) *util.Uint256 { + si := d.GetStorageItem(v.ID, makeActiveRecoveryKey(tokenID)) + if si == nil { + return nil + } + requestID, err := util.Uint256DecodeBytesBE(si) + if err != nil { + return nil + } + return &requestID +} + +func (v *Vita) clearActiveRecovery(d *dao.Simple, tokenID uint64) { + d.DeleteStorageItem(v.ID, makeActiveRecoveryKey(tokenID)) +} + +// initiateRecovery starts a key recovery process for a token. +// Anyone can initiate recovery for any token. +func (v *Vita) initiateRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + newOwner := toUint160(args[1]) + evidence := toBytes(args[2]) + + // Validate new owner + if newOwner.Equals(util.Uint160{}) { + panic(ErrInvalidNewOwner) + } + + // Get token + token, err := v.getTokenByIDInternal(ic.DAO, tokenID) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Check token is not revoked + if token.Status == state.TokenStatusRevoked { + panic(ErrTokenRevoked) + } + + // Check token is not already in recovery + if token.Status == state.TokenStatusRecovering { + panic(ErrTokenInRecovery) + } + + // Check no active recovery exists + if v.getActiveRecovery(ic.DAO, tokenID) != nil { + panic(ErrRecoveryAlreadyActive) + } + + // Get requester (caller) + requester := ic.VM.GetCallingScriptHash() + + // Generate request ID + requestIDData := append(ic.Tx.Hash().BytesBE(), make([]byte, 8)...) + binary.BigEndian.PutUint64(requestIDData[util.Uint256Size:], tokenID) + requestIDData = append(requestIDData, newOwner.BytesBE()...) + requestID := hash.Sha256(requestIDData) + + // Create recovery request + req := &state.RecoveryRequest{ + RequestID: requestID, + TokenID: tokenID, + NewOwner: newOwner, + Requester: requester, + Evidence: evidence, + Approvals: []util.Uint160{}, + RequiredApprovals: defaultRequiredApprovals, + CreatedAt: ic.Block.Index, + DelayUntil: ic.Block.Index + defaultRecoveryDelay, + ExpiresAt: ic.Block.Index + defaultRecoveryExpiry, + Status: state.RecoveryStatusPending, + } + + // Store recovery request + if err := v.putRecoveryRequest(ic.DAO, req); err != nil { + panic(err) + } + + // Mark active recovery for token + v.setActiveRecovery(ic.DAO, tokenID, requestID) + + // Update token status to recovering + token.Status = state.TokenStatusRecovering + token.StatusReason = "recovery initiated" + token.UpdatedAt = ic.Block.Index + if err := v.putToken(ic.DAO, token); err != nil { + panic(err) + } + + // Emit event + err = ic.AddNotification(v.Hash, RecoveryInitiatedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(tokenID))), + stackitem.NewByteArray(requestID.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(req.DelayUntil))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewByteArray(requestID.BytesBE()) +} + +// approveRecovery approves a recovery request (committee member only). +func (v *Vita) approveRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestIDBytes := toBytes(args[0]) + if len(requestIDBytes) != util.Uint256Size { + panic("invalid request ID length") + } + requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) + if err != nil { + panic(err) + } + + // Check committee + if !v.checkCommittee(ic) { + panic(ErrNotCommittee) + } + + // Get recovery request + req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) + if err != nil { + panic(err) + } + if req == nil { + panic(ErrRecoveryNotFound) + } + + // Check status is pending + if req.Status != state.RecoveryStatusPending { + panic(ErrRecoveryNotPending) + } + + // Check not expired + if ic.Block.Index > req.ExpiresAt { + panic(ErrRecoveryExpired) + } + + // Get approver (caller) + approver := ic.VM.GetCallingScriptHash() + + // Check not already approved by this approver + for _, a := range req.Approvals { + if a.Equals(approver) { + panic(ErrAlreadyApproved) + } + } + + // Add approval + req.Approvals = append(req.Approvals, approver) + + // Check if sufficient approvals + if len(req.Approvals) >= req.RequiredApprovals { + req.Status = state.RecoveryStatusApproved + } + + // Store updated request + if err := v.putRecoveryRequest(ic.DAO, req); err != nil { + panic(err) + } + + // Emit event + err = ic.AddNotification(v.Hash, RecoveryApprovalEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(requestID.BytesBE()), + stackitem.NewByteArray(approver.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(len(req.Approvals)))), + stackitem.NewBigInteger(big.NewInt(int64(req.RequiredApprovals))), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// executeRecovery executes an approved recovery request after the delay period. +func (v *Vita) executeRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestIDBytes := toBytes(args[0]) + if len(requestIDBytes) != util.Uint256Size { + panic("invalid request ID length") + } + requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) + if err != nil { + panic(err) + } + + // Get recovery request + req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) + if err != nil { + panic(err) + } + if req == nil { + panic(ErrRecoveryNotFound) + } + + // Check status is approved + if req.Status != state.RecoveryStatusApproved { + panic(ErrRecoveryNotPending) + } + + // Check delay period has passed + if ic.Block.Index < req.DelayUntil { + panic(ErrRecoveryDelayNotPassed) + } + + // Check not expired + if ic.Block.Index > req.ExpiresAt { + panic(ErrRecoveryExpired) + } + + // Get token + token, err := v.getTokenByIDInternal(ic.DAO, req.TokenID) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Store old owner for event + oldOwner := token.Owner + + // Delete old owner -> tokenID mapping + ic.DAO.DeleteStorageItem(v.ID, makeTokenByOwnerKey(oldOwner)) + + // Update token owner + token.Owner = req.NewOwner + token.Status = state.TokenStatusActive + token.StatusReason = "" + token.UpdatedAt = ic.Block.Index + + // Store updated token (this will create new owner -> tokenID mapping) + if err := v.putToken(ic.DAO, token); err != nil { + panic(err) + } + + // Update request status + req.Status = state.RecoveryStatusExecuted + if err := v.putRecoveryRequest(ic.DAO, req); err != nil { + panic(err) + } + + // Clear active recovery + v.clearActiveRecovery(ic.DAO, req.TokenID) + + // Emit event + err = ic.AddNotification(v.Hash, RecoveryExecutedEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(req.TokenID))), + stackitem.NewByteArray(oldOwner.BytesBE()), + stackitem.NewByteArray(req.NewOwner.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// cancelRecovery cancels an active recovery request. +// Can be called by the original token owner or the requester. +func (v *Vita) cancelRecovery(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestIDBytes := toBytes(args[0]) + if len(requestIDBytes) != util.Uint256Size { + panic("invalid request ID length") + } + requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) + if err != nil { + panic(err) + } + + // Get recovery request + req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) + if err != nil { + panic(err) + } + if req == nil { + panic(ErrRecoveryNotFound) + } + + // Check status is pending or approved (not executed or denied) + if req.Status == state.RecoveryStatusExecuted || req.Status == state.RecoveryStatusDenied { + panic(ErrRecoveryNotPending) + } + + // Get token + token, err := v.getTokenByIDInternal(ic.DAO, req.TokenID) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Check authorization: owner, requester, or committee + caller := ic.VM.GetCallingScriptHash() + isOwner := caller.Equals(token.Owner) + isRequester := caller.Equals(req.Requester) + isCommittee := v.checkCommittee(ic) + + if isOwner { + ok, err := runtime.CheckHashedWitness(ic, token.Owner) + if err != nil || !ok { + panic(ErrVitaInvalidWitness) + } + } else if !isRequester && !isCommittee { + panic(ErrVitaInvalidWitness) + } + + // Update request status + req.Status = state.RecoveryStatusDenied + if err := v.putRecoveryRequest(ic.DAO, req); err != nil { + panic(err) + } + + // Restore token status + token.Status = state.TokenStatusActive + token.StatusReason = "" + token.UpdatedAt = ic.Block.Index + if err := v.putToken(ic.DAO, token); err != nil { + panic(err) + } + + // Clear active recovery + v.clearActiveRecovery(ic.DAO, req.TokenID) + + // Emit event + err = ic.AddNotification(v.Hash, RecoveryCancelledEvent, stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(requestID.BytesBE()), + stackitem.NewByteArray(caller.BytesBE()), + })) + if err != nil { + panic(err) + } + + return stackitem.NewBool(true) +} + +// getRecoveryRequest returns a recovery request by ID. +func (v *Vita) getRecoveryRequest(ic *interop.Context, args []stackitem.Item) stackitem.Item { + requestIDBytes := toBytes(args[0]) + if len(requestIDBytes) != util.Uint256Size { + panic("invalid request ID length") + } + requestID, err := util.Uint256DecodeBytesBE(requestIDBytes) + if err != nil { + panic(err) + } + + req, err := v.getRecoveryRequestInternal(ic.DAO, requestID) + if err != nil { + panic(err) + } + if req == nil { + return stackitem.Null{} + } + + item, err := req.ToStackItem() + if err != nil { + panic(err) + } + return item +} + +// Public methods for cross-native recovery access + +// GetRecoveryRequest returns a recovery request by ID (for cross-native access). +func (v *Vita) GetRecoveryRequest(d *dao.Simple, requestID util.Uint256) (*state.RecoveryRequest, error) { + return v.getRecoveryRequestInternal(d, requestID) +} + +// GetActiveRecoveryForToken returns the active recovery request ID for a token (for cross-native access). +func (v *Vita) GetActiveRecoveryForToken(d *dao.Simple, tokenID uint64) *util.Uint256 { + return v.getActiveRecovery(d, tokenID) +} + +// Cross-contract integration methods + +// validateCaller validates that the calling contract's owner has a valid Vita. +// Returns the tokenID and core roles for the caller. +func (v *Vita) validateCaller(ic *interop.Context, args []stackitem.Item) stackitem.Item { + caller := ic.VM.GetCallingScriptHash() + + // Get token by caller address + token, err := v.getTokenByOwnerInternal(ic.DAO, caller) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrCallerHasNoVita) + } + + // Check token is active + if token.Status != state.TokenStatusActive { + if token.Status == state.TokenStatusSuspended { + panic(ErrTokenSuspended) + } + if token.Status == state.TokenStatusRevoked { + panic(ErrTokenRevoked) + } + if token.Status == state.TokenStatusRecovering { + panic(ErrTokenInRecovery) + } + panic(ErrTokenNotActive) + } + + // Determine core roles + roles := v.getCoreRoles(ic, token) + + // Return struct with tokenID and roles + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))), + stackitem.NewBigInteger(big.NewInt(int64(roles))), + }) +} + +// getCoreRoles determines the core roles for a token holder. +func (v *Vita) getCoreRoles(ic *interop.Context, token *state.Vita) uint64 { + var roles uint64 + + // All active token holders have User role + roles |= 1 << uint64(CoreRoleUser) + + // Check if user is verified (has non-expired, non-revoked attributes) + if v.hasVerifiedAttributes(ic.DAO, token.TokenID) { + roles |= 1 << uint64(CoreRoleVerified) + } + + // Check if user is a committee member + if v.checkCommittee(ic) { + roles |= 1 << uint64(CoreRoleCommittee) + roles |= 1 << uint64(CoreRoleAttestor) // Committee members can attest + roles |= 1 << uint64(CoreRoleRecovery) // Committee members participate in recovery + } + + return roles +} + +// hasVerifiedAttributes checks if a token has any valid (non-expired, non-revoked) attributes. +func (v *Vita) hasVerifiedAttributes(d *dao.Simple, tokenID uint64) bool { + // For now, just check if any attribute exists + // In a full implementation, this would scan attributes and check expiry/revocation + prefix := make([]byte, 9) + prefix[0] = prefixAttribute + binary.BigEndian.PutUint64(prefix[1:], tokenID) + + // Check if any attribute storage items exist for this token + var found bool + d.Seek(v.ID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { + found = true + return false // Stop after finding first + }) + return found +} + +// requireRole checks if the caller has a specific role ID (for RoleRegistry integration). +// This is a stub that will be extended when RoleRegistry is implemented. +func (v *Vita) requireRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { + roleID := toBigInt(args[0]).Uint64() + + caller := ic.VM.GetCallingScriptHash() + + // Get token by caller address + token, err := v.getTokenByOwnerInternal(ic.DAO, caller) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrCallerHasNoVita) + } + + // Check token is active + if token.Status != state.TokenStatusActive { + panic(ErrTokenNotActive) + } + + // Stub: In the future, this will check against RoleRegistry + // For now, just validate the token exists and is active + // Role 0 means "any authenticated user" + if roleID == 0 { + return stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))) + } + + // For non-zero roles, check if it maps to a core role + if roleID <= uint64(CoreRoleRecovery) { + roles := v.getCoreRoles(ic, token) + if roles&(1< CoreRoleRecovery { + panic(ErrVitaInvalidRole) + } + + caller := ic.VM.GetCallingScriptHash() + + // Get token by caller address + token, err := v.getTokenByOwnerInternal(ic.DAO, caller) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrCallerHasNoVita) + } + + // Check token is active + if token.Status != state.TokenStatusActive { + panic(ErrTokenNotActive) + } + + // CoreRoleNone means any authenticated user + if coreRole == CoreRoleNone { + return stackitem.NewBigInteger(big.NewInt(int64(token.TokenID))) + } + + // Check if user has the required core role + roles := v.getCoreRoles(ic, token) + if roles&(1<= vestedUntil. +func (v *Vita) isFullyVested(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + + token, err := v.getTokenByIDInternal(ic.DAO, tokenID) + if err != nil { + panic(err) + } + if token == nil { + panic(ErrTokenNotFound) + } + + // Token is fully vested if current block >= vestedUntil + // VestedUntil of 0 means immediately vested (legacy tokens) + isVested := token.VestedUntil == 0 || ic.Block.Index >= token.VestedUntil + return stackitem.NewBool(isVested) +} + +// getVestingInfo returns vesting information for a Vita token. +// Returns [vestedUntil, isFullyVested, remainingBlocks] or null if token not found. +func (v *Vita) getVestingInfo(ic *interop.Context, args []stackitem.Item) stackitem.Item { + tokenID := toBigInt(args[0]).Uint64() + + token, err := v.getTokenByIDInternal(ic.DAO, tokenID) + if err != nil { + panic(err) + } + if token == nil { + return stackitem.Null{} + } + + isVested := token.VestedUntil == 0 || ic.Block.Index >= token.VestedUntil + + var remainingBlocks uint32 + if !isVested && token.VestedUntil > ic.Block.Index { + remainingBlocks = token.VestedUntil - ic.Block.Index + } + + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(token.VestedUntil))), + stackitem.NewBool(isVested), + stackitem.NewBigInteger(big.NewInt(int64(remainingBlocks))), + }) +} + +// Public methods for cross-native vesting access + +// IsFullyVestedInternal checks if a Vita token has completed its vesting period. +// For cross-contract use by other native contracts like Collocatio and Eligere. +func (v *Vita) IsFullyVestedInternal(d *dao.Simple, tokenID uint64, currentBlock uint32) bool { + token, err := v.getTokenByIDInternal(d, tokenID) + if err != nil || token == nil { + return false + } + // VestedUntil of 0 means immediately vested (legacy tokens) + return token.VestedUntil == 0 || currentBlock >= token.VestedUntil +} + +// GetVestedUntil returns the vesting block height for a Vita token. +// Returns 0 if token not found. +func (v *Vita) GetVestedUntil(d *dao.Simple, tokenID uint64) uint32 { + token, err := v.getTokenByIDInternal(d, tokenID) + if err != nil || token == nil { + return 0 + } + return token.VestedUntil +} diff --git a/pkg/core/native/vts.go b/pkg/core/native/vts.go index 72fb0e4..96005b0 100644 --- a/pkg/core/native/vts.go +++ b/pkg/core/native/vts.go @@ -1,1607 +1,1607 @@ -package native - -import ( - "encoding/binary" - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativeids" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// VTS storage prefixes. -const ( - vtsPrefixAccount = 0x20 // address -> VTSBalance - vtsPrefixVendor = 0x21 // address -> Vendor - vtsPrefixTotalSupply = 0x11 // total supply storage - vtsPrefixTaxConfig = 0x10 // tax configuration - vtsPrefixVendorCount = 0x12 // vendor count - vtsPrefixTxRecord = 0x30 // txHash -> TransactionRecord - vtsPrefixAccountTxs = 0x31 // address + blockHeight -> []txHash index - vtsPrefixTaxWithheld = 0x32 // address + year -> cumulative tax -) - -// VTSFactor is the divisor for VTS (10^8 = 100,000,000). -const VTSFactor = 100000000 - -// VTS represents the VTS (Value Transfer System) native contract. -// VTS is programmable money with spending restrictions and automatic tax accounting. -type VTS struct { - interop.ContractMD - - Tutus ITutus - RoleRegistry IRoleRegistry - Vita IVita - Lex ILex - - symbol string - decimals int64 - factor int64 -} - -// VTSCache holds cached VTS data. -type VTSCache struct { - vendorCount int64 -} - -// Copy implements dao.NativeContractCache. -func (c *VTSCache) Copy() dao.NativeContractCache { - return &VTSCache{ - vendorCount: c.vendorCount, - } -} - -// newVTS creates a new VTS native contract. -func newVTS() *VTS { - v := &VTS{ - symbol: "VTS", - decimals: 8, - factor: VTSFactor, - } - - v.ContractMD = *interop.NewContractMD(nativenames.VTS, nativeids.VTS, func(m *manifest.Manifest, hf config.Hardfork) { - m.SupportedStandards = []string{manifest.NEP17StandardName} - }) - defer v.BuildHFSpecificMD(v.ActiveIn()) - - // NEP-17 Standard Methods - desc := NewDescriptor("symbol", smartcontract.StringType) - md := NewMethodAndPrice(v.symbolMethod, 0, callflag.NoneFlag) - v.AddMethod(md, desc) - - desc = NewDescriptor("decimals", smartcontract.IntegerType) - md = NewMethodAndPrice(v.decimalsMethod, 0, callflag.NoneFlag) - v.AddMethod(md, desc) - - desc = NewDescriptor("totalSupply", smartcontract.IntegerType) - md = NewMethodAndPrice(v.totalSupply, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("balanceOf", smartcontract.IntegerType, - manifest.NewParameter("account", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.balanceOf, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - transferParams := []manifest.Parameter{ - manifest.NewParameter("from", smartcontract.Hash160Type), - manifest.NewParameter("to", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - } - desc = NewDescriptor("transfer", smartcontract.BoolType, - append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))..., - ) - md = NewMethodAndPrice(v.transfer, 1<<17, callflag.States|callflag.AllowCall|callflag.AllowNotify) - md.StorageFee = 50 - v.AddMethod(md, desc) - - // Extended Balance Methods - desc = NewDescriptor("unrestrictedBalanceOf", smartcontract.IntegerType, - manifest.NewParameter("account", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.unrestrictedBalanceOf, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("restrictedBalanceOf", smartcontract.IntegerType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("category", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.restrictedBalanceOf, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("balanceDetails", smartcontract.ArrayType, - manifest.NewParameter("account", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.balanceDetails, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // Minting Methods (Committee-Only) - desc = NewDescriptor("mint", smartcontract.BoolType, - manifest.NewParameter("to", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.mint, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - desc = NewDescriptor("mintRestricted", smartcontract.BoolType, - manifest.NewParameter("to", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("category", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.mintRestricted, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - desc = NewDescriptor("burn", smartcontract.BoolType, - manifest.NewParameter("from", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.burn, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - desc = NewDescriptor("convertToUnrestricted", smartcontract.BoolType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("category", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.convertToUnrestricted, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // Vendor Management Methods (Committee-Only) - desc = NewDescriptor("registerVendor", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("categories", smartcontract.IntegerType), - manifest.NewParameter("ageRestricted", smartcontract.BoolType)) - md = NewMethodAndPrice(v.registerVendor, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - desc = NewDescriptor("updateVendor", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("categories", smartcontract.IntegerType), - manifest.NewParameter("ageRestricted", smartcontract.BoolType)) - md = NewMethodAndPrice(v.updateVendor, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - desc = NewDescriptor("deactivateVendor", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.deactivateVendor, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - desc = NewDescriptor("getVendor", smartcontract.ArrayType, - manifest.NewParameter("address", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.getVendor, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("isVendor", smartcontract.BoolType, - manifest.NewParameter("address", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.isVendor, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("getVendorCategories", smartcontract.IntegerType, - manifest.NewParameter("address", smartcontract.Hash160Type)) - md = NewMethodAndPrice(v.getVendorCategories, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // Spending Methods - desc = NewDescriptor("spend", smartcontract.BoolType, - manifest.NewParameter("from", smartcontract.Hash160Type), - manifest.NewParameter("vendor", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("data", smartcontract.AnyType)) - md = NewMethodAndPrice(v.spend, 1<<17, callflag.States|callflag.AllowCall|callflag.AllowNotify) - md.StorageFee = 50 - v.AddMethod(md, desc) - - desc = NewDescriptor("canSpendAt", smartcontract.BoolType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("vendor", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.canSpendAt, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - // Tax Methods - desc = NewDescriptor("payWage", smartcontract.BoolType, - manifest.NewParameter("employer", smartcontract.Hash160Type), - manifest.NewParameter("employee", smartcontract.Hash160Type), - manifest.NewParameter("grossAmount", smartcontract.IntegerType), - manifest.NewParameter("taxRate", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.payWage, 1<<17, callflag.States|callflag.AllowNotify) - md.StorageFee = 100 - v.AddMethod(md, desc) - - desc = NewDescriptor("setTaxConfig", smartcontract.BoolType, - manifest.NewParameter("incomeRate", smartcontract.IntegerType), - manifest.NewParameter("salesRate", smartcontract.IntegerType), - manifest.NewParameter("treasuryAddress", smartcontract.Hash160Type), - manifest.NewParameter("exemptCategories", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.setTaxConfig, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - desc = NewDescriptor("getTaxConfig", smartcontract.ArrayType) - md = NewMethodAndPrice(v.getTaxConfig, 1<<15, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("issueTaxRefund", smartcontract.BoolType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.issueTaxRefund, 1<<17, callflag.States|callflag.AllowNotify) - v.AddMethod(md, desc) - - // Tax Reporting Methods (read-only) - desc = NewDescriptor("getTransactions", smartcontract.ArrayType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("startBlock", smartcontract.IntegerType), - manifest.NewParameter("endBlock", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.getTransactions, 1<<17, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("getIncomeForPeriod", smartcontract.IntegerType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("startBlock", smartcontract.IntegerType), - manifest.NewParameter("endBlock", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.getIncomeForPeriod, 1<<17, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("getTaxWithheld", smartcontract.IntegerType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("startBlock", smartcontract.IntegerType), - manifest.NewParameter("endBlock", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.getTaxWithheld, 1<<17, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("getDeductibleExpenses", smartcontract.IntegerType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("startBlock", smartcontract.IntegerType), - manifest.NewParameter("endBlock", smartcontract.IntegerType), - manifest.NewParameter("category", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.getDeductibleExpenses, 1<<17, callflag.ReadStates) - v.AddMethod(md, desc) - - desc = NewDescriptor("getTaxSummary", smartcontract.ArrayType, - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("startBlock", smartcontract.IntegerType), - manifest.NewParameter("endBlock", smartcontract.IntegerType)) - md = NewMethodAndPrice(v.getTaxSummary, 1<<17, callflag.ReadStates) - v.AddMethod(md, desc) - - // Events - eDesc := NewEventDescriptor("Transfer", transferParams...) - eMD := NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("Mint", - manifest.NewParameter("to", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("category", smartcontract.IntegerType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("Burn", - manifest.NewParameter("from", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("Spend", - manifest.NewParameter("from", smartcontract.Hash160Type), - manifest.NewParameter("vendor", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType), - manifest.NewParameter("categoriesUsed", smartcontract.IntegerType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("VendorRegistered", - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("categories", smartcontract.IntegerType), - manifest.NewParameter("ageRestricted", smartcontract.BoolType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("VendorUpdated", - manifest.NewParameter("address", smartcontract.Hash160Type), - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("categories", smartcontract.IntegerType), - manifest.NewParameter("ageRestricted", smartcontract.BoolType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("VendorDeactivated", - manifest.NewParameter("address", smartcontract.Hash160Type)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("TaxWithheld", - manifest.NewParameter("from", smartcontract.Hash160Type), - manifest.NewParameter("to", smartcontract.Hash160Type), - manifest.NewParameter("grossAmount", smartcontract.IntegerType), - manifest.NewParameter("taxAmount", smartcontract.IntegerType), - manifest.NewParameter("taxRate", smartcontract.IntegerType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("TaxRefunded", - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("amount", smartcontract.IntegerType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - eDesc = NewEventDescriptor("ConvertedToUnrestricted", - manifest.NewParameter("account", smartcontract.Hash160Type), - manifest.NewParameter("category", smartcontract.IntegerType), - manifest.NewParameter("amount", smartcontract.IntegerType)) - eMD = NewEvent(eDesc) - v.AddEvent(eMD) - - return v -} - -// Metadata returns VTS contract metadata. -func (v *VTS) Metadata() *interop.ContractMD { - return &v.ContractMD -} - -// Initialize initializes VTS contract at genesis. -func (v *VTS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != v.ActiveIn() { - return nil - } - - // Initialize with zero total supply (VTS is minted on-demand) - v.putTotalSupply(ic.DAO, big.NewInt(0)) - - // Initialize vendor count - v.putVendorCount(ic.DAO, 0) - - // Initialize default tax config (no taxes by default) - cfg := &state.TaxConfig{ - DefaultIncomeRate: 0, - DefaultSalesRate: 0, - TreasuryAddress: util.Uint160{}, - ExemptCategories: 0, - } - v.putTaxConfig(ic.DAO, cfg) - - return nil -} - -// InitializeCache implements the Contract interface. -func (v *VTS) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { - cache := &VTSCache{ - vendorCount: v.getVendorCountInternal(d), - } - d.SetCache(v.ID, cache) - return nil -} - -// OnPersist implements the Contract interface. -func (v *VTS) OnPersist(ic *interop.Context) error { - return nil -} - -// PostPersist implements the Contract interface. -func (v *VTS) PostPersist(ic *interop.Context) error { - return nil -} - -// ActiveIn implements the Contract interface. -func (v *VTS) ActiveIn() *config.Hardfork { - return nil -} - -// checkCommittee verifies the caller has committee privileges. -func (v *VTS) checkCommittee(ic *interop.Context) bool { - if v.RoleRegistry != nil { - return v.RoleRegistry.CheckCommittee(ic) - } - if v.Tutus != nil { - return v.Tutus.CheckCommittee(ic) - } - return false -} - -// ============ Storage Key Helpers ============ - -func (v *VTS) makeAccountKey(h util.Uint160) []byte { - return makeUint160Key(vtsPrefixAccount, h) -} - -func (v *VTS) makeVendorKey(h util.Uint160) []byte { - return makeUint160Key(vtsPrefixVendor, h) -} - -var vtsTotalSupplyKey = []byte{vtsPrefixTotalSupply} -var vtsVendorCountKey = []byte{vtsPrefixVendorCount} -var vtsTaxConfigKey = []byte{vtsPrefixTaxConfig} - -// ============ NEP-17 Standard Methods ============ - -func (v *VTS) symbolMethod(_ *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewByteArray([]byte(v.symbol)) -} - -func (v *VTS) decimalsMethod(_ *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(v.decimals)) -} - -func (v *VTS) totalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - supply := v.getTotalSupplyInternal(ic.DAO) - return stackitem.NewBigInteger(supply) -} - -func (v *VTS) getTotalSupplyInternal(d *dao.Simple) *big.Int { - si := d.GetStorageItem(v.ID, vtsTotalSupplyKey) - if si == nil { - return big.NewInt(0) - } - bal, err := state.NEP17BalanceFromBytes(si) - if err != nil { - return big.NewInt(0) - } - return &bal.Balance -} - -func (v *VTS) putTotalSupply(d *dao.Simple, supply *big.Int) { - bal := &state.NEP17Balance{Balance: *supply} - d.PutStorageItem(v.ID, vtsTotalSupplyKey, bal.Bytes(nil)) -} - -func (v *VTS) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { - h := toUint160(args[0]) - balance := v.getBalanceInternal(ic.DAO, h) - return stackitem.NewBigInteger(balance.Total()) -} - -func (v *VTS) getBalanceInternal(d *dao.Simple, h util.Uint160) *state.VTSBalance { - key := v.makeAccountKey(h) - si := d.GetStorageItem(v.ID, key) - if si == nil { - return state.NewVTSBalance() - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return state.NewVTSBalance() - } - - bal := state.NewVTSBalance() - if err := bal.FromStackItem(item); err != nil { - return state.NewVTSBalance() - } - return bal -} - -func (v *VTS) putBalance(d *dao.Simple, h util.Uint160, bal *state.VTSBalance) error { - key := v.makeAccountKey(h) - - // If balance is zero, delete the entry - if bal.Total().Sign() == 0 { - d.DeleteStorageItem(v.ID, key) - return nil - } - - item, err := bal.ToStackItem() - if err != nil { - return err - } - data, err := stackitem.Serialize(item) - if err != nil { - return err - } - d.PutStorageItem(v.ID, key, data) - return nil -} - -func (v *VTS) transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item { - from := toUint160(args[0]) - to := toUint160(args[1]) - amount := toBigInt(args[2]) - - // Transfer only unrestricted balance - err := v.transferUnrestricted(ic, from, to, amount, args[3]) - return stackitem.NewBool(err == nil) -} - -func (v *VTS) transferUnrestricted(ic *interop.Context, from, to util.Uint160, amount *big.Int, data stackitem.Item) error { - if amount.Sign() < 0 { - return errors.New("negative amount") - } - - // Check witness - caller := ic.VM.GetCallingScriptHash() - if !from.Equals(caller) { - ok, err := checkWitness(ic, from) - if err != nil || !ok { - return errors.New("invalid signature") - } - } - - // Check property rights via Lex (if Lex is wired) - if v.Lex != nil && !v.Lex.CheckPropertyRight(ic.DAO, from, ic.Block.Index) { - return errors.New("property rights restricted") - } - - if amount.Sign() == 0 { - v.emitTransfer(ic, &from, &to, amount) - return nil - } - - // Get sender balance - fromBal := v.getBalanceInternal(ic.DAO, from) - if fromBal.Unrestricted.Cmp(amount) < 0 { - return errors.New("insufficient unrestricted funds") - } - - // Update sender balance - fromBal.Unrestricted.Sub(&fromBal.Unrestricted, amount) - if err := v.putBalance(ic.DAO, from, fromBal); err != nil { - return err - } - - // Update recipient balance - toBal := v.getBalanceInternal(ic.DAO, to) - toBal.Unrestricted.Add(&toBal.Unrestricted, amount) - if err := v.putBalance(ic.DAO, to, toBal); err != nil { - return err - } - - v.emitTransfer(ic, &from, &to, amount) - return nil -} - -func (v *VTS) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) { - ic.AddNotification(v.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{ - addrToStackItem(from), - addrToStackItem(to), - stackitem.NewBigInteger(amount), - })) -} - -// ============ Extended Balance Methods ============ - -func (v *VTS) unrestrictedBalanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { - h := toUint160(args[0]) - bal := v.getBalanceInternal(ic.DAO, h) - return stackitem.NewBigInteger(&bal.Unrestricted) -} - -func (v *VTS) restrictedBalanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { - h := toUint160(args[0]) - category := toUint8(args[1]) - bal := v.getBalanceInternal(ic.DAO, h) - - if amt, ok := bal.Restricted[category]; ok { - return stackitem.NewBigInteger(amt) - } - return stackitem.NewBigInteger(big.NewInt(0)) -} - -func (v *VTS) balanceDetails(ic *interop.Context, args []stackitem.Item) stackitem.Item { - h := toUint160(args[0]) - bal := v.getBalanceInternal(ic.DAO, h) - - // Return: [unrestricted, [[category, amount], ...]] - restricted := make([]stackitem.Item, 0, len(bal.Restricted)) - for cat, amt := range bal.Restricted { - if amt.Sign() > 0 { - restricted = append(restricted, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(cat))), - stackitem.NewBigInteger(amt), - })) - } - } - - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(&bal.Unrestricted), - stackitem.NewArray(restricted), - }) -} - -// ============ Minting Methods ============ - -func (v *VTS) mint(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - to := toUint160(args[0]) - amount := toBigInt(args[1]) - - if amount.Sign() <= 0 { - panic("amount must be positive") - } - - v.mintUnrestricted(ic, to, amount) - return stackitem.NewBool(true) -} - -func (v *VTS) mintUnrestricted(ic *interop.Context, to util.Uint160, amount *big.Int) { - bal := v.getBalanceInternal(ic.DAO, to) - bal.Unrestricted.Add(&bal.Unrestricted, amount) - v.putBalance(ic.DAO, to, bal) - - // Update total supply - supply := v.getTotalSupplyInternal(ic.DAO) - supply.Add(supply, amount) - v.putTotalSupply(ic.DAO, supply) - - v.emitTransfer(ic, nil, &to, amount) - ic.AddNotification(v.Hash, "Mint", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(to.BytesBE()), - stackitem.NewBigInteger(amount), - stackitem.NewBigInteger(big.NewInt(0)), // Category 0 = unrestricted - })) -} - -func (v *VTS) mintRestricted(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - to := toUint160(args[0]) - amount := toBigInt(args[1]) - category := toUint8(args[2]) - - if amount.Sign() <= 0 { - panic("amount must be positive") - } - if category == state.CategoryUnrestricted { - panic("use mint() for unrestricted tokens") - } - - // Restricted VTS requires recipient to have a Vita - // This ensures benefits go to verified identities - if !v.Vita.TokenExists(ic.DAO, to) { - panic("restricted VTS requires Vita") - } - - v.mintRestrictedInternal(ic, to, amount, category) - return stackitem.NewBool(true) -} - -func (v *VTS) mintRestrictedInternal(ic *interop.Context, to util.Uint160, amount *big.Int, category uint8) { - bal := v.getBalanceInternal(ic.DAO, to) - if bal.Restricted == nil { - bal.Restricted = make(map[uint8]*big.Int) - } - if bal.Restricted[category] == nil { - bal.Restricted[category] = big.NewInt(0) - } - bal.Restricted[category].Add(bal.Restricted[category], amount) - v.putBalance(ic.DAO, to, bal) - - // Update total supply - supply := v.getTotalSupplyInternal(ic.DAO) - supply.Add(supply, amount) - v.putTotalSupply(ic.DAO, supply) - - v.emitTransfer(ic, nil, &to, amount) - ic.AddNotification(v.Hash, "Mint", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(to.BytesBE()), - stackitem.NewBigInteger(amount), - stackitem.NewBigInteger(big.NewInt(int64(category))), - })) -} - -func (v *VTS) burn(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - from := toUint160(args[0]) - amount := toBigInt(args[1]) - - if amount.Sign() <= 0 { - panic("amount must be positive") - } - - bal := v.getBalanceInternal(ic.DAO, from) - if bal.Unrestricted.Cmp(amount) < 0 { - panic("insufficient unrestricted funds") - } - - bal.Unrestricted.Sub(&bal.Unrestricted, amount) - v.putBalance(ic.DAO, from, bal) - - // Update total supply - supply := v.getTotalSupplyInternal(ic.DAO) - supply.Sub(supply, amount) - v.putTotalSupply(ic.DAO, supply) - - v.emitTransfer(ic, &from, nil, amount) - ic.AddNotification(v.Hash, "Burn", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(from.BytesBE()), - stackitem.NewBigInteger(amount), - })) - - return stackitem.NewBool(true) -} - -func (v *VTS) convertToUnrestricted(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - account := toUint160(args[0]) - category := toUint8(args[1]) - amount := toBigInt(args[2]) - - if amount.Sign() <= 0 { - panic("amount must be positive") - } - - bal := v.getBalanceInternal(ic.DAO, account) - if bal.Restricted[category] == nil || bal.Restricted[category].Cmp(amount) < 0 { - panic("insufficient restricted funds in category") - } - - // Move from restricted to unrestricted - bal.Restricted[category].Sub(bal.Restricted[category], amount) - if bal.Restricted[category].Sign() == 0 { - delete(bal.Restricted, category) - } - bal.Unrestricted.Add(&bal.Unrestricted, amount) - - v.putBalance(ic.DAO, account, bal) - - ic.AddNotification(v.Hash, "ConvertedToUnrestricted", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(account.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(category))), - stackitem.NewBigInteger(amount), - })) - - return stackitem.NewBool(true) -} - -// ============ Vendor Management ============ - -func (v *VTS) registerVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - address := toUint160(args[0]) - name := toString(args[1]) - categories := toUint8(args[2]) - ageRestricted := toBool(args[3]) - - if len(name) == 0 || len(name) > 64 { - panic("invalid vendor name") - } - - // Check if vendor already exists - if v.getVendorInternal(ic.DAO, address) != nil { - panic("vendor already registered") - } - - vendor := &state.Vendor{ - Address: address, - Name: name, - Categories: categories, - RegisteredAt: ic.Block.Index, - RegisteredBy: ic.VM.GetCallingScriptHash(), - Active: true, - AgeRestricted: ageRestricted, - } - - v.putVendor(ic.DAO, vendor) - - // Increment vendor count - count := v.getVendorCountInternal(ic.DAO) - v.putVendorCount(ic.DAO, count+1) - - ic.AddNotification(v.Hash, "VendorRegistered", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(address.BytesBE()), - stackitem.NewByteArray([]byte(name)), - stackitem.NewBigInteger(big.NewInt(int64(categories))), - stackitem.NewBool(ageRestricted), - })) - - return stackitem.NewBool(true) -} - -func (v *VTS) updateVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - address := toUint160(args[0]) - name := toString(args[1]) - categories := toUint8(args[2]) - ageRestricted := toBool(args[3]) - - if len(name) == 0 || len(name) > 64 { - panic("invalid vendor name") - } - - vendor := v.getVendorInternal(ic.DAO, address) - if vendor == nil { - panic("vendor not found") - } - - vendor.Name = name - vendor.Categories = categories - vendor.AgeRestricted = ageRestricted - v.putVendor(ic.DAO, vendor) - - ic.AddNotification(v.Hash, "VendorUpdated", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(address.BytesBE()), - stackitem.NewByteArray([]byte(name)), - stackitem.NewBigInteger(big.NewInt(int64(categories))), - stackitem.NewBool(ageRestricted), - })) - - return stackitem.NewBool(true) -} - -func (v *VTS) deactivateVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - address := toUint160(args[0]) - - vendor := v.getVendorInternal(ic.DAO, address) - if vendor == nil { - panic("vendor not found") - } - - vendor.Active = false - v.putVendor(ic.DAO, vendor) - - ic.AddNotification(v.Hash, "VendorDeactivated", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(address.BytesBE()), - })) - - return stackitem.NewBool(true) -} - -func (v *VTS) getVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - vendor := v.getVendorInternal(ic.DAO, address) - if vendor == nil { - return stackitem.Null{} - } - - item, _ := vendor.ToStackItem() - return item -} - -func (v *VTS) getVendorInternal(d *dao.Simple, address util.Uint160) *state.Vendor { - key := v.makeVendorKey(address) - si := d.GetStorageItem(v.ID, key) - if si == nil { - return nil - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return nil - } - - vendor := &state.Vendor{} - if err := vendor.FromStackItem(item); err != nil { - return nil - } - return vendor -} - -func (v *VTS) putVendor(d *dao.Simple, vendor *state.Vendor) { - key := v.makeVendorKey(vendor.Address) - item, _ := vendor.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(v.ID, key, data) -} - -func (v *VTS) isVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - vendor := v.getVendorInternal(ic.DAO, address) - return stackitem.NewBool(vendor != nil && vendor.Active) -} - -func (v *VTS) getVendorCategories(ic *interop.Context, args []stackitem.Item) stackitem.Item { - address := toUint160(args[0]) - vendor := v.getVendorInternal(ic.DAO, address) - if vendor == nil { - return stackitem.NewBigInteger(big.NewInt(0)) - } - return stackitem.NewBigInteger(big.NewInt(int64(vendor.Categories))) -} - -func (v *VTS) getVendorCountInternal(d *dao.Simple) int64 { - si := d.GetStorageItem(v.ID, vtsVendorCountKey) - if si == nil { - return 0 - } - return int64(binary.BigEndian.Uint64(si)) -} - -func (v *VTS) putVendorCount(d *dao.Simple, count int64) { - data := make([]byte, 8) - binary.BigEndian.PutUint64(data, uint64(count)) - d.PutStorageItem(v.ID, vtsVendorCountKey, data) -} - -// ============ Spending Logic ============ - -func (v *VTS) spend(ic *interop.Context, args []stackitem.Item) stackitem.Item { - from := toUint160(args[0]) - vendor := toUint160(args[1]) - amount := toBigInt(args[2]) - data := args[3] - - if amount.Sign() <= 0 { - panic("amount must be positive") - } - - // Check witness for sender - caller := ic.VM.GetCallingScriptHash() - if !from.Equals(caller) { - ok, err := checkWitness(ic, from) - if err != nil || !ok { - panic("invalid signature") - } - } - - // Check property rights via Lex (if Lex is wired) - if v.Lex != nil && !v.Lex.CheckPropertyRight(ic.DAO, from, ic.Block.Index) { - panic("property rights restricted") - } - - // Verify vendor is registered and active - vendorInfo := v.getVendorInternal(ic.DAO, vendor) - if vendorInfo == nil || !vendorInfo.Active { - panic("invalid or inactive vendor") - } - - // Check age verification for age-restricted vendors (e.g., alcohol, tobacco) - if vendorInfo.AgeRestricted { - if !v.Vita.IsAdultVerified(ic.DAO, from) { - panic("age verification required for this vendor") - } - } - - // Get sender's balance - fromBal := v.getBalanceInternal(ic.DAO, from) - - // Try to spend from matching restricted categories first - remaining := new(big.Int).Set(amount) - var categoriesUsed uint8 = 0 - - for category, catBal := range fromBal.Restricted { - if remaining.Sign() <= 0 { - break - } - // Check if vendor accepts this category - if vendorInfo.Categories&category != 0 { - toSpend := new(big.Int) - if catBal.Cmp(remaining) >= 0 { - toSpend.Set(remaining) - } else { - toSpend.Set(catBal) - } - catBal.Sub(catBal, toSpend) - if catBal.Sign() == 0 { - delete(fromBal.Restricted, category) - } - remaining.Sub(remaining, toSpend) - categoriesUsed |= category - } - } - - // If still remaining, use unrestricted balance - if remaining.Sign() > 0 { - if fromBal.Unrestricted.Cmp(remaining) < 0 { - panic("insufficient funds") - } - fromBal.Unrestricted.Sub(&fromBal.Unrestricted, remaining) - } - - // Save sender balance - v.putBalance(ic.DAO, from, fromBal) - - // Credit vendor with unrestricted balance - vendorBal := v.getBalanceInternal(ic.DAO, vendor) - vendorBal.Unrestricted.Add(&vendorBal.Unrestricted, amount) - v.putBalance(ic.DAO, vendor, vendorBal) - - // Emit events - v.emitTransfer(ic, &from, &vendor, amount) - ic.AddNotification(v.Hash, "Spend", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(from.BytesBE()), - stackitem.NewByteArray(vendor.BytesBE()), - stackitem.NewBigInteger(amount), - stackitem.NewBigInteger(big.NewInt(int64(categoriesUsed))), - })) - - _ = data // data parameter for compatibility - return stackitem.NewBool(true) -} - -func (v *VTS) canSpendAt(ic *interop.Context, args []stackitem.Item) stackitem.Item { - account := toUint160(args[0]) - vendor := toUint160(args[1]) - amount := toBigInt(args[2]) - - if amount.Sign() <= 0 { - return stackitem.NewBool(false) - } - - // Check vendor - vendorInfo := v.getVendorInternal(ic.DAO, vendor) - if vendorInfo == nil || !vendorInfo.Active { - return stackitem.NewBool(false) - } - - // Check age verification for age-restricted vendors - if vendorInfo.AgeRestricted { - if !v.Vita.IsAdultVerified(ic.DAO, account) { - return stackitem.NewBool(false) - } - } - - // Get balance - bal := v.getBalanceInternal(ic.DAO, account) - - // Calculate available funds - available := new(big.Int).Set(&bal.Unrestricted) - for category, catBal := range bal.Restricted { - if vendorInfo.Categories&category != 0 { - available.Add(available, catBal) - } - } - - return stackitem.NewBool(available.Cmp(amount) >= 0) -} - -// ============ Tax Accounting ============ - -func (v *VTS) payWage(ic *interop.Context, args []stackitem.Item) stackitem.Item { - employer := toUint160(args[0]) - employee := toUint160(args[1]) - grossAmount := toBigInt(args[2]) - taxRate := toUint64(args[3]) // basis points (e.g., 2500 = 25%) - - if grossAmount.Sign() <= 0 { - panic("gross amount must be positive") - } - if taxRate > 10000 { - panic("tax rate cannot exceed 100%") - } - - // Check witness for employer - caller := ic.VM.GetCallingScriptHash() - if !employer.Equals(caller) { - ok, err := checkWitness(ic, employer) - if err != nil || !ok { - panic("invalid signature") - } - } - - // Get employer balance - employerBal := v.getBalanceInternal(ic.DAO, employer) - if employerBal.Unrestricted.Cmp(grossAmount) < 0 { - panic("insufficient funds") - } - - // Calculate tax and net amounts - taxAmount := new(big.Int).Mul(grossAmount, big.NewInt(int64(taxRate))) - taxAmount.Div(taxAmount, big.NewInt(10000)) - netAmount := new(big.Int).Sub(grossAmount, taxAmount) - - // Get tax config for treasury address - taxConfig := v.getTaxConfigInternal(ic.DAO) - - // Deduct from employer - employerBal.Unrestricted.Sub(&employerBal.Unrestricted, grossAmount) - v.putBalance(ic.DAO, employer, employerBal) - - // Credit employee (net) - employeeBal := v.getBalanceInternal(ic.DAO, employee) - employeeBal.Unrestricted.Add(&employeeBal.Unrestricted, netAmount) - v.putBalance(ic.DAO, employee, employeeBal) - - // Credit treasury (tax) - if taxAmount.Sign() > 0 && !taxConfig.TreasuryAddress.Equals(util.Uint160{}) { - treasuryBal := v.getBalanceInternal(ic.DAO, taxConfig.TreasuryAddress) - treasuryBal.Unrestricted.Add(&treasuryBal.Unrestricted, taxAmount) - v.putBalance(ic.DAO, taxConfig.TreasuryAddress, treasuryBal) - } - - // Store transaction record for tax reporting - record := &state.TransactionRecord{ - TxHash: ic.Tx.Hash(), - BlockHeight: ic.BlockHeight(), - From: employer, - To: employee, - Amount: *grossAmount, - TxType: state.TxTypeIncome, - Category: state.CategoryUnrestricted, - TaxWithheld: *taxAmount, - TaxRate: uint16(taxRate), - } - v.storeTransactionRecord(ic.DAO, record) - - // Emit events - v.emitTransfer(ic, &employer, &employee, netAmount) - if taxAmount.Sign() > 0 && !taxConfig.TreasuryAddress.Equals(util.Uint160{}) { - v.emitTransfer(ic, &employer, &taxConfig.TreasuryAddress, taxAmount) - } - ic.AddNotification(v.Hash, "TaxWithheld", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(employer.BytesBE()), - stackitem.NewByteArray(employee.BytesBE()), - stackitem.NewBigInteger(grossAmount), - stackitem.NewBigInteger(taxAmount), - stackitem.NewBigInteger(big.NewInt(int64(taxRate))), - })) - - return stackitem.NewBool(true) -} - -func (v *VTS) setTaxConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - incomeRate := uint16(toUint64(args[0])) - salesRate := uint16(toUint64(args[1])) - treasuryAddress := toUint160(args[2]) - exemptCategories := toUint8(args[3]) - - if incomeRate > 10000 || salesRate > 10000 { - panic("rate cannot exceed 100%") - } - - cfg := &state.TaxConfig{ - DefaultIncomeRate: incomeRate, - DefaultSalesRate: salesRate, - TreasuryAddress: treasuryAddress, - ExemptCategories: exemptCategories, - } - v.putTaxConfig(ic.DAO, cfg) - - return stackitem.NewBool(true) -} - -func (v *VTS) getTaxConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - cfg := v.getTaxConfigInternal(ic.DAO) - item, _ := cfg.ToStackItem() - return item -} - -func (v *VTS) getTaxConfigInternal(d *dao.Simple) *state.TaxConfig { - si := d.GetStorageItem(v.ID, vtsTaxConfigKey) - if si == nil { - return &state.TaxConfig{} - } - - item, err := stackitem.Deserialize(si) - if err != nil { - return &state.TaxConfig{} - } - - cfg := &state.TaxConfig{} - if err := cfg.FromStackItem(item); err != nil { - return &state.TaxConfig{} - } - return cfg -} - -func (v *VTS) putTaxConfig(d *dao.Simple, cfg *state.TaxConfig) { - item, _ := cfg.ToStackItem() - data, _ := stackitem.Serialize(item) - d.PutStorageItem(v.ID, vtsTaxConfigKey, data) -} - -func (v *VTS) issueTaxRefund(ic *interop.Context, args []stackitem.Item) stackitem.Item { - if !v.checkCommittee(ic) { - panic("caller is not a committee member") - } - - account := toUint160(args[0]) - amount := toBigInt(args[1]) - - if amount.Sign() <= 0 { - panic("amount must be positive") - } - - // Mint new VTS as refund (comes from treasury/system) - v.mintUnrestricted(ic, account, amount) - - ic.AddNotification(v.Hash, "TaxRefunded", stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(account.BytesBE()), - stackitem.NewBigInteger(amount), - })) - - return stackitem.NewBool(true) -} - -// ============ Tax Reporting Methods ============ - -// makeTxRecordKey creates a storage key for a transaction record. -func (v *VTS) makeTxRecordKey(blockHeight uint32, txIndex uint32) []byte { - key := make([]byte, 9) - key[0] = vtsPrefixTxRecord - binary.BigEndian.PutUint32(key[1:5], blockHeight) - binary.BigEndian.PutUint32(key[5:9], txIndex) - return key -} - -// makeAccountTxIndexKey creates a storage key for an account's transaction index. -func (v *VTS) makeAccountTxIndexKey(account util.Uint160, blockHeight uint32) []byte { - key := make([]byte, 25) - key[0] = vtsPrefixAccountTxs - copy(key[1:21], account.BytesBE()) - binary.BigEndian.PutUint32(key[21:25], blockHeight) - return key -} - -// makeTaxWithheldKey creates a storage key for cumulative tax withheld. -func (v *VTS) makeTaxWithheldKey(account util.Uint160, blockHeight uint32) []byte { - key := make([]byte, 25) - key[0] = vtsPrefixTaxWithheld - copy(key[1:21], account.BytesBE()) - binary.BigEndian.PutUint32(key[21:25], blockHeight) - return key -} - -// storeTransactionRecord stores a transaction record and updates indexes. -func (v *VTS) storeTransactionRecord(d *dao.Simple, record *state.TransactionRecord) { - // Get current tx index for this block - txIndex := v.getNextTxIndex(d, record.BlockHeight) - - // Store the record - key := v.makeTxRecordKey(record.BlockHeight, txIndex) - item, _ := record.ToStackItem() - data, err := stackitem.Serialize(item) - if err != nil { - panic("failed to serialize transaction record") - } - d.PutStorageItem(v.ID, key, data) - - // Update account indexes for both from and to - v.addAccountTxIndex(d, record.From, record.BlockHeight, txIndex) - if !record.To.Equals(record.From) { - v.addAccountTxIndex(d, record.To, record.BlockHeight, txIndex) - } - - // Update cumulative tax withheld for recipient (income tax) - if record.TxType == state.TxTypeIncome && record.TaxWithheld.Sign() > 0 { - v.addCumulativeTaxWithheld(d, record.To, record.BlockHeight, &record.TaxWithheld) - } - - // Increment tx count for this block - v.putTxCount(d, record.BlockHeight, txIndex+1) -} - -// getNextTxIndex returns the next transaction index for a block. -func (v *VTS) getNextTxIndex(d *dao.Simple, blockHeight uint32) uint32 { - return v.getTxCount(d, blockHeight) -} - -// makeTxCountKey creates a storage key for transaction count per block. -func (v *VTS) makeTxCountKey(blockHeight uint32) []byte { - key := make([]byte, 5) - key[0] = vtsPrefixTxRecord - binary.BigEndian.PutUint32(key[1:5], blockHeight) - return key -} - -// getTxCount returns the transaction count for a block. -func (v *VTS) getTxCount(d *dao.Simple, blockHeight uint32) uint32 { - key := v.makeTxCountKey(blockHeight) - si := d.GetStorageItem(v.ID, key) - if si == nil || len(si) < 4 { - return 0 - } - return binary.BigEndian.Uint32(si) -} - -// putTxCount stores the transaction count for a block. -func (v *VTS) putTxCount(d *dao.Simple, blockHeight uint32, count uint32) { - key := v.makeTxCountKey(blockHeight) - data := make([]byte, 4) - binary.BigEndian.PutUint32(data, count) - d.PutStorageItem(v.ID, key, data) -} - -// addAccountTxIndex adds a transaction index to an account's list. -func (v *VTS) addAccountTxIndex(d *dao.Simple, account util.Uint160, blockHeight uint32, txIndex uint32) { - key := v.makeAccountTxIndexKey(account, blockHeight) - existing := d.GetStorageItem(v.ID, key) - - // Append new index - newData := make([]byte, len(existing)+4) - copy(newData, existing) - binary.BigEndian.PutUint32(newData[len(existing):], txIndex) - d.PutStorageItem(v.ID, key, newData) -} - -// addCumulativeTaxWithheld adds to the cumulative tax withheld for an account. -func (v *VTS) addCumulativeTaxWithheld(d *dao.Simple, account util.Uint160, blockHeight uint32, amount *big.Int) { - key := v.makeTaxWithheldKey(account, blockHeight) - existing := d.GetStorageItem(v.ID, key) - - cumulative := big.NewInt(0) - if len(existing) > 0 { - cumulative.SetBytes(existing) - } - cumulative.Add(cumulative, amount) - - d.PutStorageItem(v.ID, key, cumulative.Bytes()) -} - -// getTransactions returns transactions for an account in a block range. -func (v *VTS) getTransactions(ic *interop.Context, args []stackitem.Item) stackitem.Item { - account := toUint160(args[0]) - startBlock := uint32(toUint64(args[1])) - endBlock := uint32(toUint64(args[2])) - - if endBlock < startBlock { - panic("endBlock must be >= startBlock") - } - - // Limit range to prevent DoS - maxRange := uint32(10000) - if endBlock-startBlock > maxRange { - panic("block range too large") - } - - var records []stackitem.Item - for block := startBlock; block <= endBlock; block++ { - // Get transaction indexes for this account at this block - key := v.makeAccountTxIndexKey(account, block) - indexData := ic.DAO.GetStorageItem(v.ID, key) - - // Parse indexes - for i := 0; i+4 <= len(indexData); i += 4 { - txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) - - // Get the transaction record - recordKey := v.makeTxRecordKey(block, txIndex) - recordData := ic.DAO.GetStorageItem(v.ID, recordKey) - if len(recordData) > 0 { - item, err := stackitem.Deserialize(recordData) - if err == nil { - records = append(records, item) - } - } - } - } - - return stackitem.NewArray(records) -} - -// getIncomeForPeriod returns total taxable income for an account in a block range. -func (v *VTS) getIncomeForPeriod(ic *interop.Context, args []stackitem.Item) stackitem.Item { - account := toUint160(args[0]) - startBlock := uint32(toUint64(args[1])) - endBlock := uint32(toUint64(args[2])) - - if endBlock < startBlock { - panic("endBlock must be >= startBlock") - } - - totalIncome := big.NewInt(0) - for block := startBlock; block <= endBlock; block++ { - key := v.makeAccountTxIndexKey(account, block) - indexData := ic.DAO.GetStorageItem(v.ID, key) - - for i := 0; i+4 <= len(indexData); i += 4 { - txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) - recordKey := v.makeTxRecordKey(block, txIndex) - recordData := ic.DAO.GetStorageItem(v.ID, recordKey) - - if len(recordData) > 0 { - item, err := stackitem.Deserialize(recordData) - if err == nil { - var record state.TransactionRecord - if record.FromStackItem(item) == nil { - // Only count income where this account is the recipient - if record.To.Equals(account) && record.TxType == state.TxTypeIncome { - totalIncome.Add(totalIncome, &record.Amount) - } - } - } - } - } - } - - return stackitem.NewBigInteger(totalIncome) -} - -// getTaxWithheld returns total tax withheld for an account in a block range. -func (v *VTS) getTaxWithheld(ic *interop.Context, args []stackitem.Item) stackitem.Item { - account := toUint160(args[0]) - startBlock := uint32(toUint64(args[1])) - endBlock := uint32(toUint64(args[2])) - - if endBlock < startBlock { - panic("endBlock must be >= startBlock") - } - - totalTax := big.NewInt(0) - for block := startBlock; block <= endBlock; block++ { - key := v.makeTaxWithheldKey(account, block) - data := ic.DAO.GetStorageItem(v.ID, key) - if len(data) > 0 { - blockTax := new(big.Int).SetBytes(data) - totalTax.Add(totalTax, blockTax) - } - } - - return stackitem.NewBigInteger(totalTax) -} - -// getDeductibleExpenses returns total deductible expenses for an account in a block range. -func (v *VTS) getDeductibleExpenses(ic *interop.Context, args []stackitem.Item) stackitem.Item { - account := toUint160(args[0]) - startBlock := uint32(toUint64(args[1])) - endBlock := uint32(toUint64(args[2])) - category := toUint8(args[3]) - - if endBlock < startBlock { - panic("endBlock must be >= startBlock") - } - - totalExpenses := big.NewInt(0) - for block := startBlock; block <= endBlock; block++ { - key := v.makeAccountTxIndexKey(account, block) - indexData := ic.DAO.GetStorageItem(v.ID, key) - - for i := 0; i+4 <= len(indexData); i += 4 { - txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) - recordKey := v.makeTxRecordKey(block, txIndex) - recordData := ic.DAO.GetStorageItem(v.ID, recordKey) - - if len(recordData) > 0 { - item, err := stackitem.Deserialize(recordData) - if err == nil { - var record state.TransactionRecord - if record.FromStackItem(item) == nil { - // Count expenses where this account is the sender - if record.From.Equals(account) && record.TxType == state.TxTypeExpense { - // Filter by category if specified - if category == 0 || record.Category == category { - totalExpenses.Add(totalExpenses, &record.Amount) - } - } - } - } - } - } - } - - return stackitem.NewBigInteger(totalExpenses) -} - -// getTaxSummary returns a tax summary for an account in a block range. -func (v *VTS) getTaxSummary(ic *interop.Context, args []stackitem.Item) stackitem.Item { - account := toUint160(args[0]) - startBlock := uint32(toUint64(args[1])) - endBlock := uint32(toUint64(args[2])) - - if endBlock < startBlock { - panic("endBlock must be >= startBlock") - } - - summary := &state.TaxSummary{ - Account: account, - StartBlock: startBlock, - EndBlock: endBlock, - } - - // Iterate through all transactions in the period - for block := startBlock; block <= endBlock; block++ { - // Get cumulative tax for this block - taxKey := v.makeTaxWithheldKey(account, block) - taxData := ic.DAO.GetStorageItem(v.ID, taxKey) - if len(taxData) > 0 { - blockTax := new(big.Int).SetBytes(taxData) - summary.TaxWithheld.Add(&summary.TaxWithheld, blockTax) - } - - // Get transaction details - key := v.makeAccountTxIndexKey(account, block) - indexData := ic.DAO.GetStorageItem(v.ID, key) - - for i := 0; i+4 <= len(indexData); i += 4 { - txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) - recordKey := v.makeTxRecordKey(block, txIndex) - recordData := ic.DAO.GetStorageItem(v.ID, recordKey) - - if len(recordData) > 0 { - item, err := stackitem.Deserialize(recordData) - if err == nil { - var record state.TransactionRecord - if record.FromStackItem(item) == nil { - if record.To.Equals(account) { - switch record.TxType { - case state.TxTypeIncome: - summary.TotalIncome.Add(&summary.TotalIncome, &record.Amount) - case state.TxTypeBenefit: - summary.TotalBenefits.Add(&summary.TotalBenefits, &record.Amount) - } - } - if record.From.Equals(account) && record.TxType == state.TxTypeExpense { - summary.TotalExpenses.Add(&summary.TotalExpenses, &record.Amount) - // Medical and Education expenses are typically deductible - if record.Category == state.CategoryMedical || record.Category == state.CategoryEducation { - summary.DeductibleExpenses.Add(&summary.DeductibleExpenses, &record.Amount) - } - } - } - } - } - } - } - - // Calculate estimated tax owed (simple flat rate calculation) - taxConfig := v.getTaxConfigInternal(ic.DAO) - if taxConfig.DefaultIncomeRate > 0 { - taxOwed := new(big.Int).Mul(&summary.TotalIncome, big.NewInt(int64(taxConfig.DefaultIncomeRate))) - taxOwed.Div(taxOwed, big.NewInt(10000)) - summary.EstimatedOwed = *taxOwed - } - - // Balance = TaxWithheld - EstimatedOwed (positive = refund due) - summary.Balance.Sub(&summary.TaxWithheld, &summary.EstimatedOwed) - - item, _ := summary.ToStackItem() - return item -} - -// ============ Public Internal Methods for Cross-Contract Use ============ - -// BalanceOf returns VTS balance for the account. -func (v *VTS) BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int { - bal := v.getBalanceInternal(d, acc) - return bal.Total() -} - -// UnrestrictedBalanceOf returns unrestricted VTS balance for the account. -func (v *VTS) UnrestrictedBalanceOf(d *dao.Simple, acc util.Uint160) *big.Int { - bal := v.getBalanceInternal(d, acc) - return &bal.Unrestricted -} - -// Mint mints unrestricted VTS to the account. -func (v *VTS) Mint(ic *interop.Context, to util.Uint160, amount *big.Int) { - v.mintUnrestricted(ic, to, amount) -} - -// MintRestricted mints restricted VTS to the account. -func (v *VTS) MintRestricted(ic *interop.Context, to util.Uint160, amount *big.Int, category uint8) { - v.mintRestrictedInternal(ic, to, amount, category) -} - -// Burn burns unrestricted VTS from the account. -func (v *VTS) Burn(ic *interop.Context, from util.Uint160, amount *big.Int) { - bal := v.getBalanceInternal(ic.DAO, from) - if bal.Unrestricted.Cmp(amount) < 0 { - panic("insufficient funds") - } - bal.Unrestricted.Sub(&bal.Unrestricted, amount) - v.putBalance(ic.DAO, from, bal) - - supply := v.getTotalSupplyInternal(ic.DAO) - supply.Sub(supply, amount) - v.putTotalSupply(ic.DAO, supply) - - v.emitTransfer(ic, &from, nil, amount) -} - -// IsVendor returns true if the address is a registered active vendor. -func (v *VTS) IsVendor(d *dao.Simple, addr util.Uint160) bool { - vendor := v.getVendorInternal(d, addr) - return vendor != nil && vendor.Active -} - -// ============ Helper Functions ============ - -func checkWitness(ic *interop.Context, h util.Uint160) (bool, error) { - return runtime.CheckHashedWitness(ic, h) -} +package native + +import ( + "encoding/binary" + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativeids" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// VTS storage prefixes. +const ( + vtsPrefixAccount = 0x20 // address -> VTSBalance + vtsPrefixVendor = 0x21 // address -> Vendor + vtsPrefixTotalSupply = 0x11 // total supply storage + vtsPrefixTaxConfig = 0x10 // tax configuration + vtsPrefixVendorCount = 0x12 // vendor count + vtsPrefixTxRecord = 0x30 // txHash -> TransactionRecord + vtsPrefixAccountTxs = 0x31 // address + blockHeight -> []txHash index + vtsPrefixTaxWithheld = 0x32 // address + year -> cumulative tax +) + +// VTSFactor is the divisor for VTS (10^8 = 100,000,000). +const VTSFactor = 100000000 + +// VTS represents the VTS (Value Transfer System) native contract. +// VTS is programmable money with spending restrictions and automatic tax accounting. +type VTS struct { + interop.ContractMD + + Tutus ITutus + RoleRegistry IRoleRegistry + Vita IVita + Lex ILex + + symbol string + decimals int64 + factor int64 +} + +// VTSCache holds cached VTS data. +type VTSCache struct { + vendorCount int64 +} + +// Copy implements dao.NativeContractCache. +func (c *VTSCache) Copy() dao.NativeContractCache { + return &VTSCache{ + vendorCount: c.vendorCount, + } +} + +// newVTS creates a new VTS native contract. +func newVTS() *VTS { + v := &VTS{ + symbol: "VTS", + decimals: 8, + factor: VTSFactor, + } + + v.ContractMD = *interop.NewContractMD(nativenames.VTS, nativeids.VTS, func(m *manifest.Manifest, hf config.Hardfork) { + m.SupportedStandards = []string{manifest.NEP17StandardName} + }) + defer v.BuildHFSpecificMD(v.ActiveIn()) + + // NEP-17 Standard Methods + desc := NewDescriptor("symbol", smartcontract.StringType) + md := NewMethodAndPrice(v.symbolMethod, 0, callflag.NoneFlag) + v.AddMethod(md, desc) + + desc = NewDescriptor("decimals", smartcontract.IntegerType) + md = NewMethodAndPrice(v.decimalsMethod, 0, callflag.NoneFlag) + v.AddMethod(md, desc) + + desc = NewDescriptor("totalSupply", smartcontract.IntegerType) + md = NewMethodAndPrice(v.totalSupply, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("balanceOf", smartcontract.IntegerType, + manifest.NewParameter("account", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.balanceOf, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + transferParams := []manifest.Parameter{ + manifest.NewParameter("from", smartcontract.Hash160Type), + manifest.NewParameter("to", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + } + desc = NewDescriptor("transfer", smartcontract.BoolType, + append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))..., + ) + md = NewMethodAndPrice(v.transfer, 1<<17, callflag.States|callflag.AllowCall|callflag.AllowNotify) + md.StorageFee = 50 + v.AddMethod(md, desc) + + // Extended Balance Methods + desc = NewDescriptor("unrestrictedBalanceOf", smartcontract.IntegerType, + manifest.NewParameter("account", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.unrestrictedBalanceOf, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("restrictedBalanceOf", smartcontract.IntegerType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("category", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.restrictedBalanceOf, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("balanceDetails", smartcontract.ArrayType, + manifest.NewParameter("account", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.balanceDetails, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // Minting Methods (Committee-Only) + desc = NewDescriptor("mint", smartcontract.BoolType, + manifest.NewParameter("to", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.mint, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + desc = NewDescriptor("mintRestricted", smartcontract.BoolType, + manifest.NewParameter("to", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("category", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.mintRestricted, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + desc = NewDescriptor("burn", smartcontract.BoolType, + manifest.NewParameter("from", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.burn, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + desc = NewDescriptor("convertToUnrestricted", smartcontract.BoolType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("category", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.convertToUnrestricted, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // Vendor Management Methods (Committee-Only) + desc = NewDescriptor("registerVendor", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("categories", smartcontract.IntegerType), + manifest.NewParameter("ageRestricted", smartcontract.BoolType)) + md = NewMethodAndPrice(v.registerVendor, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + desc = NewDescriptor("updateVendor", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("categories", smartcontract.IntegerType), + manifest.NewParameter("ageRestricted", smartcontract.BoolType)) + md = NewMethodAndPrice(v.updateVendor, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + desc = NewDescriptor("deactivateVendor", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.deactivateVendor, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + desc = NewDescriptor("getVendor", smartcontract.ArrayType, + manifest.NewParameter("address", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.getVendor, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("isVendor", smartcontract.BoolType, + manifest.NewParameter("address", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.isVendor, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("getVendorCategories", smartcontract.IntegerType, + manifest.NewParameter("address", smartcontract.Hash160Type)) + md = NewMethodAndPrice(v.getVendorCategories, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // Spending Methods + desc = NewDescriptor("spend", smartcontract.BoolType, + manifest.NewParameter("from", smartcontract.Hash160Type), + manifest.NewParameter("vendor", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("data", smartcontract.AnyType)) + md = NewMethodAndPrice(v.spend, 1<<17, callflag.States|callflag.AllowCall|callflag.AllowNotify) + md.StorageFee = 50 + v.AddMethod(md, desc) + + desc = NewDescriptor("canSpendAt", smartcontract.BoolType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("vendor", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.canSpendAt, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + // Tax Methods + desc = NewDescriptor("payWage", smartcontract.BoolType, + manifest.NewParameter("employer", smartcontract.Hash160Type), + manifest.NewParameter("employee", smartcontract.Hash160Type), + manifest.NewParameter("grossAmount", smartcontract.IntegerType), + manifest.NewParameter("taxRate", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.payWage, 1<<17, callflag.States|callflag.AllowNotify) + md.StorageFee = 100 + v.AddMethod(md, desc) + + desc = NewDescriptor("setTaxConfig", smartcontract.BoolType, + manifest.NewParameter("incomeRate", smartcontract.IntegerType), + manifest.NewParameter("salesRate", smartcontract.IntegerType), + manifest.NewParameter("treasuryAddress", smartcontract.Hash160Type), + manifest.NewParameter("exemptCategories", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.setTaxConfig, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + desc = NewDescriptor("getTaxConfig", smartcontract.ArrayType) + md = NewMethodAndPrice(v.getTaxConfig, 1<<15, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("issueTaxRefund", smartcontract.BoolType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.issueTaxRefund, 1<<17, callflag.States|callflag.AllowNotify) + v.AddMethod(md, desc) + + // Tax Reporting Methods (read-only) + desc = NewDescriptor("getTransactions", smartcontract.ArrayType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("startBlock", smartcontract.IntegerType), + manifest.NewParameter("endBlock", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.getTransactions, 1<<17, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("getIncomeForPeriod", smartcontract.IntegerType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("startBlock", smartcontract.IntegerType), + manifest.NewParameter("endBlock", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.getIncomeForPeriod, 1<<17, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("getTaxWithheld", smartcontract.IntegerType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("startBlock", smartcontract.IntegerType), + manifest.NewParameter("endBlock", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.getTaxWithheld, 1<<17, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("getDeductibleExpenses", smartcontract.IntegerType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("startBlock", smartcontract.IntegerType), + manifest.NewParameter("endBlock", smartcontract.IntegerType), + manifest.NewParameter("category", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.getDeductibleExpenses, 1<<17, callflag.ReadStates) + v.AddMethod(md, desc) + + desc = NewDescriptor("getTaxSummary", smartcontract.ArrayType, + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("startBlock", smartcontract.IntegerType), + manifest.NewParameter("endBlock", smartcontract.IntegerType)) + md = NewMethodAndPrice(v.getTaxSummary, 1<<17, callflag.ReadStates) + v.AddMethod(md, desc) + + // Events + eDesc := NewEventDescriptor("Transfer", transferParams...) + eMD := NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("Mint", + manifest.NewParameter("to", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("category", smartcontract.IntegerType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("Burn", + manifest.NewParameter("from", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("Spend", + manifest.NewParameter("from", smartcontract.Hash160Type), + manifest.NewParameter("vendor", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType), + manifest.NewParameter("categoriesUsed", smartcontract.IntegerType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("VendorRegistered", + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("categories", smartcontract.IntegerType), + manifest.NewParameter("ageRestricted", smartcontract.BoolType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("VendorUpdated", + manifest.NewParameter("address", smartcontract.Hash160Type), + manifest.NewParameter("name", smartcontract.StringType), + manifest.NewParameter("categories", smartcontract.IntegerType), + manifest.NewParameter("ageRestricted", smartcontract.BoolType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("VendorDeactivated", + manifest.NewParameter("address", smartcontract.Hash160Type)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("TaxWithheld", + manifest.NewParameter("from", smartcontract.Hash160Type), + manifest.NewParameter("to", smartcontract.Hash160Type), + manifest.NewParameter("grossAmount", smartcontract.IntegerType), + manifest.NewParameter("taxAmount", smartcontract.IntegerType), + manifest.NewParameter("taxRate", smartcontract.IntegerType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("TaxRefunded", + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("amount", smartcontract.IntegerType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + eDesc = NewEventDescriptor("ConvertedToUnrestricted", + manifest.NewParameter("account", smartcontract.Hash160Type), + manifest.NewParameter("category", smartcontract.IntegerType), + manifest.NewParameter("amount", smartcontract.IntegerType)) + eMD = NewEvent(eDesc) + v.AddEvent(eMD) + + return v +} + +// Metadata returns VTS contract metadata. +func (v *VTS) Metadata() *interop.ContractMD { + return &v.ContractMD +} + +// Initialize initializes VTS contract at genesis. +func (v *VTS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { + if hf != v.ActiveIn() { + return nil + } + + // Initialize with zero total supply (VTS is minted on-demand) + v.putTotalSupply(ic.DAO, big.NewInt(0)) + + // Initialize vendor count + v.putVendorCount(ic.DAO, 0) + + // Initialize default tax config (no taxes by default) + cfg := &state.TaxConfig{ + DefaultIncomeRate: 0, + DefaultSalesRate: 0, + TreasuryAddress: util.Uint160{}, + ExemptCategories: 0, + } + v.putTaxConfig(ic.DAO, cfg) + + return nil +} + +// InitializeCache implements the Contract interface. +func (v *VTS) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error { + cache := &VTSCache{ + vendorCount: v.getVendorCountInternal(d), + } + d.SetCache(v.ID, cache) + return nil +} + +// OnPersist implements the Contract interface. +func (v *VTS) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements the Contract interface. +func (v *VTS) PostPersist(ic *interop.Context) error { + return nil +} + +// ActiveIn implements the Contract interface. +func (v *VTS) ActiveIn() *config.Hardfork { + return nil +} + +// checkCommittee verifies the caller has committee privileges. +func (v *VTS) checkCommittee(ic *interop.Context) bool { + if v.RoleRegistry != nil { + return v.RoleRegistry.CheckCommittee(ic) + } + if v.Tutus != nil { + return v.Tutus.CheckCommittee(ic) + } + return false +} + +// ============ Storage Key Helpers ============ + +func (v *VTS) makeAccountKey(h util.Uint160) []byte { + return makeUint160Key(vtsPrefixAccount, h) +} + +func (v *VTS) makeVendorKey(h util.Uint160) []byte { + return makeUint160Key(vtsPrefixVendor, h) +} + +var vtsTotalSupplyKey = []byte{vtsPrefixTotalSupply} +var vtsVendorCountKey = []byte{vtsPrefixVendorCount} +var vtsTaxConfigKey = []byte{vtsPrefixTaxConfig} + +// ============ NEP-17 Standard Methods ============ + +func (v *VTS) symbolMethod(_ *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewByteArray([]byte(v.symbol)) +} + +func (v *VTS) decimalsMethod(_ *interop.Context, _ []stackitem.Item) stackitem.Item { + return stackitem.NewBigInteger(big.NewInt(v.decimals)) +} + +func (v *VTS) totalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + supply := v.getTotalSupplyInternal(ic.DAO) + return stackitem.NewBigInteger(supply) +} + +func (v *VTS) getTotalSupplyInternal(d *dao.Simple) *big.Int { + si := d.GetStorageItem(v.ID, vtsTotalSupplyKey) + if si == nil { + return big.NewInt(0) + } + bal, err := state.NEP17BalanceFromBytes(si) + if err != nil { + return big.NewInt(0) + } + return &bal.Balance +} + +func (v *VTS) putTotalSupply(d *dao.Simple, supply *big.Int) { + bal := &state.NEP17Balance{Balance: *supply} + d.PutStorageItem(v.ID, vtsTotalSupplyKey, bal.Bytes(nil)) +} + +func (v *VTS) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { + h := toUint160(args[0]) + balance := v.getBalanceInternal(ic.DAO, h) + return stackitem.NewBigInteger(balance.Total()) +} + +func (v *VTS) getBalanceInternal(d *dao.Simple, h util.Uint160) *state.VTSBalance { + key := v.makeAccountKey(h) + si := d.GetStorageItem(v.ID, key) + if si == nil { + return state.NewVTSBalance() + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return state.NewVTSBalance() + } + + bal := state.NewVTSBalance() + if err := bal.FromStackItem(item); err != nil { + return state.NewVTSBalance() + } + return bal +} + +func (v *VTS) putBalance(d *dao.Simple, h util.Uint160, bal *state.VTSBalance) error { + key := v.makeAccountKey(h) + + // If balance is zero, delete the entry + if bal.Total().Sign() == 0 { + d.DeleteStorageItem(v.ID, key) + return nil + } + + item, err := bal.ToStackItem() + if err != nil { + return err + } + data, err := stackitem.Serialize(item) + if err != nil { + return err + } + d.PutStorageItem(v.ID, key, data) + return nil +} + +func (v *VTS) transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item { + from := toUint160(args[0]) + to := toUint160(args[1]) + amount := toBigInt(args[2]) + + // Transfer only unrestricted balance + err := v.transferUnrestricted(ic, from, to, amount, args[3]) + return stackitem.NewBool(err == nil) +} + +func (v *VTS) transferUnrestricted(ic *interop.Context, from, to util.Uint160, amount *big.Int, data stackitem.Item) error { + if amount.Sign() < 0 { + return errors.New("negative amount") + } + + // Check witness + caller := ic.VM.GetCallingScriptHash() + if !from.Equals(caller) { + ok, err := checkWitness(ic, from) + if err != nil || !ok { + return errors.New("invalid signature") + } + } + + // Check property rights via Lex (if Lex is wired) + if v.Lex != nil && !v.Lex.CheckPropertyRight(ic.DAO, from, ic.Block.Index) { + return errors.New("property rights restricted") + } + + if amount.Sign() == 0 { + v.emitTransfer(ic, &from, &to, amount) + return nil + } + + // Get sender balance + fromBal := v.getBalanceInternal(ic.DAO, from) + if fromBal.Unrestricted.Cmp(amount) < 0 { + return errors.New("insufficient unrestricted funds") + } + + // Update sender balance + fromBal.Unrestricted.Sub(&fromBal.Unrestricted, amount) + if err := v.putBalance(ic.DAO, from, fromBal); err != nil { + return err + } + + // Update recipient balance + toBal := v.getBalanceInternal(ic.DAO, to) + toBal.Unrestricted.Add(&toBal.Unrestricted, amount) + if err := v.putBalance(ic.DAO, to, toBal); err != nil { + return err + } + + v.emitTransfer(ic, &from, &to, amount) + return nil +} + +func (v *VTS) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) { + ic.AddNotification(v.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{ + addrToStackItem(from), + addrToStackItem(to), + stackitem.NewBigInteger(amount), + })) +} + +// ============ Extended Balance Methods ============ + +func (v *VTS) unrestrictedBalanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { + h := toUint160(args[0]) + bal := v.getBalanceInternal(ic.DAO, h) + return stackitem.NewBigInteger(&bal.Unrestricted) +} + +func (v *VTS) restrictedBalanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { + h := toUint160(args[0]) + category := toUint8(args[1]) + bal := v.getBalanceInternal(ic.DAO, h) + + if amt, ok := bal.Restricted[category]; ok { + return stackitem.NewBigInteger(amt) + } + return stackitem.NewBigInteger(big.NewInt(0)) +} + +func (v *VTS) balanceDetails(ic *interop.Context, args []stackitem.Item) stackitem.Item { + h := toUint160(args[0]) + bal := v.getBalanceInternal(ic.DAO, h) + + // Return: [unrestricted, [[category, amount], ...]] + restricted := make([]stackitem.Item, 0, len(bal.Restricted)) + for cat, amt := range bal.Restricted { + if amt.Sign() > 0 { + restricted = append(restricted, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(cat))), + stackitem.NewBigInteger(amt), + })) + } + } + + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(&bal.Unrestricted), + stackitem.NewArray(restricted), + }) +} + +// ============ Minting Methods ============ + +func (v *VTS) mint(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + to := toUint160(args[0]) + amount := toBigInt(args[1]) + + if amount.Sign() <= 0 { + panic("amount must be positive") + } + + v.mintUnrestricted(ic, to, amount) + return stackitem.NewBool(true) +} + +func (v *VTS) mintUnrestricted(ic *interop.Context, to util.Uint160, amount *big.Int) { + bal := v.getBalanceInternal(ic.DAO, to) + bal.Unrestricted.Add(&bal.Unrestricted, amount) + v.putBalance(ic.DAO, to, bal) + + // Update total supply + supply := v.getTotalSupplyInternal(ic.DAO) + supply.Add(supply, amount) + v.putTotalSupply(ic.DAO, supply) + + v.emitTransfer(ic, nil, &to, amount) + ic.AddNotification(v.Hash, "Mint", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(to.BytesBE()), + stackitem.NewBigInteger(amount), + stackitem.NewBigInteger(big.NewInt(0)), // Category 0 = unrestricted + })) +} + +func (v *VTS) mintRestricted(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + to := toUint160(args[0]) + amount := toBigInt(args[1]) + category := toUint8(args[2]) + + if amount.Sign() <= 0 { + panic("amount must be positive") + } + if category == state.CategoryUnrestricted { + panic("use mint() for unrestricted tokens") + } + + // Restricted VTS requires recipient to have a Vita + // This ensures benefits go to verified identities + if !v.Vita.TokenExists(ic.DAO, to) { + panic("restricted VTS requires Vita") + } + + v.mintRestrictedInternal(ic, to, amount, category) + return stackitem.NewBool(true) +} + +func (v *VTS) mintRestrictedInternal(ic *interop.Context, to util.Uint160, amount *big.Int, category uint8) { + bal := v.getBalanceInternal(ic.DAO, to) + if bal.Restricted == nil { + bal.Restricted = make(map[uint8]*big.Int) + } + if bal.Restricted[category] == nil { + bal.Restricted[category] = big.NewInt(0) + } + bal.Restricted[category].Add(bal.Restricted[category], amount) + v.putBalance(ic.DAO, to, bal) + + // Update total supply + supply := v.getTotalSupplyInternal(ic.DAO) + supply.Add(supply, amount) + v.putTotalSupply(ic.DAO, supply) + + v.emitTransfer(ic, nil, &to, amount) + ic.AddNotification(v.Hash, "Mint", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(to.BytesBE()), + stackitem.NewBigInteger(amount), + stackitem.NewBigInteger(big.NewInt(int64(category))), + })) +} + +func (v *VTS) burn(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + from := toUint160(args[0]) + amount := toBigInt(args[1]) + + if amount.Sign() <= 0 { + panic("amount must be positive") + } + + bal := v.getBalanceInternal(ic.DAO, from) + if bal.Unrestricted.Cmp(amount) < 0 { + panic("insufficient unrestricted funds") + } + + bal.Unrestricted.Sub(&bal.Unrestricted, amount) + v.putBalance(ic.DAO, from, bal) + + // Update total supply + supply := v.getTotalSupplyInternal(ic.DAO) + supply.Sub(supply, amount) + v.putTotalSupply(ic.DAO, supply) + + v.emitTransfer(ic, &from, nil, amount) + ic.AddNotification(v.Hash, "Burn", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(from.BytesBE()), + stackitem.NewBigInteger(amount), + })) + + return stackitem.NewBool(true) +} + +func (v *VTS) convertToUnrestricted(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + account := toUint160(args[0]) + category := toUint8(args[1]) + amount := toBigInt(args[2]) + + if amount.Sign() <= 0 { + panic("amount must be positive") + } + + bal := v.getBalanceInternal(ic.DAO, account) + if bal.Restricted[category] == nil || bal.Restricted[category].Cmp(amount) < 0 { + panic("insufficient restricted funds in category") + } + + // Move from restricted to unrestricted + bal.Restricted[category].Sub(bal.Restricted[category], amount) + if bal.Restricted[category].Sign() == 0 { + delete(bal.Restricted, category) + } + bal.Unrestricted.Add(&bal.Unrestricted, amount) + + v.putBalance(ic.DAO, account, bal) + + ic.AddNotification(v.Hash, "ConvertedToUnrestricted", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(account.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(category))), + stackitem.NewBigInteger(amount), + })) + + return stackitem.NewBool(true) +} + +// ============ Vendor Management ============ + +func (v *VTS) registerVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + address := toUint160(args[0]) + name := toString(args[1]) + categories := toUint8(args[2]) + ageRestricted := toBool(args[3]) + + if len(name) == 0 || len(name) > 64 { + panic("invalid vendor name") + } + + // Check if vendor already exists + if v.getVendorInternal(ic.DAO, address) != nil { + panic("vendor already registered") + } + + vendor := &state.Vendor{ + Address: address, + Name: name, + Categories: categories, + RegisteredAt: ic.Block.Index, + RegisteredBy: ic.VM.GetCallingScriptHash(), + Active: true, + AgeRestricted: ageRestricted, + } + + v.putVendor(ic.DAO, vendor) + + // Increment vendor count + count := v.getVendorCountInternal(ic.DAO) + v.putVendorCount(ic.DAO, count+1) + + ic.AddNotification(v.Hash, "VendorRegistered", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(address.BytesBE()), + stackitem.NewByteArray([]byte(name)), + stackitem.NewBigInteger(big.NewInt(int64(categories))), + stackitem.NewBool(ageRestricted), + })) + + return stackitem.NewBool(true) +} + +func (v *VTS) updateVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + address := toUint160(args[0]) + name := toString(args[1]) + categories := toUint8(args[2]) + ageRestricted := toBool(args[3]) + + if len(name) == 0 || len(name) > 64 { + panic("invalid vendor name") + } + + vendor := v.getVendorInternal(ic.DAO, address) + if vendor == nil { + panic("vendor not found") + } + + vendor.Name = name + vendor.Categories = categories + vendor.AgeRestricted = ageRestricted + v.putVendor(ic.DAO, vendor) + + ic.AddNotification(v.Hash, "VendorUpdated", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(address.BytesBE()), + stackitem.NewByteArray([]byte(name)), + stackitem.NewBigInteger(big.NewInt(int64(categories))), + stackitem.NewBool(ageRestricted), + })) + + return stackitem.NewBool(true) +} + +func (v *VTS) deactivateVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + address := toUint160(args[0]) + + vendor := v.getVendorInternal(ic.DAO, address) + if vendor == nil { + panic("vendor not found") + } + + vendor.Active = false + v.putVendor(ic.DAO, vendor) + + ic.AddNotification(v.Hash, "VendorDeactivated", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(address.BytesBE()), + })) + + return stackitem.NewBool(true) +} + +func (v *VTS) getVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + vendor := v.getVendorInternal(ic.DAO, address) + if vendor == nil { + return stackitem.Null{} + } + + item, _ := vendor.ToStackItem() + return item +} + +func (v *VTS) getVendorInternal(d *dao.Simple, address util.Uint160) *state.Vendor { + key := v.makeVendorKey(address) + si := d.GetStorageItem(v.ID, key) + if si == nil { + return nil + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return nil + } + + vendor := &state.Vendor{} + if err := vendor.FromStackItem(item); err != nil { + return nil + } + return vendor +} + +func (v *VTS) putVendor(d *dao.Simple, vendor *state.Vendor) { + key := v.makeVendorKey(vendor.Address) + item, _ := vendor.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(v.ID, key, data) +} + +func (v *VTS) isVendor(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + vendor := v.getVendorInternal(ic.DAO, address) + return stackitem.NewBool(vendor != nil && vendor.Active) +} + +func (v *VTS) getVendorCategories(ic *interop.Context, args []stackitem.Item) stackitem.Item { + address := toUint160(args[0]) + vendor := v.getVendorInternal(ic.DAO, address) + if vendor == nil { + return stackitem.NewBigInteger(big.NewInt(0)) + } + return stackitem.NewBigInteger(big.NewInt(int64(vendor.Categories))) +} + +func (v *VTS) getVendorCountInternal(d *dao.Simple) int64 { + si := d.GetStorageItem(v.ID, vtsVendorCountKey) + if si == nil { + return 0 + } + return int64(binary.BigEndian.Uint64(si)) +} + +func (v *VTS) putVendorCount(d *dao.Simple, count int64) { + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, uint64(count)) + d.PutStorageItem(v.ID, vtsVendorCountKey, data) +} + +// ============ Spending Logic ============ + +func (v *VTS) spend(ic *interop.Context, args []stackitem.Item) stackitem.Item { + from := toUint160(args[0]) + vendor := toUint160(args[1]) + amount := toBigInt(args[2]) + data := args[3] + + if amount.Sign() <= 0 { + panic("amount must be positive") + } + + // Check witness for sender + caller := ic.VM.GetCallingScriptHash() + if !from.Equals(caller) { + ok, err := checkWitness(ic, from) + if err != nil || !ok { + panic("invalid signature") + } + } + + // Check property rights via Lex (if Lex is wired) + if v.Lex != nil && !v.Lex.CheckPropertyRight(ic.DAO, from, ic.Block.Index) { + panic("property rights restricted") + } + + // Verify vendor is registered and active + vendorInfo := v.getVendorInternal(ic.DAO, vendor) + if vendorInfo == nil || !vendorInfo.Active { + panic("invalid or inactive vendor") + } + + // Check age verification for age-restricted vendors (e.g., alcohol, tobacco) + if vendorInfo.AgeRestricted { + if !v.Vita.IsAdultVerified(ic.DAO, from) { + panic("age verification required for this vendor") + } + } + + // Get sender's balance + fromBal := v.getBalanceInternal(ic.DAO, from) + + // Try to spend from matching restricted categories first + remaining := new(big.Int).Set(amount) + var categoriesUsed uint8 = 0 + + for category, catBal := range fromBal.Restricted { + if remaining.Sign() <= 0 { + break + } + // Check if vendor accepts this category + if vendorInfo.Categories&category != 0 { + toSpend := new(big.Int) + if catBal.Cmp(remaining) >= 0 { + toSpend.Set(remaining) + } else { + toSpend.Set(catBal) + } + catBal.Sub(catBal, toSpend) + if catBal.Sign() == 0 { + delete(fromBal.Restricted, category) + } + remaining.Sub(remaining, toSpend) + categoriesUsed |= category + } + } + + // If still remaining, use unrestricted balance + if remaining.Sign() > 0 { + if fromBal.Unrestricted.Cmp(remaining) < 0 { + panic("insufficient funds") + } + fromBal.Unrestricted.Sub(&fromBal.Unrestricted, remaining) + } + + // Save sender balance + v.putBalance(ic.DAO, from, fromBal) + + // Credit vendor with unrestricted balance + vendorBal := v.getBalanceInternal(ic.DAO, vendor) + vendorBal.Unrestricted.Add(&vendorBal.Unrestricted, amount) + v.putBalance(ic.DAO, vendor, vendorBal) + + // Emit events + v.emitTransfer(ic, &from, &vendor, amount) + ic.AddNotification(v.Hash, "Spend", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(from.BytesBE()), + stackitem.NewByteArray(vendor.BytesBE()), + stackitem.NewBigInteger(amount), + stackitem.NewBigInteger(big.NewInt(int64(categoriesUsed))), + })) + + _ = data // data parameter for compatibility + return stackitem.NewBool(true) +} + +func (v *VTS) canSpendAt(ic *interop.Context, args []stackitem.Item) stackitem.Item { + account := toUint160(args[0]) + vendor := toUint160(args[1]) + amount := toBigInt(args[2]) + + if amount.Sign() <= 0 { + return stackitem.NewBool(false) + } + + // Check vendor + vendorInfo := v.getVendorInternal(ic.DAO, vendor) + if vendorInfo == nil || !vendorInfo.Active { + return stackitem.NewBool(false) + } + + // Check age verification for age-restricted vendors + if vendorInfo.AgeRestricted { + if !v.Vita.IsAdultVerified(ic.DAO, account) { + return stackitem.NewBool(false) + } + } + + // Get balance + bal := v.getBalanceInternal(ic.DAO, account) + + // Calculate available funds + available := new(big.Int).Set(&bal.Unrestricted) + for category, catBal := range bal.Restricted { + if vendorInfo.Categories&category != 0 { + available.Add(available, catBal) + } + } + + return stackitem.NewBool(available.Cmp(amount) >= 0) +} + +// ============ Tax Accounting ============ + +func (v *VTS) payWage(ic *interop.Context, args []stackitem.Item) stackitem.Item { + employer := toUint160(args[0]) + employee := toUint160(args[1]) + grossAmount := toBigInt(args[2]) + taxRate := toUint64(args[3]) // basis points (e.g., 2500 = 25%) + + if grossAmount.Sign() <= 0 { + panic("gross amount must be positive") + } + if taxRate > 10000 { + panic("tax rate cannot exceed 100%") + } + + // Check witness for employer + caller := ic.VM.GetCallingScriptHash() + if !employer.Equals(caller) { + ok, err := checkWitness(ic, employer) + if err != nil || !ok { + panic("invalid signature") + } + } + + // Get employer balance + employerBal := v.getBalanceInternal(ic.DAO, employer) + if employerBal.Unrestricted.Cmp(grossAmount) < 0 { + panic("insufficient funds") + } + + // Calculate tax and net amounts + taxAmount := new(big.Int).Mul(grossAmount, big.NewInt(int64(taxRate))) + taxAmount.Div(taxAmount, big.NewInt(10000)) + netAmount := new(big.Int).Sub(grossAmount, taxAmount) + + // Get tax config for treasury address + taxConfig := v.getTaxConfigInternal(ic.DAO) + + // Deduct from employer + employerBal.Unrestricted.Sub(&employerBal.Unrestricted, grossAmount) + v.putBalance(ic.DAO, employer, employerBal) + + // Credit employee (net) + employeeBal := v.getBalanceInternal(ic.DAO, employee) + employeeBal.Unrestricted.Add(&employeeBal.Unrestricted, netAmount) + v.putBalance(ic.DAO, employee, employeeBal) + + // Credit treasury (tax) + if taxAmount.Sign() > 0 && !taxConfig.TreasuryAddress.Equals(util.Uint160{}) { + treasuryBal := v.getBalanceInternal(ic.DAO, taxConfig.TreasuryAddress) + treasuryBal.Unrestricted.Add(&treasuryBal.Unrestricted, taxAmount) + v.putBalance(ic.DAO, taxConfig.TreasuryAddress, treasuryBal) + } + + // Store transaction record for tax reporting + record := &state.TransactionRecord{ + TxHash: ic.Tx.Hash(), + BlockHeight: ic.BlockHeight(), + From: employer, + To: employee, + Amount: *grossAmount, + TxType: state.TxTypeIncome, + Category: state.CategoryUnrestricted, + TaxWithheld: *taxAmount, + TaxRate: uint16(taxRate), + } + v.storeTransactionRecord(ic.DAO, record) + + // Emit events + v.emitTransfer(ic, &employer, &employee, netAmount) + if taxAmount.Sign() > 0 && !taxConfig.TreasuryAddress.Equals(util.Uint160{}) { + v.emitTransfer(ic, &employer, &taxConfig.TreasuryAddress, taxAmount) + } + ic.AddNotification(v.Hash, "TaxWithheld", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(employer.BytesBE()), + stackitem.NewByteArray(employee.BytesBE()), + stackitem.NewBigInteger(grossAmount), + stackitem.NewBigInteger(taxAmount), + stackitem.NewBigInteger(big.NewInt(int64(taxRate))), + })) + + return stackitem.NewBool(true) +} + +func (v *VTS) setTaxConfig(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + incomeRate := uint16(toUint64(args[0])) + salesRate := uint16(toUint64(args[1])) + treasuryAddress := toUint160(args[2]) + exemptCategories := toUint8(args[3]) + + if incomeRate > 10000 || salesRate > 10000 { + panic("rate cannot exceed 100%") + } + + cfg := &state.TaxConfig{ + DefaultIncomeRate: incomeRate, + DefaultSalesRate: salesRate, + TreasuryAddress: treasuryAddress, + ExemptCategories: exemptCategories, + } + v.putTaxConfig(ic.DAO, cfg) + + return stackitem.NewBool(true) +} + +func (v *VTS) getTaxConfig(ic *interop.Context, _ []stackitem.Item) stackitem.Item { + cfg := v.getTaxConfigInternal(ic.DAO) + item, _ := cfg.ToStackItem() + return item +} + +func (v *VTS) getTaxConfigInternal(d *dao.Simple) *state.TaxConfig { + si := d.GetStorageItem(v.ID, vtsTaxConfigKey) + if si == nil { + return &state.TaxConfig{} + } + + item, err := stackitem.Deserialize(si) + if err != nil { + return &state.TaxConfig{} + } + + cfg := &state.TaxConfig{} + if err := cfg.FromStackItem(item); err != nil { + return &state.TaxConfig{} + } + return cfg +} + +func (v *VTS) putTaxConfig(d *dao.Simple, cfg *state.TaxConfig) { + item, _ := cfg.ToStackItem() + data, _ := stackitem.Serialize(item) + d.PutStorageItem(v.ID, vtsTaxConfigKey, data) +} + +func (v *VTS) issueTaxRefund(ic *interop.Context, args []stackitem.Item) stackitem.Item { + if !v.checkCommittee(ic) { + panic("caller is not a committee member") + } + + account := toUint160(args[0]) + amount := toBigInt(args[1]) + + if amount.Sign() <= 0 { + panic("amount must be positive") + } + + // Mint new VTS as refund (comes from treasury/system) + v.mintUnrestricted(ic, account, amount) + + ic.AddNotification(v.Hash, "TaxRefunded", stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(account.BytesBE()), + stackitem.NewBigInteger(amount), + })) + + return stackitem.NewBool(true) +} + +// ============ Tax Reporting Methods ============ + +// makeTxRecordKey creates a storage key for a transaction record. +func (v *VTS) makeTxRecordKey(blockHeight uint32, txIndex uint32) []byte { + key := make([]byte, 9) + key[0] = vtsPrefixTxRecord + binary.BigEndian.PutUint32(key[1:5], blockHeight) + binary.BigEndian.PutUint32(key[5:9], txIndex) + return key +} + +// makeAccountTxIndexKey creates a storage key for an account's transaction index. +func (v *VTS) makeAccountTxIndexKey(account util.Uint160, blockHeight uint32) []byte { + key := make([]byte, 25) + key[0] = vtsPrefixAccountTxs + copy(key[1:21], account.BytesBE()) + binary.BigEndian.PutUint32(key[21:25], blockHeight) + return key +} + +// makeTaxWithheldKey creates a storage key for cumulative tax withheld. +func (v *VTS) makeTaxWithheldKey(account util.Uint160, blockHeight uint32) []byte { + key := make([]byte, 25) + key[0] = vtsPrefixTaxWithheld + copy(key[1:21], account.BytesBE()) + binary.BigEndian.PutUint32(key[21:25], blockHeight) + return key +} + +// storeTransactionRecord stores a transaction record and updates indexes. +func (v *VTS) storeTransactionRecord(d *dao.Simple, record *state.TransactionRecord) { + // Get current tx index for this block + txIndex := v.getNextTxIndex(d, record.BlockHeight) + + // Store the record + key := v.makeTxRecordKey(record.BlockHeight, txIndex) + item, _ := record.ToStackItem() + data, err := stackitem.Serialize(item) + if err != nil { + panic("failed to serialize transaction record") + } + d.PutStorageItem(v.ID, key, data) + + // Update account indexes for both from and to + v.addAccountTxIndex(d, record.From, record.BlockHeight, txIndex) + if !record.To.Equals(record.From) { + v.addAccountTxIndex(d, record.To, record.BlockHeight, txIndex) + } + + // Update cumulative tax withheld for recipient (income tax) + if record.TxType == state.TxTypeIncome && record.TaxWithheld.Sign() > 0 { + v.addCumulativeTaxWithheld(d, record.To, record.BlockHeight, &record.TaxWithheld) + } + + // Increment tx count for this block + v.putTxCount(d, record.BlockHeight, txIndex+1) +} + +// getNextTxIndex returns the next transaction index for a block. +func (v *VTS) getNextTxIndex(d *dao.Simple, blockHeight uint32) uint32 { + return v.getTxCount(d, blockHeight) +} + +// makeTxCountKey creates a storage key for transaction count per block. +func (v *VTS) makeTxCountKey(blockHeight uint32) []byte { + key := make([]byte, 5) + key[0] = vtsPrefixTxRecord + binary.BigEndian.PutUint32(key[1:5], blockHeight) + return key +} + +// getTxCount returns the transaction count for a block. +func (v *VTS) getTxCount(d *dao.Simple, blockHeight uint32) uint32 { + key := v.makeTxCountKey(blockHeight) + si := d.GetStorageItem(v.ID, key) + if si == nil || len(si) < 4 { + return 0 + } + return binary.BigEndian.Uint32(si) +} + +// putTxCount stores the transaction count for a block. +func (v *VTS) putTxCount(d *dao.Simple, blockHeight uint32, count uint32) { + key := v.makeTxCountKey(blockHeight) + data := make([]byte, 4) + binary.BigEndian.PutUint32(data, count) + d.PutStorageItem(v.ID, key, data) +} + +// addAccountTxIndex adds a transaction index to an account's list. +func (v *VTS) addAccountTxIndex(d *dao.Simple, account util.Uint160, blockHeight uint32, txIndex uint32) { + key := v.makeAccountTxIndexKey(account, blockHeight) + existing := d.GetStorageItem(v.ID, key) + + // Append new index + newData := make([]byte, len(existing)+4) + copy(newData, existing) + binary.BigEndian.PutUint32(newData[len(existing):], txIndex) + d.PutStorageItem(v.ID, key, newData) +} + +// addCumulativeTaxWithheld adds to the cumulative tax withheld for an account. +func (v *VTS) addCumulativeTaxWithheld(d *dao.Simple, account util.Uint160, blockHeight uint32, amount *big.Int) { + key := v.makeTaxWithheldKey(account, blockHeight) + existing := d.GetStorageItem(v.ID, key) + + cumulative := big.NewInt(0) + if len(existing) > 0 { + cumulative.SetBytes(existing) + } + cumulative.Add(cumulative, amount) + + d.PutStorageItem(v.ID, key, cumulative.Bytes()) +} + +// getTransactions returns transactions for an account in a block range. +func (v *VTS) getTransactions(ic *interop.Context, args []stackitem.Item) stackitem.Item { + account := toUint160(args[0]) + startBlock := uint32(toUint64(args[1])) + endBlock := uint32(toUint64(args[2])) + + if endBlock < startBlock { + panic("endBlock must be >= startBlock") + } + + // Limit range to prevent DoS + maxRange := uint32(10000) + if endBlock-startBlock > maxRange { + panic("block range too large") + } + + var records []stackitem.Item + for block := startBlock; block <= endBlock; block++ { + // Get transaction indexes for this account at this block + key := v.makeAccountTxIndexKey(account, block) + indexData := ic.DAO.GetStorageItem(v.ID, key) + + // Parse indexes + for i := 0; i+4 <= len(indexData); i += 4 { + txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) + + // Get the transaction record + recordKey := v.makeTxRecordKey(block, txIndex) + recordData := ic.DAO.GetStorageItem(v.ID, recordKey) + if len(recordData) > 0 { + item, err := stackitem.Deserialize(recordData) + if err == nil { + records = append(records, item) + } + } + } + } + + return stackitem.NewArray(records) +} + +// getIncomeForPeriod returns total taxable income for an account in a block range. +func (v *VTS) getIncomeForPeriod(ic *interop.Context, args []stackitem.Item) stackitem.Item { + account := toUint160(args[0]) + startBlock := uint32(toUint64(args[1])) + endBlock := uint32(toUint64(args[2])) + + if endBlock < startBlock { + panic("endBlock must be >= startBlock") + } + + totalIncome := big.NewInt(0) + for block := startBlock; block <= endBlock; block++ { + key := v.makeAccountTxIndexKey(account, block) + indexData := ic.DAO.GetStorageItem(v.ID, key) + + for i := 0; i+4 <= len(indexData); i += 4 { + txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) + recordKey := v.makeTxRecordKey(block, txIndex) + recordData := ic.DAO.GetStorageItem(v.ID, recordKey) + + if len(recordData) > 0 { + item, err := stackitem.Deserialize(recordData) + if err == nil { + var record state.TransactionRecord + if record.FromStackItem(item) == nil { + // Only count income where this account is the recipient + if record.To.Equals(account) && record.TxType == state.TxTypeIncome { + totalIncome.Add(totalIncome, &record.Amount) + } + } + } + } + } + } + + return stackitem.NewBigInteger(totalIncome) +} + +// getTaxWithheld returns total tax withheld for an account in a block range. +func (v *VTS) getTaxWithheld(ic *interop.Context, args []stackitem.Item) stackitem.Item { + account := toUint160(args[0]) + startBlock := uint32(toUint64(args[1])) + endBlock := uint32(toUint64(args[2])) + + if endBlock < startBlock { + panic("endBlock must be >= startBlock") + } + + totalTax := big.NewInt(0) + for block := startBlock; block <= endBlock; block++ { + key := v.makeTaxWithheldKey(account, block) + data := ic.DAO.GetStorageItem(v.ID, key) + if len(data) > 0 { + blockTax := new(big.Int).SetBytes(data) + totalTax.Add(totalTax, blockTax) + } + } + + return stackitem.NewBigInteger(totalTax) +} + +// getDeductibleExpenses returns total deductible expenses for an account in a block range. +func (v *VTS) getDeductibleExpenses(ic *interop.Context, args []stackitem.Item) stackitem.Item { + account := toUint160(args[0]) + startBlock := uint32(toUint64(args[1])) + endBlock := uint32(toUint64(args[2])) + category := toUint8(args[3]) + + if endBlock < startBlock { + panic("endBlock must be >= startBlock") + } + + totalExpenses := big.NewInt(0) + for block := startBlock; block <= endBlock; block++ { + key := v.makeAccountTxIndexKey(account, block) + indexData := ic.DAO.GetStorageItem(v.ID, key) + + for i := 0; i+4 <= len(indexData); i += 4 { + txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) + recordKey := v.makeTxRecordKey(block, txIndex) + recordData := ic.DAO.GetStorageItem(v.ID, recordKey) + + if len(recordData) > 0 { + item, err := stackitem.Deserialize(recordData) + if err == nil { + var record state.TransactionRecord + if record.FromStackItem(item) == nil { + // Count expenses where this account is the sender + if record.From.Equals(account) && record.TxType == state.TxTypeExpense { + // Filter by category if specified + if category == 0 || record.Category == category { + totalExpenses.Add(totalExpenses, &record.Amount) + } + } + } + } + } + } + } + + return stackitem.NewBigInteger(totalExpenses) +} + +// getTaxSummary returns a tax summary for an account in a block range. +func (v *VTS) getTaxSummary(ic *interop.Context, args []stackitem.Item) stackitem.Item { + account := toUint160(args[0]) + startBlock := uint32(toUint64(args[1])) + endBlock := uint32(toUint64(args[2])) + + if endBlock < startBlock { + panic("endBlock must be >= startBlock") + } + + summary := &state.TaxSummary{ + Account: account, + StartBlock: startBlock, + EndBlock: endBlock, + } + + // Iterate through all transactions in the period + for block := startBlock; block <= endBlock; block++ { + // Get cumulative tax for this block + taxKey := v.makeTaxWithheldKey(account, block) + taxData := ic.DAO.GetStorageItem(v.ID, taxKey) + if len(taxData) > 0 { + blockTax := new(big.Int).SetBytes(taxData) + summary.TaxWithheld.Add(&summary.TaxWithheld, blockTax) + } + + // Get transaction details + key := v.makeAccountTxIndexKey(account, block) + indexData := ic.DAO.GetStorageItem(v.ID, key) + + for i := 0; i+4 <= len(indexData); i += 4 { + txIndex := binary.BigEndian.Uint32(indexData[i : i+4]) + recordKey := v.makeTxRecordKey(block, txIndex) + recordData := ic.DAO.GetStorageItem(v.ID, recordKey) + + if len(recordData) > 0 { + item, err := stackitem.Deserialize(recordData) + if err == nil { + var record state.TransactionRecord + if record.FromStackItem(item) == nil { + if record.To.Equals(account) { + switch record.TxType { + case state.TxTypeIncome: + summary.TotalIncome.Add(&summary.TotalIncome, &record.Amount) + case state.TxTypeBenefit: + summary.TotalBenefits.Add(&summary.TotalBenefits, &record.Amount) + } + } + if record.From.Equals(account) && record.TxType == state.TxTypeExpense { + summary.TotalExpenses.Add(&summary.TotalExpenses, &record.Amount) + // Medical and Education expenses are typically deductible + if record.Category == state.CategoryMedical || record.Category == state.CategoryEducation { + summary.DeductibleExpenses.Add(&summary.DeductibleExpenses, &record.Amount) + } + } + } + } + } + } + } + + // Calculate estimated tax owed (simple flat rate calculation) + taxConfig := v.getTaxConfigInternal(ic.DAO) + if taxConfig.DefaultIncomeRate > 0 { + taxOwed := new(big.Int).Mul(&summary.TotalIncome, big.NewInt(int64(taxConfig.DefaultIncomeRate))) + taxOwed.Div(taxOwed, big.NewInt(10000)) + summary.EstimatedOwed = *taxOwed + } + + // Balance = TaxWithheld - EstimatedOwed (positive = refund due) + summary.Balance.Sub(&summary.TaxWithheld, &summary.EstimatedOwed) + + item, _ := summary.ToStackItem() + return item +} + +// ============ Public Internal Methods for Cross-Contract Use ============ + +// BalanceOf returns VTS balance for the account. +func (v *VTS) BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int { + bal := v.getBalanceInternal(d, acc) + return bal.Total() +} + +// UnrestrictedBalanceOf returns unrestricted VTS balance for the account. +func (v *VTS) UnrestrictedBalanceOf(d *dao.Simple, acc util.Uint160) *big.Int { + bal := v.getBalanceInternal(d, acc) + return &bal.Unrestricted +} + +// Mint mints unrestricted VTS to the account. +func (v *VTS) Mint(ic *interop.Context, to util.Uint160, amount *big.Int) { + v.mintUnrestricted(ic, to, amount) +} + +// MintRestricted mints restricted VTS to the account. +func (v *VTS) MintRestricted(ic *interop.Context, to util.Uint160, amount *big.Int, category uint8) { + v.mintRestrictedInternal(ic, to, amount, category) +} + +// Burn burns unrestricted VTS from the account. +func (v *VTS) Burn(ic *interop.Context, from util.Uint160, amount *big.Int) { + bal := v.getBalanceInternal(ic.DAO, from) + if bal.Unrestricted.Cmp(amount) < 0 { + panic("insufficient funds") + } + bal.Unrestricted.Sub(&bal.Unrestricted, amount) + v.putBalance(ic.DAO, from, bal) + + supply := v.getTotalSupplyInternal(ic.DAO) + supply.Sub(supply, amount) + v.putTotalSupply(ic.DAO, supply) + + v.emitTransfer(ic, &from, nil, amount) +} + +// IsVendor returns true if the address is a registered active vendor. +func (v *VTS) IsVendor(d *dao.Simple, addr util.Uint160) bool { + vendor := v.getVendorInternal(d, addr) + return vendor != nil && vendor.Active +} + +// ============ Helper Functions ============ + +func checkWitness(ic *interop.Context, h util.Uint160) (bool, error) { + return runtime.CheckHashedWitness(ic, h) +} diff --git a/pkg/core/native_designate_test.go b/pkg/core/native_designate_test.go index 2abad74..5b02941 100644 --- a/pkg/core/native_designate_test.go +++ b/pkg/core/native_designate_test.go @@ -3,14 +3,14 @@ package core import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/annos.go b/pkg/core/state/annos.go index 40ac3dc..b902bae 100644 --- a/pkg/core/state/annos.go +++ b/pkg/core/state/annos.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // LifeStage represents age-based life stages. diff --git a/pkg/core/state/collocatio.go b/pkg/core/state/collocatio.go index 7864099..a9e8578 100644 --- a/pkg/core/state/collocatio.go +++ b/pkg/core/state/collocatio.go @@ -5,9 +5,9 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // OpportunityType represents the type of investment opportunity. diff --git a/pkg/core/state/contract.go b/pkg/core/state/contract.go index 8ecb3f3..9f10532 100644 --- a/pkg/core/state/contract.go +++ b/pkg/core/state/contract.go @@ -5,14 +5,14 @@ import ( "math" "math/big" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Contract holds information about a smart contract in the Neo blockchain. diff --git a/pkg/core/state/contract_invocation.go b/pkg/core/state/contract_invocation.go index 8b37eab..afc0f55 100644 --- a/pkg/core/state/contract_invocation.go +++ b/pkg/core/state/contract_invocation.go @@ -4,9 +4,9 @@ import ( "encoding/json" "fmt" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // ContractInvocation contains method call information. diff --git a/pkg/core/state/contract_invocation_test.go b/pkg/core/state/contract_invocation_test.go index c53e208..eb4f121 100644 --- a/pkg/core/state/contract_invocation_test.go +++ b/pkg/core/state/contract_invocation_test.go @@ -3,10 +3,10 @@ package state import ( "testing" - json "github.com/tutus-one/tutus-ordered-json" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + json "git.marketally.com/tutus-one/tutus-ordered-json" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/contract_test.go b/pkg/core/state/contract_test.go index ad289cc..7f2e24d 100644 --- a/pkg/core/state/contract_test.go +++ b/pkg/core/state/contract_test.go @@ -4,13 +4,13 @@ import ( "math" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/deposit.go b/pkg/core/state/deposit.go index 81b591b..b11ea4e 100644 --- a/pkg/core/state/deposit.go +++ b/pkg/core/state/deposit.go @@ -6,7 +6,7 @@ import ( "math" "math/big" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Deposit represents GAS deposit from a Notary contract. diff --git a/pkg/core/state/deposit_test.go b/pkg/core/state/deposit_test.go index 18c2114..393b5bf 100644 --- a/pkg/core/state/deposit_test.go +++ b/pkg/core/state/deposit_test.go @@ -4,8 +4,8 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/eligere.go b/pkg/core/state/eligere.go index e8fa72b..0911406 100644 --- a/pkg/core/state/eligere.go +++ b/pkg/core/state/eligere.go @@ -4,8 +4,8 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // ProposalCategory represents the type of proposal. diff --git a/pkg/core/state/lex.go b/pkg/core/state/lex.go index d701c2a..df8ecc9 100644 --- a/pkg/core/state/lex.go +++ b/pkg/core/state/lex.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // LawCategory represents the hierarchical category of a law. diff --git a/pkg/core/state/mpt_root.go b/pkg/core/state/mpt_root.go index aa06f70..17d7c3c 100644 --- a/pkg/core/state/mpt_root.go +++ b/pkg/core/state/mpt_root.go @@ -1,10 +1,10 @@ package state import ( - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // MPTRoot represents the storage state root together with sign info. diff --git a/pkg/core/state/mpt_root_test.go b/pkg/core/state/mpt_root_test.go index 7c8d64e..f66242a 100644 --- a/pkg/core/state/mpt_root_test.go +++ b/pkg/core/state/mpt_root_test.go @@ -5,10 +5,10 @@ import ( "math/rand/v2" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index e09045e..ffb49ad 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -6,9 +6,9 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // NEP17Balance represents the balance state of a NEP-17-token. diff --git a/pkg/core/state/native_state_test.go b/pkg/core/state/native_state_test.go index d5cf2d2..1bfa33f 100644 --- a/pkg/core/state/native_state_test.go +++ b/pkg/core/state/native_state_test.go @@ -4,8 +4,8 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/notification_event.go b/pkg/core/state/notification_event.go index 9c7acd1..5f6b10a 100644 --- a/pkg/core/state/notification_event.go +++ b/pkg/core/state/notification_event.go @@ -5,11 +5,11 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) const ( @@ -17,7 +17,7 @@ const ( // invocations (tracked by the VM) have been stored into the DB and thus whether they // should be deserialized upon retrieval. This approach saves 1 byte for all // applicationlogs over using WriteVarBytes. The original discussion can be found here - // https://github.com/tutus-one/tutus-chain/pull/3569#discussion_r1909357541 + // https://git.marketally.com/tutus-one/tutus-chain/pull/3569#discussion_r1909357541 saveInvocationsBit = 0x80 // cleanSaveInvocationsBitMask is used to remove the save invocations marker bit from // the VMState. diff --git a/pkg/core/state/notification_event_test.go b/pkg/core/state/notification_event_test.go index 41d362d..d6cd767 100644 --- a/pkg/core/state/notification_event_test.go +++ b/pkg/core/state/notification_event_test.go @@ -4,13 +4,13 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/opus.go b/pkg/core/state/opus.go index 2f40cbf..8102c56 100644 --- a/pkg/core/state/opus.go +++ b/pkg/core/state/opus.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // AIWorkerStatus represents the status of an AI worker. diff --git a/pkg/core/state/oracle.go b/pkg/core/state/oracle.go index f961733..8dcba5d 100644 --- a/pkg/core/state/oracle.go +++ b/pkg/core/state/oracle.go @@ -5,8 +5,8 @@ import ( "math/big" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // OracleRequest represents an oracle request. diff --git a/pkg/core/state/oracle_test.go b/pkg/core/state/oracle_test.go index 396dccf..328124a 100644 --- a/pkg/core/state/oracle_test.go +++ b/pkg/core/state/oracle_test.go @@ -4,9 +4,9 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/palam.go b/pkg/core/state/palam.go index 3a92bf8..662b29b 100644 --- a/pkg/core/state/palam.go +++ b/pkg/core/state/palam.go @@ -4,8 +4,8 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var errPalamInvalidStackItem = errors.New("invalid stack item") diff --git a/pkg/core/state/pons.go b/pkg/core/state/pons.go index 5c2a757..f8950ec 100644 --- a/pkg/core/state/pons.go +++ b/pkg/core/state/pons.go @@ -1,130 +1,130 @@ -package state - -import ( - "github.com/tutus-one/tutus-chain/pkg/util" -) - -// AgreementStatus represents the status of a bilateral agreement. -type AgreementStatus uint8 - -// Agreement status constants. -const ( - AgreementPending AgreementStatus = 0 - AgreementActive AgreementStatus = 1 - AgreementSuspended AgreementStatus = 2 - AgreementTerminated AgreementStatus = 3 -) - -// AgreementType represents the type of bilateral agreement. -type AgreementType uint8 - -// Agreement type constants. -const ( - AgreementTypeGeneral AgreementType = 0 // General cooperation - AgreementTypeIdentity AgreementType = 1 // Identity verification - AgreementTypeSettlement AgreementType = 2 // VTS settlement - AgreementTypeEducation AgreementType = 3 // Education credential sharing - AgreementTypeHealthcare AgreementType = 4 // Healthcare record sharing - AgreementTypeComprehensive AgreementType = 5 // All services -) - -// VerificationStatus represents the status of a verification request. -type VerificationStatus uint8 - -// Verification status constants. -const ( - VerificationPending VerificationStatus = 0 - VerificationApproved VerificationStatus = 1 - VerificationRejected VerificationStatus = 2 - VerificationExpired VerificationStatus = 3 -) - -// VerificationType represents the type of verification request. -type VerificationType uint8 - -// Verification type constants. -const ( - VerificationTypeIdentity VerificationType = 0 // Identity verification - VerificationTypeCredential VerificationType = 1 // Education credential - VerificationTypeHealth VerificationType = 2 // Healthcare record - VerificationTypeCertificate VerificationType = 3 // Professional certificate -) - -// SettlementStatus represents the status of a settlement request. -type SettlementStatus uint8 - -// Settlement status constants. -const ( - SettlementPending SettlementStatus = 0 - SettlementCompleted SettlementStatus = 1 - SettlementRejected SettlementStatus = 2 - SettlementCancelled SettlementStatus = 3 -) - -// BilateralAgreement represents an agreement between two sovereign chains. -type BilateralAgreement struct { - ID uint64 // Unique agreement ID - LocalChainID uint32 // This chain's ID - RemoteChainID uint32 // Partner chain's ID - AgreementType AgreementType // Type of agreement - Status AgreementStatus - Terms util.Uint256 // Hash of off-chain terms document - EffectiveDate uint32 // Block height when effective - ExpirationDate uint32 // Block height when expires (0 = no expiry) - CreatedAt uint32 // Block height when created - UpdatedAt uint32 // Last update block height -} - -// VerificationRequest represents a cross-border verification request. -type VerificationRequest struct { - ID uint64 // Unique request ID - RequestingChain uint32 // Chain requesting verification - TargetChain uint32 // Chain being queried - Subject util.Uint160 // Subject of verification - VerificationType VerificationType - DataHash util.Uint256 // Hash of requested data - Status VerificationStatus - ResponseHash util.Uint256 // Hash of response data (if any) - Requester util.Uint160 // Who initiated request - CreatedAt uint32 // Block height - ExpiresAt uint32 // Request expiry - RespondedAt uint32 // When responded (0 = pending) -} - -// SettlementRequest represents an international VTS settlement request. -type SettlementRequest struct { - ID uint64 // Unique request ID - FromChain uint32 // Originating chain - ToChain uint32 // Destination chain - Sender util.Uint160 // Sender on from chain - Receiver util.Uint160 // Receiver on to chain - Amount uint64 // VTS amount (in smallest units) - Reference string // Payment reference - Status SettlementStatus - CreatedAt uint32 // Block height - SettledAt uint32 // When settled (0 = pending) - TxHash util.Uint256 // Settlement transaction hash -} - -// CredentialShare represents a shared credential between chains. -type CredentialShare struct { - ID uint64 // Unique share ID - SourceChain uint32 // Chain where credential originated - TargetChain uint32 // Chain receiving credential - Owner util.Uint160 // Credential owner - CredentialType VerificationType - CredentialID uint64 // Original credential ID on source chain - ContentHash util.Uint256 // Hash of credential content - ValidUntil uint32 // Validity period on target chain - CreatedAt uint32 // Block height - IsRevoked bool // Has been revoked -} - -// PonsConfig represents configurable parameters for the Pons contract. -type PonsConfig struct { - LocalChainID uint32 // This chain's unique identifier - VerificationTimeout uint32 // Blocks until verification request expires - SettlementTimeout uint32 // Blocks until settlement request expires - MaxPendingRequests uint64 // Maximum pending requests per chain - CredentialShareExpiry uint32 // Default validity period for shared credentials -} +package state + +import ( + "git.marketally.com/tutus-one/tutus-chain/pkg/util" +) + +// AgreementStatus represents the status of a bilateral agreement. +type AgreementStatus uint8 + +// Agreement status constants. +const ( + AgreementPending AgreementStatus = 0 + AgreementActive AgreementStatus = 1 + AgreementSuspended AgreementStatus = 2 + AgreementTerminated AgreementStatus = 3 +) + +// AgreementType represents the type of bilateral agreement. +type AgreementType uint8 + +// Agreement type constants. +const ( + AgreementTypeGeneral AgreementType = 0 // General cooperation + AgreementTypeIdentity AgreementType = 1 // Identity verification + AgreementTypeSettlement AgreementType = 2 // VTS settlement + AgreementTypeEducation AgreementType = 3 // Education credential sharing + AgreementTypeHealthcare AgreementType = 4 // Healthcare record sharing + AgreementTypeComprehensive AgreementType = 5 // All services +) + +// VerificationStatus represents the status of a verification request. +type VerificationStatus uint8 + +// Verification status constants. +const ( + VerificationPending VerificationStatus = 0 + VerificationApproved VerificationStatus = 1 + VerificationRejected VerificationStatus = 2 + VerificationExpired VerificationStatus = 3 +) + +// VerificationType represents the type of verification request. +type VerificationType uint8 + +// Verification type constants. +const ( + VerificationTypeIdentity VerificationType = 0 // Identity verification + VerificationTypeCredential VerificationType = 1 // Education credential + VerificationTypeHealth VerificationType = 2 // Healthcare record + VerificationTypeCertificate VerificationType = 3 // Professional certificate +) + +// SettlementStatus represents the status of a settlement request. +type SettlementStatus uint8 + +// Settlement status constants. +const ( + SettlementPending SettlementStatus = 0 + SettlementCompleted SettlementStatus = 1 + SettlementRejected SettlementStatus = 2 + SettlementCancelled SettlementStatus = 3 +) + +// BilateralAgreement represents an agreement between two sovereign chains. +type BilateralAgreement struct { + ID uint64 // Unique agreement ID + LocalChainID uint32 // This chain's ID + RemoteChainID uint32 // Partner chain's ID + AgreementType AgreementType // Type of agreement + Status AgreementStatus + Terms util.Uint256 // Hash of off-chain terms document + EffectiveDate uint32 // Block height when effective + ExpirationDate uint32 // Block height when expires (0 = no expiry) + CreatedAt uint32 // Block height when created + UpdatedAt uint32 // Last update block height +} + +// VerificationRequest represents a cross-border verification request. +type VerificationRequest struct { + ID uint64 // Unique request ID + RequestingChain uint32 // Chain requesting verification + TargetChain uint32 // Chain being queried + Subject util.Uint160 // Subject of verification + VerificationType VerificationType + DataHash util.Uint256 // Hash of requested data + Status VerificationStatus + ResponseHash util.Uint256 // Hash of response data (if any) + Requester util.Uint160 // Who initiated request + CreatedAt uint32 // Block height + ExpiresAt uint32 // Request expiry + RespondedAt uint32 // When responded (0 = pending) +} + +// SettlementRequest represents an international VTS settlement request. +type SettlementRequest struct { + ID uint64 // Unique request ID + FromChain uint32 // Originating chain + ToChain uint32 // Destination chain + Sender util.Uint160 // Sender on from chain + Receiver util.Uint160 // Receiver on to chain + Amount uint64 // VTS amount (in smallest units) + Reference string // Payment reference + Status SettlementStatus + CreatedAt uint32 // Block height + SettledAt uint32 // When settled (0 = pending) + TxHash util.Uint256 // Settlement transaction hash +} + +// CredentialShare represents a shared credential between chains. +type CredentialShare struct { + ID uint64 // Unique share ID + SourceChain uint32 // Chain where credential originated + TargetChain uint32 // Chain receiving credential + Owner util.Uint160 // Credential owner + CredentialType VerificationType + CredentialID uint64 // Original credential ID on source chain + ContentHash util.Uint256 // Hash of credential content + ValidUntil uint32 // Validity period on target chain + CreatedAt uint32 // Block height + IsRevoked bool // Has been revoked +} + +// PonsConfig represents configurable parameters for the Pons contract. +type PonsConfig struct { + LocalChainID uint32 // This chain's unique identifier + VerificationTimeout uint32 // Blocks until verification request expires + SettlementTimeout uint32 // Blocks until settlement request expires + MaxPendingRequests uint64 // Maximum pending requests per chain + CredentialShareExpiry uint32 // Default validity period for shared credentials +} diff --git a/pkg/core/state/role_registry.go b/pkg/core/state/role_registry.go index add4883..a84ca41 100644 --- a/pkg/core/state/role_registry.go +++ b/pkg/core/state/role_registry.go @@ -1,327 +1,327 @@ -package state - -import ( - "errors" - "fmt" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Scope represents permission scope level. -type Scope uint8 - -const ( - // ScopeGlobal applies the permission globally. - ScopeGlobal Scope = 0 - // ScopePersonal applies only to the owner's own resources. - ScopePersonal Scope = 1 - // ScopeDelegated is delegated by another token holder. - ScopeDelegated Scope = 2 -) - -// Role represents a custom role definition in the RoleRegistry. -type Role struct { - ID uint64 // Unique sequential identifier - Name string // Human-readable name (max 64 chars) - Description string // Description (max 256 chars) - ParentID uint64 // Parent role ID (0 = no parent, enables hierarchy) - CreatedAt uint32 // Block height when created - CreatedBy util.Uint160 // Creator's script hash - Active bool // Whether role is active -} - -// ToStackItem implements stackitem.Convertible interface. -func (r *Role) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(r.ID))), - stackitem.NewByteArray([]byte(r.Name)), - stackitem.NewByteArray([]byte(r.Description)), - stackitem.NewBigInteger(big.NewInt(int64(r.ParentID))), - stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))), - stackitem.NewByteArray(r.CreatedBy.BytesBE()), - stackitem.NewBool(r.Active), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (r *Role) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 7 { - return fmt.Errorf("wrong number of elements: expected 7, got %d", len(items)) - } - - id, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid id: %w", err) - } - r.ID = id.Uint64() - - nameBytes, err := items[1].TryBytes() - if err != nil { - return fmt.Errorf("invalid name: %w", err) - } - r.Name = string(nameBytes) - - descBytes, err := items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid description: %w", err) - } - r.Description = string(descBytes) - - parentID, err := items[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid parentID: %w", err) - } - r.ParentID = parentID.Uint64() - - createdAt, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid createdAt: %w", err) - } - r.CreatedAt = uint32(createdAt.Int64()) - - createdByBytes, err := items[5].TryBytes() - if err != nil { - return fmt.Errorf("invalid createdBy: %w", err) - } - r.CreatedBy, err = util.Uint160DecodeBytesBE(createdByBytes) - if err != nil { - return fmt.Errorf("invalid createdBy hash: %w", err) - } - - r.Active, err = items[6].TryBool() - if err != nil { - return fmt.Errorf("invalid active: %w", err) - } - - return nil -} - -// RoleAssignment represents a role granted to a Vita holder. -type RoleAssignment struct { - TokenID uint64 // Vita ID (or script hash as uint64 for address-based) - RoleID uint64 // Role ID - GrantedAt uint32 // Block height when granted - GrantedBy util.Uint160 // Granter's script hash - ExpiresAt uint32 // Expiration block (0 = never) - Active bool // Whether assignment is active -} - -// ToStackItem implements stackitem.Convertible interface. -func (a *RoleAssignment) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(a.TokenID))), - stackitem.NewBigInteger(big.NewInt(int64(a.RoleID))), - stackitem.NewBigInteger(big.NewInt(int64(a.GrantedAt))), - stackitem.NewByteArray(a.GrantedBy.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))), - stackitem.NewBool(a.Active), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (a *RoleAssignment) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 6 { - return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items)) - } - - tokenID, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid tokenID: %w", err) - } - a.TokenID = tokenID.Uint64() - - roleID, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid roleID: %w", err) - } - a.RoleID = roleID.Uint64() - - grantedAt, err := items[2].TryInteger() - if err != nil { - return fmt.Errorf("invalid grantedAt: %w", err) - } - a.GrantedAt = uint32(grantedAt.Int64()) - - grantedByBytes, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid grantedBy: %w", err) - } - a.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes) - if err != nil { - return fmt.Errorf("invalid grantedBy hash: %w", err) - } - - expiresAt, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid expiresAt: %w", err) - } - a.ExpiresAt = uint32(expiresAt.Int64()) - - a.Active, err = items[5].TryBool() - if err != nil { - return fmt.Errorf("invalid active: %w", err) - } - - return nil -} - -// PermissionGrant represents a permission assigned to a role. -type PermissionGrant struct { - RoleID uint64 // Role ID - Resource string // Resource identifier (e.g., "documents") - Action string // Action identifier (e.g., "read", "write") - Scope Scope // Scope level - GrantedAt uint32 // Block height when granted - GrantedBy util.Uint160 // Granter's script hash -} - -// ToStackItem implements stackitem.Convertible interface. -func (p *PermissionGrant) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(p.RoleID))), - stackitem.NewByteArray([]byte(p.Resource)), - stackitem.NewByteArray([]byte(p.Action)), - stackitem.NewBigInteger(big.NewInt(int64(p.Scope))), - stackitem.NewBigInteger(big.NewInt(int64(p.GrantedAt))), - stackitem.NewByteArray(p.GrantedBy.BytesBE()), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (p *PermissionGrant) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 6 { - return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items)) - } - - roleID, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid roleID: %w", err) - } - p.RoleID = roleID.Uint64() - - resourceBytes, err := items[1].TryBytes() - if err != nil { - return fmt.Errorf("invalid resource: %w", err) - } - p.Resource = string(resourceBytes) - - actionBytes, err := items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid action: %w", err) - } - p.Action = string(actionBytes) - - scope, err := items[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid scope: %w", err) - } - p.Scope = Scope(scope.Int64()) - - grantedAt, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid grantedAt: %w", err) - } - p.GrantedAt = uint32(grantedAt.Int64()) - - grantedByBytes, err := items[5].TryBytes() - if err != nil { - return fmt.Errorf("invalid grantedBy: %w", err) - } - p.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes) - if err != nil { - return fmt.Errorf("invalid grantedBy hash: %w", err) - } - - return nil -} - -// AddressRoleAssignment represents a role granted directly to an address (script hash). -// This is used for bootstrapping when Vitas may not exist yet. -type AddressRoleAssignment struct { - Address util.Uint160 // Script hash of the address - RoleID uint64 // Role ID - GrantedAt uint32 // Block height when granted - GrantedBy util.Uint160 // Granter's script hash - ExpiresAt uint32 // Expiration block (0 = never) - Active bool // Whether assignment is active -} - -// ToStackItem implements stackitem.Convertible interface. -func (a *AddressRoleAssignment) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray(a.Address.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(a.RoleID))), - stackitem.NewBigInteger(big.NewInt(int64(a.GrantedAt))), - stackitem.NewByteArray(a.GrantedBy.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))), - stackitem.NewBool(a.Active), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (a *AddressRoleAssignment) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 6 { - return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items)) - } - - addressBytes, err := items[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid address: %w", err) - } - a.Address, err = util.Uint160DecodeBytesBE(addressBytes) - if err != nil { - return fmt.Errorf("invalid address hash: %w", err) - } - - roleID, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid roleID: %w", err) - } - a.RoleID = roleID.Uint64() - - grantedAt, err := items[2].TryInteger() - if err != nil { - return fmt.Errorf("invalid grantedAt: %w", err) - } - a.GrantedAt = uint32(grantedAt.Int64()) - - grantedByBytes, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid grantedBy: %w", err) - } - a.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes) - if err != nil { - return fmt.Errorf("invalid grantedBy hash: %w", err) - } - - expiresAt, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid expiresAt: %w", err) - } - a.ExpiresAt = uint32(expiresAt.Int64()) - - a.Active, err = items[5].TryBool() - if err != nil { - return fmt.Errorf("invalid active: %w", err) - } - - return nil -} +package state + +import ( + "errors" + "fmt" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Scope represents permission scope level. +type Scope uint8 + +const ( + // ScopeGlobal applies the permission globally. + ScopeGlobal Scope = 0 + // ScopePersonal applies only to the owner's own resources. + ScopePersonal Scope = 1 + // ScopeDelegated is delegated by another token holder. + ScopeDelegated Scope = 2 +) + +// Role represents a custom role definition in the RoleRegistry. +type Role struct { + ID uint64 // Unique sequential identifier + Name string // Human-readable name (max 64 chars) + Description string // Description (max 256 chars) + ParentID uint64 // Parent role ID (0 = no parent, enables hierarchy) + CreatedAt uint32 // Block height when created + CreatedBy util.Uint160 // Creator's script hash + Active bool // Whether role is active +} + +// ToStackItem implements stackitem.Convertible interface. +func (r *Role) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(r.ID))), + stackitem.NewByteArray([]byte(r.Name)), + stackitem.NewByteArray([]byte(r.Description)), + stackitem.NewBigInteger(big.NewInt(int64(r.ParentID))), + stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))), + stackitem.NewByteArray(r.CreatedBy.BytesBE()), + stackitem.NewBool(r.Active), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (r *Role) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 7 { + return fmt.Errorf("wrong number of elements: expected 7, got %d", len(items)) + } + + id, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid id: %w", err) + } + r.ID = id.Uint64() + + nameBytes, err := items[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid name: %w", err) + } + r.Name = string(nameBytes) + + descBytes, err := items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid description: %w", err) + } + r.Description = string(descBytes) + + parentID, err := items[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid parentID: %w", err) + } + r.ParentID = parentID.Uint64() + + createdAt, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid createdAt: %w", err) + } + r.CreatedAt = uint32(createdAt.Int64()) + + createdByBytes, err := items[5].TryBytes() + if err != nil { + return fmt.Errorf("invalid createdBy: %w", err) + } + r.CreatedBy, err = util.Uint160DecodeBytesBE(createdByBytes) + if err != nil { + return fmt.Errorf("invalid createdBy hash: %w", err) + } + + r.Active, err = items[6].TryBool() + if err != nil { + return fmt.Errorf("invalid active: %w", err) + } + + return nil +} + +// RoleAssignment represents a role granted to a Vita holder. +type RoleAssignment struct { + TokenID uint64 // Vita ID (or script hash as uint64 for address-based) + RoleID uint64 // Role ID + GrantedAt uint32 // Block height when granted + GrantedBy util.Uint160 // Granter's script hash + ExpiresAt uint32 // Expiration block (0 = never) + Active bool // Whether assignment is active +} + +// ToStackItem implements stackitem.Convertible interface. +func (a *RoleAssignment) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(a.TokenID))), + stackitem.NewBigInteger(big.NewInt(int64(a.RoleID))), + stackitem.NewBigInteger(big.NewInt(int64(a.GrantedAt))), + stackitem.NewByteArray(a.GrantedBy.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))), + stackitem.NewBool(a.Active), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (a *RoleAssignment) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 6 { + return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items)) + } + + tokenID, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid tokenID: %w", err) + } + a.TokenID = tokenID.Uint64() + + roleID, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid roleID: %w", err) + } + a.RoleID = roleID.Uint64() + + grantedAt, err := items[2].TryInteger() + if err != nil { + return fmt.Errorf("invalid grantedAt: %w", err) + } + a.GrantedAt = uint32(grantedAt.Int64()) + + grantedByBytes, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid grantedBy: %w", err) + } + a.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes) + if err != nil { + return fmt.Errorf("invalid grantedBy hash: %w", err) + } + + expiresAt, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid expiresAt: %w", err) + } + a.ExpiresAt = uint32(expiresAt.Int64()) + + a.Active, err = items[5].TryBool() + if err != nil { + return fmt.Errorf("invalid active: %w", err) + } + + return nil +} + +// PermissionGrant represents a permission assigned to a role. +type PermissionGrant struct { + RoleID uint64 // Role ID + Resource string // Resource identifier (e.g., "documents") + Action string // Action identifier (e.g., "read", "write") + Scope Scope // Scope level + GrantedAt uint32 // Block height when granted + GrantedBy util.Uint160 // Granter's script hash +} + +// ToStackItem implements stackitem.Convertible interface. +func (p *PermissionGrant) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(p.RoleID))), + stackitem.NewByteArray([]byte(p.Resource)), + stackitem.NewByteArray([]byte(p.Action)), + stackitem.NewBigInteger(big.NewInt(int64(p.Scope))), + stackitem.NewBigInteger(big.NewInt(int64(p.GrantedAt))), + stackitem.NewByteArray(p.GrantedBy.BytesBE()), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (p *PermissionGrant) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 6 { + return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items)) + } + + roleID, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid roleID: %w", err) + } + p.RoleID = roleID.Uint64() + + resourceBytes, err := items[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid resource: %w", err) + } + p.Resource = string(resourceBytes) + + actionBytes, err := items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid action: %w", err) + } + p.Action = string(actionBytes) + + scope, err := items[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid scope: %w", err) + } + p.Scope = Scope(scope.Int64()) + + grantedAt, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid grantedAt: %w", err) + } + p.GrantedAt = uint32(grantedAt.Int64()) + + grantedByBytes, err := items[5].TryBytes() + if err != nil { + return fmt.Errorf("invalid grantedBy: %w", err) + } + p.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes) + if err != nil { + return fmt.Errorf("invalid grantedBy hash: %w", err) + } + + return nil +} + +// AddressRoleAssignment represents a role granted directly to an address (script hash). +// This is used for bootstrapping when Vitas may not exist yet. +type AddressRoleAssignment struct { + Address util.Uint160 // Script hash of the address + RoleID uint64 // Role ID + GrantedAt uint32 // Block height when granted + GrantedBy util.Uint160 // Granter's script hash + ExpiresAt uint32 // Expiration block (0 = never) + Active bool // Whether assignment is active +} + +// ToStackItem implements stackitem.Convertible interface. +func (a *AddressRoleAssignment) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(a.Address.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(a.RoleID))), + stackitem.NewBigInteger(big.NewInt(int64(a.GrantedAt))), + stackitem.NewByteArray(a.GrantedBy.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))), + stackitem.NewBool(a.Active), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (a *AddressRoleAssignment) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 6 { + return fmt.Errorf("wrong number of elements: expected 6, got %d", len(items)) + } + + addressBytes, err := items[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid address: %w", err) + } + a.Address, err = util.Uint160DecodeBytesBE(addressBytes) + if err != nil { + return fmt.Errorf("invalid address hash: %w", err) + } + + roleID, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid roleID: %w", err) + } + a.RoleID = roleID.Uint64() + + grantedAt, err := items[2].TryInteger() + if err != nil { + return fmt.Errorf("invalid grantedAt: %w", err) + } + a.GrantedAt = uint32(grantedAt.Int64()) + + grantedByBytes, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid grantedBy: %w", err) + } + a.GrantedBy, err = util.Uint160DecodeBytesBE(grantedByBytes) + if err != nil { + return fmt.Errorf("invalid grantedBy hash: %w", err) + } + + expiresAt, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid expiresAt: %w", err) + } + a.ExpiresAt = uint32(expiresAt.Int64()) + + a.Active, err = items[5].TryBool() + if err != nil { + return fmt.Errorf("invalid active: %w", err) + } + + return nil +} diff --git a/pkg/core/state/salus.go b/pkg/core/state/salus.go index 14a3637..ca96290 100644 --- a/pkg/core/state/salus.go +++ b/pkg/core/state/salus.go @@ -1,632 +1,632 @@ -package state - -import ( - "errors" - "fmt" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// HealthcareAccountStatus represents the status of a healthcare account. -type HealthcareAccountStatus uint8 - -const ( - // HealthcareAccountActive indicates an active account. - HealthcareAccountActive HealthcareAccountStatus = 0 - // HealthcareAccountSuspended indicates a temporarily suspended account. - HealthcareAccountSuspended HealthcareAccountStatus = 1 - // HealthcareAccountClosed indicates a permanently closed account. - HealthcareAccountClosed HealthcareAccountStatus = 2 -) - -// MedicalRecordType represents the type of medical record. -type MedicalRecordType uint8 - -const ( - // RecordTypeCheckup indicates a routine checkup. - RecordTypeCheckup MedicalRecordType = 0 - // RecordTypeTreatment indicates a treatment. - RecordTypeTreatment MedicalRecordType = 1 - // RecordTypeEmergency indicates an emergency visit. - RecordTypeEmergency MedicalRecordType = 2 - // RecordTypePrescription indicates a prescription. - RecordTypePrescription MedicalRecordType = 3 - // RecordTypeLabResult indicates lab results. - RecordTypeLabResult MedicalRecordType = 4 - // RecordTypeVaccination indicates a vaccination. - RecordTypeVaccination MedicalRecordType = 5 - // RecordTypeMentalHealth indicates mental health services. - RecordTypeMentalHealth MedicalRecordType = 6 - // RecordTypePreventive indicates preventive care. - RecordTypePreventive MedicalRecordType = 7 -) - -// AccessLevel represents the level of access granted to a provider. -type AccessLevel uint8 - -const ( - // AccessLevelNone indicates no access. - AccessLevelNone AccessLevel = 0 - // AccessLevelEmergency indicates emergency-only access. - AccessLevelEmergency AccessLevel = 1 - // AccessLevelLimited indicates limited access (specific record types). - AccessLevelLimited AccessLevel = 2 - // AccessLevelFull indicates full access to all records. - AccessLevelFull AccessLevel = 3 -) - -// ProviderStatus represents the status of a healthcare provider. -type ProviderStatus uint8 - -const ( - // ProviderStatusActive indicates an active provider. - ProviderStatusActive ProviderStatus = 0 - // ProviderStatusSuspended indicates a suspended provider. - ProviderStatusSuspended ProviderStatus = 1 - // ProviderStatusRevoked indicates a revoked provider. - ProviderStatusRevoked ProviderStatus = 2 -) - -// HealthcareAccount represents a citizen's healthcare account. -type HealthcareAccount struct { - VitaID uint64 // Owner's Vita token ID - Owner util.Uint160 // Owner's address - AnnualAllocation uint64 // Annual healthcare credits - CreditsUsed uint64 // Credits used this year - CreditsAvailable uint64 // Available healthcare credits - BiologicalAge uint32 // Biological age (Salus-adjusted) - LastCheckup uint32 // Block height of last checkup - Status HealthcareAccountStatus // Account status - CreatedAt uint32 // Block height when created - UpdatedAt uint32 // Block height of last update -} - -// ToStackItem implements stackitem.Convertible interface. -func (a *HealthcareAccount) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(a.VitaID))), - stackitem.NewByteArray(a.Owner.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(a.AnnualAllocation))), - stackitem.NewBigInteger(big.NewInt(int64(a.CreditsUsed))), - stackitem.NewBigInteger(big.NewInt(int64(a.CreditsAvailable))), - stackitem.NewBigInteger(big.NewInt(int64(a.BiologicalAge))), - stackitem.NewBigInteger(big.NewInt(int64(a.LastCheckup))), - stackitem.NewBigInteger(big.NewInt(int64(a.Status))), - stackitem.NewBigInteger(big.NewInt(int64(a.CreatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(a.UpdatedAt))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (a *HealthcareAccount) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 10 { - return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items)) - } - - vitaID, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid vitaID: %w", err) - } - a.VitaID = vitaID.Uint64() - - owner, err := items[1].TryBytes() - if err != nil { - return fmt.Errorf("invalid owner: %w", err) - } - a.Owner, err = util.Uint160DecodeBytesBE(owner) - if err != nil { - return fmt.Errorf("invalid owner address: %w", err) - } - - annualAllocation, err := items[2].TryInteger() - if err != nil { - return fmt.Errorf("invalid annualAllocation: %w", err) - } - a.AnnualAllocation = annualAllocation.Uint64() - - creditsUsed, err := items[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid creditsUsed: %w", err) - } - a.CreditsUsed = creditsUsed.Uint64() - - creditsAvailable, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid creditsAvailable: %w", err) - } - a.CreditsAvailable = creditsAvailable.Uint64() - - biologicalAge, err := items[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid biologicalAge: %w", err) - } - a.BiologicalAge = uint32(biologicalAge.Uint64()) - - lastCheckup, err := items[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid lastCheckup: %w", err) - } - a.LastCheckup = uint32(lastCheckup.Uint64()) - - status, err := items[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid status: %w", err) - } - a.Status = HealthcareAccountStatus(status.Uint64()) - - createdAt, err := items[8].TryInteger() - if err != nil { - return fmt.Errorf("invalid createdAt: %w", err) - } - a.CreatedAt = uint32(createdAt.Uint64()) - - updatedAt, err := items[9].TryInteger() - if err != nil { - return fmt.Errorf("invalid updatedAt: %w", err) - } - a.UpdatedAt = uint32(updatedAt.Uint64()) - - return nil -} - -// MedicalRecord represents a medical record reference (data stored off-chain). -type MedicalRecord struct { - ID uint64 // Unique record ID - VitaID uint64 // Patient's Vita ID - Patient util.Uint160 // Patient's address - Provider util.Uint160 // Healthcare provider's address - RecordType MedicalRecordType // Type of medical record - ContentHash util.Uint256 // Hash of encrypted off-chain data - CreditsUsed uint64 // Healthcare credits used - CreatedAt uint32 // Block height when created - IsActive bool // Whether record is valid -} - -// ToStackItem implements stackitem.Convertible interface. -func (r *MedicalRecord) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(r.ID))), - stackitem.NewBigInteger(big.NewInt(int64(r.VitaID))), - stackitem.NewByteArray(r.Patient.BytesBE()), - stackitem.NewByteArray(r.Provider.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(r.RecordType))), - stackitem.NewByteArray(r.ContentHash.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(r.CreditsUsed))), - stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))), - stackitem.NewBool(r.IsActive), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (r *MedicalRecord) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 9 { - return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) - } - - id, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid id: %w", err) - } - r.ID = id.Uint64() - - vitaID, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid vitaID: %w", err) - } - r.VitaID = vitaID.Uint64() - - patient, err := items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid patient: %w", err) - } - r.Patient, err = util.Uint160DecodeBytesBE(patient) - if err != nil { - return fmt.Errorf("invalid patient address: %w", err) - } - - provider, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid provider: %w", err) - } - r.Provider, err = util.Uint160DecodeBytesBE(provider) - if err != nil { - return fmt.Errorf("invalid provider address: %w", err) - } - - recordType, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid recordType: %w", err) - } - r.RecordType = MedicalRecordType(recordType.Uint64()) - - contentHash, err := items[5].TryBytes() - if err != nil { - return fmt.Errorf("invalid contentHash: %w", err) - } - r.ContentHash, err = util.Uint256DecodeBytesBE(contentHash) - if err != nil { - return fmt.Errorf("invalid contentHash value: %w", err) - } - - creditsUsed, err := items[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid creditsUsed: %w", err) - } - r.CreditsUsed = creditsUsed.Uint64() - - createdAt, err := items[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid createdAt: %w", err) - } - r.CreatedAt = uint32(createdAt.Uint64()) - - isActive, err := items[8].TryBool() - if err != nil { - return fmt.Errorf("invalid isActive: %w", err) - } - r.IsActive = isActive - - return nil -} - -// ProviderAuthorization represents a healthcare provider's access authorization. -type ProviderAuthorization struct { - ID uint64 // Authorization ID - VitaID uint64 // Patient's Vita ID - Patient util.Uint160 // Patient's address - Provider util.Uint160 // Healthcare provider's address - AccessLevel AccessLevel // Level of access granted - StartsAt uint32 // Block height when access starts - ExpiresAt uint32 // Block height when access expires (0 = no expiry) - IsActive bool // Whether authorization is currently active - GrantedAt uint32 // Block height when granted -} - -// ToStackItem implements stackitem.Convertible interface. -func (p *ProviderAuthorization) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(p.ID))), - stackitem.NewBigInteger(big.NewInt(int64(p.VitaID))), - stackitem.NewByteArray(p.Patient.BytesBE()), - stackitem.NewByteArray(p.Provider.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(p.AccessLevel))), - stackitem.NewBigInteger(big.NewInt(int64(p.StartsAt))), - stackitem.NewBigInteger(big.NewInt(int64(p.ExpiresAt))), - stackitem.NewBool(p.IsActive), - stackitem.NewBigInteger(big.NewInt(int64(p.GrantedAt))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (p *ProviderAuthorization) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 9 { - return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) - } - - id, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid id: %w", err) - } - p.ID = id.Uint64() - - vitaID, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid vitaID: %w", err) - } - p.VitaID = vitaID.Uint64() - - patient, err := items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid patient: %w", err) - } - p.Patient, err = util.Uint160DecodeBytesBE(patient) - if err != nil { - return fmt.Errorf("invalid patient address: %w", err) - } - - provider, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid provider: %w", err) - } - p.Provider, err = util.Uint160DecodeBytesBE(provider) - if err != nil { - return fmt.Errorf("invalid provider address: %w", err) - } - - accessLevel, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid accessLevel: %w", err) - } - p.AccessLevel = AccessLevel(accessLevel.Uint64()) - - startsAt, err := items[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid startsAt: %w", err) - } - p.StartsAt = uint32(startsAt.Uint64()) - - expiresAt, err := items[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid expiresAt: %w", err) - } - p.ExpiresAt = uint32(expiresAt.Uint64()) - - isActive, err := items[7].TryBool() - if err != nil { - return fmt.Errorf("invalid isActive: %w", err) - } - p.IsActive = isActive - - grantedAt, err := items[8].TryInteger() - if err != nil { - return fmt.Errorf("invalid grantedAt: %w", err) - } - p.GrantedAt = uint32(grantedAt.Uint64()) - - return nil -} - -// IsExpired checks if the authorization has expired. -func (p *ProviderAuthorization) IsExpired(currentBlock uint32) bool { - return p.ExpiresAt != 0 && p.ExpiresAt <= currentBlock -} - -// IsValid checks if the authorization is currently valid. -func (p *ProviderAuthorization) IsValid(currentBlock uint32) bool { - return p.IsActive && currentBlock >= p.StartsAt && !p.IsExpired(currentBlock) -} - -// HealthcareProvider represents a registered healthcare provider. -type HealthcareProvider struct { - Address util.Uint160 // Provider's address - Name string // Provider name - ProviderID uint64 // Unique provider ID - Specialty string // Medical specialty - LicenseHash util.Uint256 // Hash of license documentation - Status ProviderStatus // Provider status - RegisteredAt uint32 // Block height when registered - UpdatedAt uint32 // Block height of last update -} - -// ToStackItem implements stackitem.Convertible interface. -func (p *HealthcareProvider) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray(p.Address.BytesBE()), - stackitem.NewByteArray([]byte(p.Name)), - stackitem.NewBigInteger(big.NewInt(int64(p.ProviderID))), - stackitem.NewByteArray([]byte(p.Specialty)), - stackitem.NewByteArray(p.LicenseHash.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(p.Status))), - stackitem.NewBigInteger(big.NewInt(int64(p.RegisteredAt))), - stackitem.NewBigInteger(big.NewInt(int64(p.UpdatedAt))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (p *HealthcareProvider) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 8 { - return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) - } - - address, err := items[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid address: %w", err) - } - p.Address, err = util.Uint160DecodeBytesBE(address) - if err != nil { - return fmt.Errorf("invalid provider address: %w", err) - } - - name, err := items[1].TryBytes() - if err != nil { - return fmt.Errorf("invalid name: %w", err) - } - p.Name = string(name) - - providerID, err := items[2].TryInteger() - if err != nil { - return fmt.Errorf("invalid providerID: %w", err) - } - p.ProviderID = providerID.Uint64() - - specialty, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid specialty: %w", err) - } - p.Specialty = string(specialty) - - licenseHash, err := items[4].TryBytes() - if err != nil { - return fmt.Errorf("invalid licenseHash: %w", err) - } - p.LicenseHash, err = util.Uint256DecodeBytesBE(licenseHash) - if err != nil { - return fmt.Errorf("invalid licenseHash value: %w", err) - } - - status, err := items[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid status: %w", err) - } - p.Status = ProviderStatus(status.Uint64()) - - registeredAt, err := items[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid registeredAt: %w", err) - } - p.RegisteredAt = uint32(registeredAt.Uint64()) - - updatedAt, err := items[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid updatedAt: %w", err) - } - p.UpdatedAt = uint32(updatedAt.Uint64()) - - return nil -} - -// EmergencyAccess represents an emergency access grant. -type EmergencyAccess struct { - ID uint64 // Emergency access ID - VitaID uint64 // Patient's Vita ID - Patient util.Uint160 // Patient's address - Provider util.Uint160 // Provider who accessed - Reason string // Emergency reason - GrantedAt uint32 // Block height when granted - ExpiresAt uint32 // Block height when expires - WasReviewed bool // Whether access was reviewed -} - -// ToStackItem implements stackitem.Convertible interface. -func (e *EmergencyAccess) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(e.ID))), - stackitem.NewBigInteger(big.NewInt(int64(e.VitaID))), - stackitem.NewByteArray(e.Patient.BytesBE()), - stackitem.NewByteArray(e.Provider.BytesBE()), - stackitem.NewByteArray([]byte(e.Reason)), - stackitem.NewBigInteger(big.NewInt(int64(e.GrantedAt))), - stackitem.NewBigInteger(big.NewInt(int64(e.ExpiresAt))), - stackitem.NewBool(e.WasReviewed), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (e *EmergencyAccess) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 8 { - return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) - } - - id, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid id: %w", err) - } - e.ID = id.Uint64() - - vitaID, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid vitaID: %w", err) - } - e.VitaID = vitaID.Uint64() - - patient, err := items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid patient: %w", err) - } - e.Patient, err = util.Uint160DecodeBytesBE(patient) - if err != nil { - return fmt.Errorf("invalid patient address: %w", err) - } - - provider, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid provider: %w", err) - } - e.Provider, err = util.Uint160DecodeBytesBE(provider) - if err != nil { - return fmt.Errorf("invalid provider address: %w", err) - } - - reason, err := items[4].TryBytes() - if err != nil { - return fmt.Errorf("invalid reason: %w", err) - } - e.Reason = string(reason) - - grantedAt, err := items[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid grantedAt: %w", err) - } - e.GrantedAt = uint32(grantedAt.Uint64()) - - expiresAt, err := items[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid expiresAt: %w", err) - } - e.ExpiresAt = uint32(expiresAt.Uint64()) - - wasReviewed, err := items[7].TryBool() - if err != nil { - return fmt.Errorf("invalid wasReviewed: %w", err) - } - e.WasReviewed = wasReviewed - - return nil -} - -// SalusConfig represents configurable parameters for the Salus contract. -type SalusConfig struct { - DefaultAnnualCredits uint64 // Default annual healthcare credits - EmergencyAccessDuration uint32 // Blocks for emergency access (default ~24 hours) - PreventiveCareBonus uint64 // Bonus credits for preventive care - MaxAuthorizationDuration uint32 // Maximum authorization duration in blocks -} - -// ToStackItem implements stackitem.Convertible interface. -func (c *SalusConfig) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(c.DefaultAnnualCredits))), - stackitem.NewBigInteger(big.NewInt(int64(c.EmergencyAccessDuration))), - stackitem.NewBigInteger(big.NewInt(int64(c.PreventiveCareBonus))), - stackitem.NewBigInteger(big.NewInt(int64(c.MaxAuthorizationDuration))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (c *SalusConfig) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 4 { - return fmt.Errorf("wrong number of elements: expected 4, got %d", len(items)) - } - - defaultCredits, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid defaultAnnualCredits: %w", err) - } - c.DefaultAnnualCredits = defaultCredits.Uint64() - - emergencyDuration, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid emergencyAccessDuration: %w", err) - } - c.EmergencyAccessDuration = uint32(emergencyDuration.Uint64()) - - preventiveBonus, err := items[2].TryInteger() - if err != nil { - return fmt.Errorf("invalid preventiveCareBonus: %w", err) - } - c.PreventiveCareBonus = preventiveBonus.Uint64() - - maxAuthDuration, err := items[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid maxAuthorizationDuration: %w", err) - } - c.MaxAuthorizationDuration = uint32(maxAuthDuration.Uint64()) - - return nil -} +package state + +import ( + "errors" + "fmt" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// HealthcareAccountStatus represents the status of a healthcare account. +type HealthcareAccountStatus uint8 + +const ( + // HealthcareAccountActive indicates an active account. + HealthcareAccountActive HealthcareAccountStatus = 0 + // HealthcareAccountSuspended indicates a temporarily suspended account. + HealthcareAccountSuspended HealthcareAccountStatus = 1 + // HealthcareAccountClosed indicates a permanently closed account. + HealthcareAccountClosed HealthcareAccountStatus = 2 +) + +// MedicalRecordType represents the type of medical record. +type MedicalRecordType uint8 + +const ( + // RecordTypeCheckup indicates a routine checkup. + RecordTypeCheckup MedicalRecordType = 0 + // RecordTypeTreatment indicates a treatment. + RecordTypeTreatment MedicalRecordType = 1 + // RecordTypeEmergency indicates an emergency visit. + RecordTypeEmergency MedicalRecordType = 2 + // RecordTypePrescription indicates a prescription. + RecordTypePrescription MedicalRecordType = 3 + // RecordTypeLabResult indicates lab results. + RecordTypeLabResult MedicalRecordType = 4 + // RecordTypeVaccination indicates a vaccination. + RecordTypeVaccination MedicalRecordType = 5 + // RecordTypeMentalHealth indicates mental health services. + RecordTypeMentalHealth MedicalRecordType = 6 + // RecordTypePreventive indicates preventive care. + RecordTypePreventive MedicalRecordType = 7 +) + +// AccessLevel represents the level of access granted to a provider. +type AccessLevel uint8 + +const ( + // AccessLevelNone indicates no access. + AccessLevelNone AccessLevel = 0 + // AccessLevelEmergency indicates emergency-only access. + AccessLevelEmergency AccessLevel = 1 + // AccessLevelLimited indicates limited access (specific record types). + AccessLevelLimited AccessLevel = 2 + // AccessLevelFull indicates full access to all records. + AccessLevelFull AccessLevel = 3 +) + +// ProviderStatus represents the status of a healthcare provider. +type ProviderStatus uint8 + +const ( + // ProviderStatusActive indicates an active provider. + ProviderStatusActive ProviderStatus = 0 + // ProviderStatusSuspended indicates a suspended provider. + ProviderStatusSuspended ProviderStatus = 1 + // ProviderStatusRevoked indicates a revoked provider. + ProviderStatusRevoked ProviderStatus = 2 +) + +// HealthcareAccount represents a citizen's healthcare account. +type HealthcareAccount struct { + VitaID uint64 // Owner's Vita token ID + Owner util.Uint160 // Owner's address + AnnualAllocation uint64 // Annual healthcare credits + CreditsUsed uint64 // Credits used this year + CreditsAvailable uint64 // Available healthcare credits + BiologicalAge uint32 // Biological age (Salus-adjusted) + LastCheckup uint32 // Block height of last checkup + Status HealthcareAccountStatus // Account status + CreatedAt uint32 // Block height when created + UpdatedAt uint32 // Block height of last update +} + +// ToStackItem implements stackitem.Convertible interface. +func (a *HealthcareAccount) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(a.VitaID))), + stackitem.NewByteArray(a.Owner.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(a.AnnualAllocation))), + stackitem.NewBigInteger(big.NewInt(int64(a.CreditsUsed))), + stackitem.NewBigInteger(big.NewInt(int64(a.CreditsAvailable))), + stackitem.NewBigInteger(big.NewInt(int64(a.BiologicalAge))), + stackitem.NewBigInteger(big.NewInt(int64(a.LastCheckup))), + stackitem.NewBigInteger(big.NewInt(int64(a.Status))), + stackitem.NewBigInteger(big.NewInt(int64(a.CreatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(a.UpdatedAt))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (a *HealthcareAccount) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 10 { + return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items)) + } + + vitaID, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid vitaID: %w", err) + } + a.VitaID = vitaID.Uint64() + + owner, err := items[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid owner: %w", err) + } + a.Owner, err = util.Uint160DecodeBytesBE(owner) + if err != nil { + return fmt.Errorf("invalid owner address: %w", err) + } + + annualAllocation, err := items[2].TryInteger() + if err != nil { + return fmt.Errorf("invalid annualAllocation: %w", err) + } + a.AnnualAllocation = annualAllocation.Uint64() + + creditsUsed, err := items[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid creditsUsed: %w", err) + } + a.CreditsUsed = creditsUsed.Uint64() + + creditsAvailable, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid creditsAvailable: %w", err) + } + a.CreditsAvailable = creditsAvailable.Uint64() + + biologicalAge, err := items[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid biologicalAge: %w", err) + } + a.BiologicalAge = uint32(biologicalAge.Uint64()) + + lastCheckup, err := items[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid lastCheckup: %w", err) + } + a.LastCheckup = uint32(lastCheckup.Uint64()) + + status, err := items[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid status: %w", err) + } + a.Status = HealthcareAccountStatus(status.Uint64()) + + createdAt, err := items[8].TryInteger() + if err != nil { + return fmt.Errorf("invalid createdAt: %w", err) + } + a.CreatedAt = uint32(createdAt.Uint64()) + + updatedAt, err := items[9].TryInteger() + if err != nil { + return fmt.Errorf("invalid updatedAt: %w", err) + } + a.UpdatedAt = uint32(updatedAt.Uint64()) + + return nil +} + +// MedicalRecord represents a medical record reference (data stored off-chain). +type MedicalRecord struct { + ID uint64 // Unique record ID + VitaID uint64 // Patient's Vita ID + Patient util.Uint160 // Patient's address + Provider util.Uint160 // Healthcare provider's address + RecordType MedicalRecordType // Type of medical record + ContentHash util.Uint256 // Hash of encrypted off-chain data + CreditsUsed uint64 // Healthcare credits used + CreatedAt uint32 // Block height when created + IsActive bool // Whether record is valid +} + +// ToStackItem implements stackitem.Convertible interface. +func (r *MedicalRecord) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(r.ID))), + stackitem.NewBigInteger(big.NewInt(int64(r.VitaID))), + stackitem.NewByteArray(r.Patient.BytesBE()), + stackitem.NewByteArray(r.Provider.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(r.RecordType))), + stackitem.NewByteArray(r.ContentHash.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(r.CreditsUsed))), + stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))), + stackitem.NewBool(r.IsActive), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (r *MedicalRecord) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 9 { + return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) + } + + id, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid id: %w", err) + } + r.ID = id.Uint64() + + vitaID, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid vitaID: %w", err) + } + r.VitaID = vitaID.Uint64() + + patient, err := items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid patient: %w", err) + } + r.Patient, err = util.Uint160DecodeBytesBE(patient) + if err != nil { + return fmt.Errorf("invalid patient address: %w", err) + } + + provider, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid provider: %w", err) + } + r.Provider, err = util.Uint160DecodeBytesBE(provider) + if err != nil { + return fmt.Errorf("invalid provider address: %w", err) + } + + recordType, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid recordType: %w", err) + } + r.RecordType = MedicalRecordType(recordType.Uint64()) + + contentHash, err := items[5].TryBytes() + if err != nil { + return fmt.Errorf("invalid contentHash: %w", err) + } + r.ContentHash, err = util.Uint256DecodeBytesBE(contentHash) + if err != nil { + return fmt.Errorf("invalid contentHash value: %w", err) + } + + creditsUsed, err := items[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid creditsUsed: %w", err) + } + r.CreditsUsed = creditsUsed.Uint64() + + createdAt, err := items[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid createdAt: %w", err) + } + r.CreatedAt = uint32(createdAt.Uint64()) + + isActive, err := items[8].TryBool() + if err != nil { + return fmt.Errorf("invalid isActive: %w", err) + } + r.IsActive = isActive + + return nil +} + +// ProviderAuthorization represents a healthcare provider's access authorization. +type ProviderAuthorization struct { + ID uint64 // Authorization ID + VitaID uint64 // Patient's Vita ID + Patient util.Uint160 // Patient's address + Provider util.Uint160 // Healthcare provider's address + AccessLevel AccessLevel // Level of access granted + StartsAt uint32 // Block height when access starts + ExpiresAt uint32 // Block height when access expires (0 = no expiry) + IsActive bool // Whether authorization is currently active + GrantedAt uint32 // Block height when granted +} + +// ToStackItem implements stackitem.Convertible interface. +func (p *ProviderAuthorization) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(p.ID))), + stackitem.NewBigInteger(big.NewInt(int64(p.VitaID))), + stackitem.NewByteArray(p.Patient.BytesBE()), + stackitem.NewByteArray(p.Provider.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(p.AccessLevel))), + stackitem.NewBigInteger(big.NewInt(int64(p.StartsAt))), + stackitem.NewBigInteger(big.NewInt(int64(p.ExpiresAt))), + stackitem.NewBool(p.IsActive), + stackitem.NewBigInteger(big.NewInt(int64(p.GrantedAt))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (p *ProviderAuthorization) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 9 { + return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) + } + + id, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid id: %w", err) + } + p.ID = id.Uint64() + + vitaID, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid vitaID: %w", err) + } + p.VitaID = vitaID.Uint64() + + patient, err := items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid patient: %w", err) + } + p.Patient, err = util.Uint160DecodeBytesBE(patient) + if err != nil { + return fmt.Errorf("invalid patient address: %w", err) + } + + provider, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid provider: %w", err) + } + p.Provider, err = util.Uint160DecodeBytesBE(provider) + if err != nil { + return fmt.Errorf("invalid provider address: %w", err) + } + + accessLevel, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid accessLevel: %w", err) + } + p.AccessLevel = AccessLevel(accessLevel.Uint64()) + + startsAt, err := items[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid startsAt: %w", err) + } + p.StartsAt = uint32(startsAt.Uint64()) + + expiresAt, err := items[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid expiresAt: %w", err) + } + p.ExpiresAt = uint32(expiresAt.Uint64()) + + isActive, err := items[7].TryBool() + if err != nil { + return fmt.Errorf("invalid isActive: %w", err) + } + p.IsActive = isActive + + grantedAt, err := items[8].TryInteger() + if err != nil { + return fmt.Errorf("invalid grantedAt: %w", err) + } + p.GrantedAt = uint32(grantedAt.Uint64()) + + return nil +} + +// IsExpired checks if the authorization has expired. +func (p *ProviderAuthorization) IsExpired(currentBlock uint32) bool { + return p.ExpiresAt != 0 && p.ExpiresAt <= currentBlock +} + +// IsValid checks if the authorization is currently valid. +func (p *ProviderAuthorization) IsValid(currentBlock uint32) bool { + return p.IsActive && currentBlock >= p.StartsAt && !p.IsExpired(currentBlock) +} + +// HealthcareProvider represents a registered healthcare provider. +type HealthcareProvider struct { + Address util.Uint160 // Provider's address + Name string // Provider name + ProviderID uint64 // Unique provider ID + Specialty string // Medical specialty + LicenseHash util.Uint256 // Hash of license documentation + Status ProviderStatus // Provider status + RegisteredAt uint32 // Block height when registered + UpdatedAt uint32 // Block height of last update +} + +// ToStackItem implements stackitem.Convertible interface. +func (p *HealthcareProvider) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(p.Address.BytesBE()), + stackitem.NewByteArray([]byte(p.Name)), + stackitem.NewBigInteger(big.NewInt(int64(p.ProviderID))), + stackitem.NewByteArray([]byte(p.Specialty)), + stackitem.NewByteArray(p.LicenseHash.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(p.Status))), + stackitem.NewBigInteger(big.NewInt(int64(p.RegisteredAt))), + stackitem.NewBigInteger(big.NewInt(int64(p.UpdatedAt))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (p *HealthcareProvider) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 8 { + return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) + } + + address, err := items[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid address: %w", err) + } + p.Address, err = util.Uint160DecodeBytesBE(address) + if err != nil { + return fmt.Errorf("invalid provider address: %w", err) + } + + name, err := items[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid name: %w", err) + } + p.Name = string(name) + + providerID, err := items[2].TryInteger() + if err != nil { + return fmt.Errorf("invalid providerID: %w", err) + } + p.ProviderID = providerID.Uint64() + + specialty, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid specialty: %w", err) + } + p.Specialty = string(specialty) + + licenseHash, err := items[4].TryBytes() + if err != nil { + return fmt.Errorf("invalid licenseHash: %w", err) + } + p.LicenseHash, err = util.Uint256DecodeBytesBE(licenseHash) + if err != nil { + return fmt.Errorf("invalid licenseHash value: %w", err) + } + + status, err := items[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid status: %w", err) + } + p.Status = ProviderStatus(status.Uint64()) + + registeredAt, err := items[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid registeredAt: %w", err) + } + p.RegisteredAt = uint32(registeredAt.Uint64()) + + updatedAt, err := items[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid updatedAt: %w", err) + } + p.UpdatedAt = uint32(updatedAt.Uint64()) + + return nil +} + +// EmergencyAccess represents an emergency access grant. +type EmergencyAccess struct { + ID uint64 // Emergency access ID + VitaID uint64 // Patient's Vita ID + Patient util.Uint160 // Patient's address + Provider util.Uint160 // Provider who accessed + Reason string // Emergency reason + GrantedAt uint32 // Block height when granted + ExpiresAt uint32 // Block height when expires + WasReviewed bool // Whether access was reviewed +} + +// ToStackItem implements stackitem.Convertible interface. +func (e *EmergencyAccess) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(e.ID))), + stackitem.NewBigInteger(big.NewInt(int64(e.VitaID))), + stackitem.NewByteArray(e.Patient.BytesBE()), + stackitem.NewByteArray(e.Provider.BytesBE()), + stackitem.NewByteArray([]byte(e.Reason)), + stackitem.NewBigInteger(big.NewInt(int64(e.GrantedAt))), + stackitem.NewBigInteger(big.NewInt(int64(e.ExpiresAt))), + stackitem.NewBool(e.WasReviewed), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (e *EmergencyAccess) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 8 { + return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) + } + + id, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid id: %w", err) + } + e.ID = id.Uint64() + + vitaID, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid vitaID: %w", err) + } + e.VitaID = vitaID.Uint64() + + patient, err := items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid patient: %w", err) + } + e.Patient, err = util.Uint160DecodeBytesBE(patient) + if err != nil { + return fmt.Errorf("invalid patient address: %w", err) + } + + provider, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid provider: %w", err) + } + e.Provider, err = util.Uint160DecodeBytesBE(provider) + if err != nil { + return fmt.Errorf("invalid provider address: %w", err) + } + + reason, err := items[4].TryBytes() + if err != nil { + return fmt.Errorf("invalid reason: %w", err) + } + e.Reason = string(reason) + + grantedAt, err := items[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid grantedAt: %w", err) + } + e.GrantedAt = uint32(grantedAt.Uint64()) + + expiresAt, err := items[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid expiresAt: %w", err) + } + e.ExpiresAt = uint32(expiresAt.Uint64()) + + wasReviewed, err := items[7].TryBool() + if err != nil { + return fmt.Errorf("invalid wasReviewed: %w", err) + } + e.WasReviewed = wasReviewed + + return nil +} + +// SalusConfig represents configurable parameters for the Salus contract. +type SalusConfig struct { + DefaultAnnualCredits uint64 // Default annual healthcare credits + EmergencyAccessDuration uint32 // Blocks for emergency access (default ~24 hours) + PreventiveCareBonus uint64 // Bonus credits for preventive care + MaxAuthorizationDuration uint32 // Maximum authorization duration in blocks +} + +// ToStackItem implements stackitem.Convertible interface. +func (c *SalusConfig) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(c.DefaultAnnualCredits))), + stackitem.NewBigInteger(big.NewInt(int64(c.EmergencyAccessDuration))), + stackitem.NewBigInteger(big.NewInt(int64(c.PreventiveCareBonus))), + stackitem.NewBigInteger(big.NewInt(int64(c.MaxAuthorizationDuration))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (c *SalusConfig) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 4 { + return fmt.Errorf("wrong number of elements: expected 4, got %d", len(items)) + } + + defaultCredits, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid defaultAnnualCredits: %w", err) + } + c.DefaultAnnualCredits = defaultCredits.Uint64() + + emergencyDuration, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid emergencyAccessDuration: %w", err) + } + c.EmergencyAccessDuration = uint32(emergencyDuration.Uint64()) + + preventiveBonus, err := items[2].TryInteger() + if err != nil { + return fmt.Errorf("invalid preventiveCareBonus: %w", err) + } + c.PreventiveCareBonus = preventiveBonus.Uint64() + + maxAuthDuration, err := items[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid maxAuthorizationDuration: %w", err) + } + c.MaxAuthorizationDuration = uint32(maxAuthDuration.Uint64()) + + return nil +} diff --git a/pkg/core/state/scire.go b/pkg/core/state/scire.go index b4b447c..c1744d5 100644 --- a/pkg/core/state/scire.go +++ b/pkg/core/state/scire.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // EducationAccountStatus represents the status of an education account. diff --git a/pkg/core/state/sese.go b/pkg/core/state/sese.go index d273d5d..8bb58d9 100644 --- a/pkg/core/state/sese.go +++ b/pkg/core/state/sese.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // LifePlanAccountStatus represents the status of a life planning account. diff --git a/pkg/core/state/state_anchors.go b/pkg/core/state/state_anchors.go index bdd6cbf..0309da8 100644 --- a/pkg/core/state/state_anchors.go +++ b/pkg/core/state/state_anchors.go @@ -1,423 +1,423 @@ -package state - -import ( - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -var errAncoraInvalidStackItem = errors.New("invalid stack item") - -// DataType represents the type of off-chain data being anchored. -type DataType uint8 - -const ( - // DataTypeMedical is for Salus healthcare records (HIPAA protected). - DataTypeMedical DataType = 0 - // DataTypeEducation is for Scire education records (certifications, transcripts). - DataTypeEducation DataType = 1 - // DataTypeInvestment is for Collocatio investment records (portfolios, transactions). - DataTypeInvestment DataType = 2 - // DataTypeDocuments is for personal documents (IPFS CIDs). - DataTypeDocuments DataType = 3 - // DataTypePresence is for VPP presence attestations (real-time humanity verification). - DataTypePresence DataType = 4 - // DataTypeCustom is for government-defined extensions. - DataTypeCustom DataType = 5 -) - -// TreeAlgorithm represents the Merkle tree hash algorithm. -type TreeAlgorithm uint8 - -const ( - // TreeAlgorithmSHA256 is the default SHA256 algorithm. - TreeAlgorithmSHA256 TreeAlgorithm = 0 - // TreeAlgorithmKeccak256 is Keccak256 for EVM compatibility. - TreeAlgorithmKeccak256 TreeAlgorithm = 1 - // TreeAlgorithmPoseidon is Poseidon for ZK-proof friendliness. - TreeAlgorithmPoseidon TreeAlgorithm = 2 -) - -// ErasureStatus represents the status of a GDPR erasure request. -type ErasureStatus uint8 - -const ( - // ErasurePending means the request is awaiting off-chain deletion. - ErasurePending ErasureStatus = 0 - // ErasureConfirmed means off-chain deletion has been confirmed. - ErasureConfirmed ErasureStatus = 1 - // ErasureDenied means the erasure was denied (legal hold, etc.). - ErasureDenied ErasureStatus = 2 -) - -// RootInfo contains metadata about a Merkle root anchored on-chain. -type RootInfo struct { - // Root is the 32-byte Merkle root. - Root []byte - // LeafCount is the number of leaves in the tree. - LeafCount uint64 - // UpdatedAt is the block height of the last update. - UpdatedAt uint32 - // UpdatedBy is the provider script hash that updated this root. - UpdatedBy util.Uint160 - // Version is the incrementing version number. - Version uint64 - // TreeAlgorithm is the hash algorithm used (0=SHA256, 1=Keccak256, 2=Poseidon). - TreeAlgorithm TreeAlgorithm - // SchemaVersion is the data schema version (e.g., "1.0.0"). - SchemaVersion string - // ContentHash is an optional hash of the serialized tree. - ContentHash []byte -} - -// EncodeBinary implements io.Serializable. -func (r *RootInfo) EncodeBinary(w *io.BinWriter) { - w.WriteVarBytes(r.Root) - w.WriteU64LE(r.LeafCount) - w.WriteU32LE(r.UpdatedAt) - r.UpdatedBy.EncodeBinary(w) - w.WriteU64LE(r.Version) - w.WriteB(byte(r.TreeAlgorithm)) - w.WriteString(r.SchemaVersion) - w.WriteVarBytes(r.ContentHash) -} - -// DecodeBinary implements io.Serializable. -func (r *RootInfo) DecodeBinary(br *io.BinReader) { - r.Root = br.ReadVarBytes() - r.LeafCount = br.ReadU64LE() - r.UpdatedAt = br.ReadU32LE() - r.UpdatedBy.DecodeBinary(br) - r.Version = br.ReadU64LE() - r.TreeAlgorithm = TreeAlgorithm(br.ReadB()) - r.SchemaVersion = br.ReadString() - r.ContentHash = br.ReadVarBytes() -} - -// ToStackItem converts RootInfo to a stack item for VM. -func (r *RootInfo) ToStackItem() (stackitem.Item, error) { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(r.Root), - stackitem.NewBigInteger(big.NewInt(int64(r.LeafCount))), - stackitem.NewBigInteger(big.NewInt(int64(r.UpdatedAt))), - stackitem.NewByteArray(r.UpdatedBy.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(r.Version))), - stackitem.NewBigInteger(big.NewInt(int64(r.TreeAlgorithm))), - stackitem.NewByteArray([]byte(r.SchemaVersion)), - stackitem.NewByteArray(r.ContentHash), - }), nil -} - -// FromStackItem populates RootInfo from a stack item. -func (r *RootInfo) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 8 { - return errAncoraInvalidStackItem - } - - root, err := arr[0].TryBytes() - if err != nil { - return err - } - r.Root = root - - leafCount, err := arr[1].TryInteger() - if err != nil { - return err - } - r.LeafCount = leafCount.Uint64() - - updatedAt, err := arr[2].TryInteger() - if err != nil { - return err - } - r.UpdatedAt = uint32(updatedAt.Uint64()) - - updatedBy, err := arr[3].TryBytes() - if err != nil { - return err - } - r.UpdatedBy, err = util.Uint160DecodeBytesBE(updatedBy) - if err != nil { - return err - } - - version, err := arr[4].TryInteger() - if err != nil { - return err - } - r.Version = version.Uint64() - - algo, err := arr[5].TryInteger() - if err != nil { - return err - } - r.TreeAlgorithm = TreeAlgorithm(algo.Uint64()) - - schema, err := arr[6].TryBytes() - if err != nil { - return err - } - r.SchemaVersion = string(schema) - - contentHash, err := arr[7].TryBytes() - if err != nil { - return err - } - r.ContentHash = contentHash - - return nil -} - -// ErasureInfo contains metadata about a GDPR erasure request. -type ErasureInfo struct { - // RequestedAt is the block height when erasure was requested. - RequestedAt uint32 - // RequestedBy is the script hash of the requester. - RequestedBy util.Uint160 - // Reason is the GDPR reason code. - Reason string - // Status is the current erasure status. - Status ErasureStatus - // ProcessedAt is when off-chain deletion was confirmed. - ProcessedAt uint32 - // ConfirmedBy is the provider that confirmed deletion. - ConfirmedBy util.Uint160 - // DeniedReason is the reason for denial (if denied). - DeniedReason string -} - -// EncodeBinary implements io.Serializable. -func (e *ErasureInfo) EncodeBinary(w *io.BinWriter) { - w.WriteU32LE(e.RequestedAt) - e.RequestedBy.EncodeBinary(w) - w.WriteString(e.Reason) - w.WriteB(byte(e.Status)) - w.WriteU32LE(e.ProcessedAt) - e.ConfirmedBy.EncodeBinary(w) - w.WriteString(e.DeniedReason) -} - -// DecodeBinary implements io.Serializable. -func (e *ErasureInfo) DecodeBinary(br *io.BinReader) { - e.RequestedAt = br.ReadU32LE() - e.RequestedBy.DecodeBinary(br) - e.Reason = br.ReadString() - e.Status = ErasureStatus(br.ReadB()) - e.ProcessedAt = br.ReadU32LE() - e.ConfirmedBy.DecodeBinary(br) - e.DeniedReason = br.ReadString() -} - -// ToStackItem converts ErasureInfo to a stack item for VM. -func (e *ErasureInfo) ToStackItem() (stackitem.Item, error) { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(e.RequestedAt))), - stackitem.NewByteArray(e.RequestedBy.BytesBE()), - stackitem.NewByteArray([]byte(e.Reason)), - stackitem.NewBigInteger(big.NewInt(int64(e.Status))), - stackitem.NewBigInteger(big.NewInt(int64(e.ProcessedAt))), - stackitem.NewByteArray(e.ConfirmedBy.BytesBE()), - stackitem.NewByteArray([]byte(e.DeniedReason)), - }), nil -} - -// FromStackItem populates ErasureInfo from a stack item. -func (e *ErasureInfo) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 7 { - return errAncoraInvalidStackItem - } - - requestedAt, err := arr[0].TryInteger() - if err != nil { - return err - } - e.RequestedAt = uint32(requestedAt.Uint64()) - - requestedBy, err := arr[1].TryBytes() - if err != nil { - return err - } - e.RequestedBy, err = util.Uint160DecodeBytesBE(requestedBy) - if err != nil { - return err - } - - reason, err := arr[2].TryBytes() - if err != nil { - return err - } - e.Reason = string(reason) - - status, err := arr[3].TryInteger() - if err != nil { - return err - } - e.Status = ErasureStatus(status.Uint64()) - - processedAt, err := arr[4].TryInteger() - if err != nil { - return err - } - e.ProcessedAt = uint32(processedAt.Uint64()) - - confirmedBy, err := arr[5].TryBytes() - if err != nil { - return err - } - e.ConfirmedBy, err = util.Uint160DecodeBytesBE(confirmedBy) - if err != nil { - return err - } - - deniedReason, err := arr[6].TryBytes() - if err != nil { - return err - } - e.DeniedReason = string(deniedReason) - - return nil -} - -// ProviderConfig contains configuration for an authorized data provider. -type ProviderConfig struct { - // DataType is the data type this provider is authorized for. - DataType DataType - // Provider is the script hash of the authorized provider. - Provider util.Uint160 - // Description is a human-readable description. - Description string - // RegisteredAt is the block height when registered. - RegisteredAt uint32 - // Active indicates if the provider is currently active. - Active bool - // MaxUpdatesPerBlock is the anti-spam rate limit. - MaxUpdatesPerBlock uint32 - // UpdateCooldown is the blocks between updates per VitaID. - UpdateCooldown uint32 -} - -// EncodeBinary implements io.Serializable. -func (p *ProviderConfig) EncodeBinary(w *io.BinWriter) { - w.WriteB(byte(p.DataType)) - p.Provider.EncodeBinary(w) - w.WriteString(p.Description) - w.WriteU32LE(p.RegisteredAt) - w.WriteBool(p.Active) - w.WriteU32LE(p.MaxUpdatesPerBlock) - w.WriteU32LE(p.UpdateCooldown) -} - -// DecodeBinary implements io.Serializable. -func (p *ProviderConfig) DecodeBinary(br *io.BinReader) { - p.DataType = DataType(br.ReadB()) - p.Provider.DecodeBinary(br) - p.Description = br.ReadString() - p.RegisteredAt = br.ReadU32LE() - p.Active = br.ReadBool() - p.MaxUpdatesPerBlock = br.ReadU32LE() - p.UpdateCooldown = br.ReadU32LE() -} - -// ToStackItem converts ProviderConfig to a stack item for VM. -func (p *ProviderConfig) ToStackItem() (stackitem.Item, error) { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(p.DataType))), - stackitem.NewByteArray(p.Provider.BytesBE()), - stackitem.NewByteArray([]byte(p.Description)), - stackitem.NewBigInteger(big.NewInt(int64(p.RegisteredAt))), - stackitem.NewBool(p.Active), - stackitem.NewBigInteger(big.NewInt(int64(p.MaxUpdatesPerBlock))), - stackitem.NewBigInteger(big.NewInt(int64(p.UpdateCooldown))), - }), nil -} - -// FromStackItem populates ProviderConfig from a stack item. -func (p *ProviderConfig) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 7 { - return errAncoraInvalidStackItem - } - - dataType, err := arr[0].TryInteger() - if err != nil { - return err - } - p.DataType = DataType(dataType.Uint64()) - - provider, err := arr[1].TryBytes() - if err != nil { - return err - } - p.Provider, err = util.Uint160DecodeBytesBE(provider) - if err != nil { - return err - } - - description, err := arr[2].TryBytes() - if err != nil { - return err - } - p.Description = string(description) - - registeredAt, err := arr[3].TryInteger() - if err != nil { - return err - } - p.RegisteredAt = uint32(registeredAt.Uint64()) - - active, err := arr[4].TryBool() - if err != nil { - return err - } - p.Active = active - - maxUpdates, err := arr[5].TryInteger() - if err != nil { - return err - } - p.MaxUpdatesPerBlock = uint32(maxUpdates.Uint64()) - - cooldown, err := arr[6].TryInteger() - if err != nil { - return err - } - p.UpdateCooldown = uint32(cooldown.Uint64()) - - return nil -} - -// StateAnchorsConfig contains configuration for the StateAnchors contract. -type StateAnchorsConfig struct { - // DefaultTreeAlgorithm is the default hash algorithm (0=SHA256). - DefaultTreeAlgorithm TreeAlgorithm - // MaxProofDepth is the maximum depth of Merkle proofs (default: 32). - MaxProofDepth uint32 - // DefaultMaxUpdatesPerBlock is the default rate limit. - DefaultMaxUpdatesPerBlock uint32 - // DefaultUpdateCooldown is the default cooldown in blocks. - DefaultUpdateCooldown uint32 - // MaxHistoryVersions is the max history to retain per vitaID+dataType. - MaxHistoryVersions uint32 - // ErasureGracePeriod is blocks before erasure can be denied. - ErasureGracePeriod uint32 - // AttestationValidBlocks is how long attestations are valid. - AttestationValidBlocks uint32 -} - -// DefaultStateAnchorsConfig returns the default configuration. -func DefaultStateAnchorsConfig() StateAnchorsConfig { - return StateAnchorsConfig{ - DefaultTreeAlgorithm: TreeAlgorithmSHA256, - MaxProofDepth: 32, - DefaultMaxUpdatesPerBlock: 10, - DefaultUpdateCooldown: 1, - MaxHistoryVersions: 100, - ErasureGracePeriod: 1000, // ~16 minutes at 1s blocks - AttestationValidBlocks: 86400, // ~24 hours - } -} +package state + +import ( + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +var errAncoraInvalidStackItem = errors.New("invalid stack item") + +// DataType represents the type of off-chain data being anchored. +type DataType uint8 + +const ( + // DataTypeMedical is for Salus healthcare records (HIPAA protected). + DataTypeMedical DataType = 0 + // DataTypeEducation is for Scire education records (certifications, transcripts). + DataTypeEducation DataType = 1 + // DataTypeInvestment is for Collocatio investment records (portfolios, transactions). + DataTypeInvestment DataType = 2 + // DataTypeDocuments is for personal documents (IPFS CIDs). + DataTypeDocuments DataType = 3 + // DataTypePresence is for VPP presence attestations (real-time humanity verification). + DataTypePresence DataType = 4 + // DataTypeCustom is for government-defined extensions. + DataTypeCustom DataType = 5 +) + +// TreeAlgorithm represents the Merkle tree hash algorithm. +type TreeAlgorithm uint8 + +const ( + // TreeAlgorithmSHA256 is the default SHA256 algorithm. + TreeAlgorithmSHA256 TreeAlgorithm = 0 + // TreeAlgorithmKeccak256 is Keccak256 for EVM compatibility. + TreeAlgorithmKeccak256 TreeAlgorithm = 1 + // TreeAlgorithmPoseidon is Poseidon for ZK-proof friendliness. + TreeAlgorithmPoseidon TreeAlgorithm = 2 +) + +// ErasureStatus represents the status of a GDPR erasure request. +type ErasureStatus uint8 + +const ( + // ErasurePending means the request is awaiting off-chain deletion. + ErasurePending ErasureStatus = 0 + // ErasureConfirmed means off-chain deletion has been confirmed. + ErasureConfirmed ErasureStatus = 1 + // ErasureDenied means the erasure was denied (legal hold, etc.). + ErasureDenied ErasureStatus = 2 +) + +// RootInfo contains metadata about a Merkle root anchored on-chain. +type RootInfo struct { + // Root is the 32-byte Merkle root. + Root []byte + // LeafCount is the number of leaves in the tree. + LeafCount uint64 + // UpdatedAt is the block height of the last update. + UpdatedAt uint32 + // UpdatedBy is the provider script hash that updated this root. + UpdatedBy util.Uint160 + // Version is the incrementing version number. + Version uint64 + // TreeAlgorithm is the hash algorithm used (0=SHA256, 1=Keccak256, 2=Poseidon). + TreeAlgorithm TreeAlgorithm + // SchemaVersion is the data schema version (e.g., "1.0.0"). + SchemaVersion string + // ContentHash is an optional hash of the serialized tree. + ContentHash []byte +} + +// EncodeBinary implements io.Serializable. +func (r *RootInfo) EncodeBinary(w *io.BinWriter) { + w.WriteVarBytes(r.Root) + w.WriteU64LE(r.LeafCount) + w.WriteU32LE(r.UpdatedAt) + r.UpdatedBy.EncodeBinary(w) + w.WriteU64LE(r.Version) + w.WriteB(byte(r.TreeAlgorithm)) + w.WriteString(r.SchemaVersion) + w.WriteVarBytes(r.ContentHash) +} + +// DecodeBinary implements io.Serializable. +func (r *RootInfo) DecodeBinary(br *io.BinReader) { + r.Root = br.ReadVarBytes() + r.LeafCount = br.ReadU64LE() + r.UpdatedAt = br.ReadU32LE() + r.UpdatedBy.DecodeBinary(br) + r.Version = br.ReadU64LE() + r.TreeAlgorithm = TreeAlgorithm(br.ReadB()) + r.SchemaVersion = br.ReadString() + r.ContentHash = br.ReadVarBytes() +} + +// ToStackItem converts RootInfo to a stack item for VM. +func (r *RootInfo) ToStackItem() (stackitem.Item, error) { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(r.Root), + stackitem.NewBigInteger(big.NewInt(int64(r.LeafCount))), + stackitem.NewBigInteger(big.NewInt(int64(r.UpdatedAt))), + stackitem.NewByteArray(r.UpdatedBy.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(r.Version))), + stackitem.NewBigInteger(big.NewInt(int64(r.TreeAlgorithm))), + stackitem.NewByteArray([]byte(r.SchemaVersion)), + stackitem.NewByteArray(r.ContentHash), + }), nil +} + +// FromStackItem populates RootInfo from a stack item. +func (r *RootInfo) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 8 { + return errAncoraInvalidStackItem + } + + root, err := arr[0].TryBytes() + if err != nil { + return err + } + r.Root = root + + leafCount, err := arr[1].TryInteger() + if err != nil { + return err + } + r.LeafCount = leafCount.Uint64() + + updatedAt, err := arr[2].TryInteger() + if err != nil { + return err + } + r.UpdatedAt = uint32(updatedAt.Uint64()) + + updatedBy, err := arr[3].TryBytes() + if err != nil { + return err + } + r.UpdatedBy, err = util.Uint160DecodeBytesBE(updatedBy) + if err != nil { + return err + } + + version, err := arr[4].TryInteger() + if err != nil { + return err + } + r.Version = version.Uint64() + + algo, err := arr[5].TryInteger() + if err != nil { + return err + } + r.TreeAlgorithm = TreeAlgorithm(algo.Uint64()) + + schema, err := arr[6].TryBytes() + if err != nil { + return err + } + r.SchemaVersion = string(schema) + + contentHash, err := arr[7].TryBytes() + if err != nil { + return err + } + r.ContentHash = contentHash + + return nil +} + +// ErasureInfo contains metadata about a GDPR erasure request. +type ErasureInfo struct { + // RequestedAt is the block height when erasure was requested. + RequestedAt uint32 + // RequestedBy is the script hash of the requester. + RequestedBy util.Uint160 + // Reason is the GDPR reason code. + Reason string + // Status is the current erasure status. + Status ErasureStatus + // ProcessedAt is when off-chain deletion was confirmed. + ProcessedAt uint32 + // ConfirmedBy is the provider that confirmed deletion. + ConfirmedBy util.Uint160 + // DeniedReason is the reason for denial (if denied). + DeniedReason string +} + +// EncodeBinary implements io.Serializable. +func (e *ErasureInfo) EncodeBinary(w *io.BinWriter) { + w.WriteU32LE(e.RequestedAt) + e.RequestedBy.EncodeBinary(w) + w.WriteString(e.Reason) + w.WriteB(byte(e.Status)) + w.WriteU32LE(e.ProcessedAt) + e.ConfirmedBy.EncodeBinary(w) + w.WriteString(e.DeniedReason) +} + +// DecodeBinary implements io.Serializable. +func (e *ErasureInfo) DecodeBinary(br *io.BinReader) { + e.RequestedAt = br.ReadU32LE() + e.RequestedBy.DecodeBinary(br) + e.Reason = br.ReadString() + e.Status = ErasureStatus(br.ReadB()) + e.ProcessedAt = br.ReadU32LE() + e.ConfirmedBy.DecodeBinary(br) + e.DeniedReason = br.ReadString() +} + +// ToStackItem converts ErasureInfo to a stack item for VM. +func (e *ErasureInfo) ToStackItem() (stackitem.Item, error) { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(e.RequestedAt))), + stackitem.NewByteArray(e.RequestedBy.BytesBE()), + stackitem.NewByteArray([]byte(e.Reason)), + stackitem.NewBigInteger(big.NewInt(int64(e.Status))), + stackitem.NewBigInteger(big.NewInt(int64(e.ProcessedAt))), + stackitem.NewByteArray(e.ConfirmedBy.BytesBE()), + stackitem.NewByteArray([]byte(e.DeniedReason)), + }), nil +} + +// FromStackItem populates ErasureInfo from a stack item. +func (e *ErasureInfo) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 7 { + return errAncoraInvalidStackItem + } + + requestedAt, err := arr[0].TryInteger() + if err != nil { + return err + } + e.RequestedAt = uint32(requestedAt.Uint64()) + + requestedBy, err := arr[1].TryBytes() + if err != nil { + return err + } + e.RequestedBy, err = util.Uint160DecodeBytesBE(requestedBy) + if err != nil { + return err + } + + reason, err := arr[2].TryBytes() + if err != nil { + return err + } + e.Reason = string(reason) + + status, err := arr[3].TryInteger() + if err != nil { + return err + } + e.Status = ErasureStatus(status.Uint64()) + + processedAt, err := arr[4].TryInteger() + if err != nil { + return err + } + e.ProcessedAt = uint32(processedAt.Uint64()) + + confirmedBy, err := arr[5].TryBytes() + if err != nil { + return err + } + e.ConfirmedBy, err = util.Uint160DecodeBytesBE(confirmedBy) + if err != nil { + return err + } + + deniedReason, err := arr[6].TryBytes() + if err != nil { + return err + } + e.DeniedReason = string(deniedReason) + + return nil +} + +// ProviderConfig contains configuration for an authorized data provider. +type ProviderConfig struct { + // DataType is the data type this provider is authorized for. + DataType DataType + // Provider is the script hash of the authorized provider. + Provider util.Uint160 + // Description is a human-readable description. + Description string + // RegisteredAt is the block height when registered. + RegisteredAt uint32 + // Active indicates if the provider is currently active. + Active bool + // MaxUpdatesPerBlock is the anti-spam rate limit. + MaxUpdatesPerBlock uint32 + // UpdateCooldown is the blocks between updates per VitaID. + UpdateCooldown uint32 +} + +// EncodeBinary implements io.Serializable. +func (p *ProviderConfig) EncodeBinary(w *io.BinWriter) { + w.WriteB(byte(p.DataType)) + p.Provider.EncodeBinary(w) + w.WriteString(p.Description) + w.WriteU32LE(p.RegisteredAt) + w.WriteBool(p.Active) + w.WriteU32LE(p.MaxUpdatesPerBlock) + w.WriteU32LE(p.UpdateCooldown) +} + +// DecodeBinary implements io.Serializable. +func (p *ProviderConfig) DecodeBinary(br *io.BinReader) { + p.DataType = DataType(br.ReadB()) + p.Provider.DecodeBinary(br) + p.Description = br.ReadString() + p.RegisteredAt = br.ReadU32LE() + p.Active = br.ReadBool() + p.MaxUpdatesPerBlock = br.ReadU32LE() + p.UpdateCooldown = br.ReadU32LE() +} + +// ToStackItem converts ProviderConfig to a stack item for VM. +func (p *ProviderConfig) ToStackItem() (stackitem.Item, error) { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(p.DataType))), + stackitem.NewByteArray(p.Provider.BytesBE()), + stackitem.NewByteArray([]byte(p.Description)), + stackitem.NewBigInteger(big.NewInt(int64(p.RegisteredAt))), + stackitem.NewBool(p.Active), + stackitem.NewBigInteger(big.NewInt(int64(p.MaxUpdatesPerBlock))), + stackitem.NewBigInteger(big.NewInt(int64(p.UpdateCooldown))), + }), nil +} + +// FromStackItem populates ProviderConfig from a stack item. +func (p *ProviderConfig) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 7 { + return errAncoraInvalidStackItem + } + + dataType, err := arr[0].TryInteger() + if err != nil { + return err + } + p.DataType = DataType(dataType.Uint64()) + + provider, err := arr[1].TryBytes() + if err != nil { + return err + } + p.Provider, err = util.Uint160DecodeBytesBE(provider) + if err != nil { + return err + } + + description, err := arr[2].TryBytes() + if err != nil { + return err + } + p.Description = string(description) + + registeredAt, err := arr[3].TryInteger() + if err != nil { + return err + } + p.RegisteredAt = uint32(registeredAt.Uint64()) + + active, err := arr[4].TryBool() + if err != nil { + return err + } + p.Active = active + + maxUpdates, err := arr[5].TryInteger() + if err != nil { + return err + } + p.MaxUpdatesPerBlock = uint32(maxUpdates.Uint64()) + + cooldown, err := arr[6].TryInteger() + if err != nil { + return err + } + p.UpdateCooldown = uint32(cooldown.Uint64()) + + return nil +} + +// StateAnchorsConfig contains configuration for the StateAnchors contract. +type StateAnchorsConfig struct { + // DefaultTreeAlgorithm is the default hash algorithm (0=SHA256). + DefaultTreeAlgorithm TreeAlgorithm + // MaxProofDepth is the maximum depth of Merkle proofs (default: 32). + MaxProofDepth uint32 + // DefaultMaxUpdatesPerBlock is the default rate limit. + DefaultMaxUpdatesPerBlock uint32 + // DefaultUpdateCooldown is the default cooldown in blocks. + DefaultUpdateCooldown uint32 + // MaxHistoryVersions is the max history to retain per vitaID+dataType. + MaxHistoryVersions uint32 + // ErasureGracePeriod is blocks before erasure can be denied. + ErasureGracePeriod uint32 + // AttestationValidBlocks is how long attestations are valid. + AttestationValidBlocks uint32 +} + +// DefaultStateAnchorsConfig returns the default configuration. +func DefaultStateAnchorsConfig() StateAnchorsConfig { + return StateAnchorsConfig{ + DefaultTreeAlgorithm: TreeAlgorithmSHA256, + MaxProofDepth: 32, + DefaultMaxUpdatesPerBlock: 10, + DefaultUpdateCooldown: 1, + MaxHistoryVersions: 100, + ErasureGracePeriod: 1000, // ~16 minutes at 1s blocks + AttestationValidBlocks: 86400, // ~24 hours + } +} diff --git a/pkg/core/state/tokens.go b/pkg/core/state/tokens.go index 0114290..ca6a28e 100644 --- a/pkg/core/state/tokens.go +++ b/pkg/core/state/tokens.go @@ -4,10 +4,10 @@ import ( "bytes" "math/big" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // TokenTransferBatchSize is the maximum number of entries for TokenTransferLog. diff --git a/pkg/core/state/tokens_test.go b/pkg/core/state/tokens_test.go index 5fe2fdd..29ccae3 100644 --- a/pkg/core/state/tokens_test.go +++ b/pkg/core/state/tokens_test.go @@ -5,9 +5,9 @@ import ( "math/rand/v2" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/state/tribute.go b/pkg/core/state/tribute.go index faf3483..dc310c8 100644 --- a/pkg/core/state/tribute.go +++ b/pkg/core/state/tribute.go @@ -1,647 +1,647 @@ -package state - -import ( - "errors" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// VelocityAccountStatus represents the velocity tracking account status. -type VelocityAccountStatus uint8 - -const ( - // VelocityAccountActive indicates the account is actively tracked. - VelocityAccountActive VelocityAccountStatus = 0 - // VelocityAccountExempt indicates the account is exempt from tribute. - VelocityAccountExempt VelocityAccountStatus = 1 - // VelocityAccountSuspended indicates velocity tracking is suspended. - VelocityAccountSuspended VelocityAccountStatus = 2 -) - -// AssessmentStatus represents the status of a tribute assessment. -type AssessmentStatus uint8 - -const ( - // AssessmentPending indicates the assessment is pending collection. - AssessmentPending AssessmentStatus = 0 - // AssessmentCollected indicates the tribute has been collected. - AssessmentCollected AssessmentStatus = 1 - // AssessmentWaived indicates the assessment was waived. - AssessmentWaived AssessmentStatus = 2 - // AssessmentAppealed indicates the assessment is under appeal. - AssessmentAppealed AssessmentStatus = 3 -) - -// HoardingLevel represents the severity of resource hoarding. -type HoardingLevel uint8 - -const ( - // HoardingNone indicates no hoarding detected. - HoardingNone HoardingLevel = 0 - // HoardingMild indicates mild hoarding (below normal velocity). - HoardingMild HoardingLevel = 1 - // HoardingModerate indicates moderate hoarding. - HoardingModerate HoardingLevel = 2 - // HoardingSevere indicates severe hoarding. - HoardingSevere HoardingLevel = 3 - // HoardingExtreme indicates extreme hoarding (stagnant resources). - HoardingExtreme HoardingLevel = 4 -) - -// IncentiveType represents types of circulation incentives. -type IncentiveType uint8 - -const ( - // IncentiveVelocityBonus rewards high resource velocity. - IncentiveVelocityBonus IncentiveType = 0 - // IncentiveProductiveUse rewards productive resource use. - IncentiveProductiveUse IncentiveType = 1 - // IncentiveCommunitySupport rewards community contributions. - IncentiveCommunitySupport IncentiveType = 2 - // IncentiveEducationSpending rewards education investment. - IncentiveEducationSpending IncentiveType = 3 - // IncentiveHealthcareSpending rewards healthcare investment. - IncentiveHealthcareSpending IncentiveType = 4 -) - -// VelocityAccount tracks resource velocity for a Vita holder. -type VelocityAccount struct { - VitaID uint64 // Owner's Vita token ID - Owner util.Uint160 // Owner's address - CurrentVelocity uint64 // Current velocity score (0-10000 basis points) - AverageVelocity uint64 // Rolling average velocity - LastActivityBlock uint32 // Last transaction block - TotalInflow uint64 // Total resources received - TotalOutflow uint64 // Total resources spent/transferred - StagnantBalance uint64 // Balance considered stagnant - HoardingLevel HoardingLevel // Current hoarding assessment - ExemptionReason string // Reason for exemption (if any) - TotalTributePaid uint64 // Lifetime tribute paid - TotalIncentivesRcvd uint64 // Lifetime incentives received - Status VelocityAccountStatus // Account status - CreatedAt uint32 // Block height when created - UpdatedAt uint32 // Last update block -} - -// ToStackItem implements stackitem.Convertible. -func (a *VelocityAccount) ToStackItem() stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)), - stackitem.NewByteArray(a.Owner.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.CurrentVelocity)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.AverageVelocity)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.LastActivityBlock))), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalInflow)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalOutflow)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantBalance)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))), - stackitem.NewByteArray([]byte(a.ExemptionReason)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalTributePaid)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalIncentivesRcvd)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CreatedAt))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.UpdatedAt))), - }) -} - -// FromStackItem implements stackitem.Convertible. -func (a *VelocityAccount) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 15 { - return errors.New("not a struct") - } - vitaID, err := arr[0].TryInteger() - if err != nil { - return err - } - a.VitaID = vitaID.Uint64() - - ownerBytes, err := arr[1].TryBytes() - if err != nil { - return err - } - a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes) - if err != nil { - return err - } - - currentVel, err := arr[2].TryInteger() - if err != nil { - return err - } - a.CurrentVelocity = currentVel.Uint64() - - avgVel, err := arr[3].TryInteger() - if err != nil { - return err - } - a.AverageVelocity = avgVel.Uint64() - - lastActivity, err := arr[4].TryInteger() - if err != nil { - return err - } - a.LastActivityBlock = uint32(lastActivity.Uint64()) - - totalIn, err := arr[5].TryInteger() - if err != nil { - return err - } - a.TotalInflow = totalIn.Uint64() - - totalOut, err := arr[6].TryInteger() - if err != nil { - return err - } - a.TotalOutflow = totalOut.Uint64() - - stagnant, err := arr[7].TryInteger() - if err != nil { - return err - } - a.StagnantBalance = stagnant.Uint64() - - hoardLevel, err := arr[8].TryInteger() - if err != nil { - return err - } - a.HoardingLevel = HoardingLevel(hoardLevel.Uint64()) - - exemptReason, err := arr[9].TryBytes() - if err != nil { - return err - } - a.ExemptionReason = string(exemptReason) - - totalTrib, err := arr[10].TryInteger() - if err != nil { - return err - } - a.TotalTributePaid = totalTrib.Uint64() - - totalInc, err := arr[11].TryInteger() - if err != nil { - return err - } - a.TotalIncentivesRcvd = totalInc.Uint64() - - status, err := arr[12].TryInteger() - if err != nil { - return err - } - a.Status = VelocityAccountStatus(status.Uint64()) - - created, err := arr[13].TryInteger() - if err != nil { - return err - } - a.CreatedAt = uint32(created.Uint64()) - - updated, err := arr[14].TryInteger() - if err != nil { - return err - } - a.UpdatedAt = uint32(updated.Uint64()) - - return nil -} - -// TributeAssessment represents a hoarding tribute assessment. -type TributeAssessment struct { - ID uint64 // Unique assessment ID - VitaID uint64 // Owner's Vita ID - Owner util.Uint160 // Owner's address - AssessmentBlock uint32 // Block when assessment made - HoardingLevel HoardingLevel // Hoarding level at assessment - StagnantAmount uint64 // Amount considered stagnant - TributeRate uint64 // Rate applied (basis points) - TributeAmount uint64 // Tribute amount due - DueBlock uint32 // Block by which tribute is due - CollectedBlock uint32 // Block when collected (0 if not) - Status AssessmentStatus // Assessment status - AppealReason string // Reason for appeal (if any) -} - -// ToStackItem implements stackitem.Convertible. -func (a *TributeAssessment) ToStackItem() stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(a.ID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)), - stackitem.NewByteArray(a.Owner.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.AssessmentBlock))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantAmount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeRate)), - stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeAmount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.DueBlock))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CollectedBlock))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))), - stackitem.NewByteArray([]byte(a.AppealReason)), - }) -} - -// FromStackItem implements stackitem.Convertible. -func (a *TributeAssessment) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 12 { - return errors.New("not a struct") - } - - id, err := arr[0].TryInteger() - if err != nil { - return err - } - a.ID = id.Uint64() - - vitaID, err := arr[1].TryInteger() - if err != nil { - return err - } - a.VitaID = vitaID.Uint64() - - ownerBytes, err := arr[2].TryBytes() - if err != nil { - return err - } - a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes) - if err != nil { - return err - } - - assessBlock, err := arr[3].TryInteger() - if err != nil { - return err - } - a.AssessmentBlock = uint32(assessBlock.Uint64()) - - hoardLevel, err := arr[4].TryInteger() - if err != nil { - return err - } - a.HoardingLevel = HoardingLevel(hoardLevel.Uint64()) - - stagnant, err := arr[5].TryInteger() - if err != nil { - return err - } - a.StagnantAmount = stagnant.Uint64() - - rate, err := arr[6].TryInteger() - if err != nil { - return err - } - a.TributeRate = rate.Uint64() - - amount, err := arr[7].TryInteger() - if err != nil { - return err - } - a.TributeAmount = amount.Uint64() - - due, err := arr[8].TryInteger() - if err != nil { - return err - } - a.DueBlock = uint32(due.Uint64()) - - collected, err := arr[9].TryInteger() - if err != nil { - return err - } - a.CollectedBlock = uint32(collected.Uint64()) - - status, err := arr[10].TryInteger() - if err != nil { - return err - } - a.Status = AssessmentStatus(status.Uint64()) - - appealReason, err := arr[11].TryBytes() - if err != nil { - return err - } - a.AppealReason = string(appealReason) - - return nil -} - -// CirculationIncentive represents a reward for resource circulation. -type CirculationIncentive struct { - ID uint64 // Unique incentive ID - VitaID uint64 // Recipient's Vita ID - Recipient util.Uint160 // Recipient's address - IncentiveType IncentiveType // Type of incentive - Amount uint64 // Incentive amount - Reason string // Reason for incentive - VelocityScore uint64 // Velocity score that triggered incentive - GrantedBlock uint32 // Block when granted - ClaimedBlock uint32 // Block when claimed (0 if not) - Claimed bool // Whether incentive was claimed -} - -// ToStackItem implements stackitem.Convertible. -func (i *CirculationIncentive) ToStackItem() stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(i.ID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(i.VitaID)), - stackitem.NewByteArray(i.Recipient.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.IncentiveType))), - stackitem.NewBigInteger(new(big.Int).SetUint64(i.Amount)), - stackitem.NewByteArray([]byte(i.Reason)), - stackitem.NewBigInteger(new(big.Int).SetUint64(i.VelocityScore)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.GrantedBlock))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.ClaimedBlock))), - stackitem.NewBool(i.Claimed), - }) -} - -// FromStackItem implements stackitem.Convertible. -func (i *CirculationIncentive) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 10 { - return errors.New("not a struct") - } - - id, err := arr[0].TryInteger() - if err != nil { - return err - } - i.ID = id.Uint64() - - vitaID, err := arr[1].TryInteger() - if err != nil { - return err - } - i.VitaID = vitaID.Uint64() - - recipBytes, err := arr[2].TryBytes() - if err != nil { - return err - } - i.Recipient, err = util.Uint160DecodeBytesBE(recipBytes) - if err != nil { - return err - } - - incType, err := arr[3].TryInteger() - if err != nil { - return err - } - i.IncentiveType = IncentiveType(incType.Uint64()) - - amount, err := arr[4].TryInteger() - if err != nil { - return err - } - i.Amount = amount.Uint64() - - reason, err := arr[5].TryBytes() - if err != nil { - return err - } - i.Reason = string(reason) - - velScore, err := arr[6].TryInteger() - if err != nil { - return err - } - i.VelocityScore = velScore.Uint64() - - granted, err := arr[7].TryInteger() - if err != nil { - return err - } - i.GrantedBlock = uint32(granted.Uint64()) - - claimed, err := arr[8].TryInteger() - if err != nil { - return err - } - i.ClaimedBlock = uint32(claimed.Uint64()) - - claimedBool, err := arr[9].TryBool() - if err != nil { - return err - } - i.Claimed = claimedBool - - return nil -} - -// RedistributionRecord tracks wealth redistribution events. -type RedistributionRecord struct { - ID uint64 // Unique record ID - SourceAssessment uint64 // Source assessment ID - TotalAmount uint64 // Total amount redistributed - RecipientCount uint64 // Number of recipients - PerCapitaAmount uint64 // Amount per recipient - RedistBlock uint32 // Block when redistribution occurred - TargetCategory string // Category of recipients (e.g., "low_velocity", "all_citizens") -} - -// ToStackItem implements stackitem.Convertible. -func (r *RedistributionRecord) ToStackItem() stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(r.ID)), - stackitem.NewBigInteger(new(big.Int).SetUint64(r.SourceAssessment)), - stackitem.NewBigInteger(new(big.Int).SetUint64(r.TotalAmount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(r.RecipientCount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(r.PerCapitaAmount)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(r.RedistBlock))), - stackitem.NewByteArray([]byte(r.TargetCategory)), - }) -} - -// FromStackItem implements stackitem.Convertible. -func (r *RedistributionRecord) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 7 { - return errors.New("not a struct") - } - - id, err := arr[0].TryInteger() - if err != nil { - return err - } - r.ID = id.Uint64() - - sourceAssess, err := arr[1].TryInteger() - if err != nil { - return err - } - r.SourceAssessment = sourceAssess.Uint64() - - total, err := arr[2].TryInteger() - if err != nil { - return err - } - r.TotalAmount = total.Uint64() - - recipCount, err := arr[3].TryInteger() - if err != nil { - return err - } - r.RecipientCount = recipCount.Uint64() - - perCapita, err := arr[4].TryInteger() - if err != nil { - return err - } - r.PerCapitaAmount = perCapita.Uint64() - - redistBlock, err := arr[5].TryInteger() - if err != nil { - return err - } - r.RedistBlock = uint32(redistBlock.Uint64()) - - targetCat, err := arr[6].TryBytes() - if err != nil { - return err - } - r.TargetCategory = string(targetCat) - - return nil -} - -// TributeConfig holds configurable parameters for the Tribute system. -type TributeConfig struct { - VelocityThresholdMild uint64 // Velocity below this = mild hoarding (basis points) - VelocityThresholdModerate uint64 // Velocity below this = moderate hoarding - VelocityThresholdSevere uint64 // Velocity below this = severe hoarding - VelocityThresholdExtreme uint64 // Velocity below this = extreme hoarding - TributeRateMild uint64 // Tribute rate for mild hoarding (basis points) - TributeRateModerate uint64 // Tribute rate for moderate hoarding - TributeRateSevere uint64 // Tribute rate for severe hoarding - TributeRateExtreme uint64 // Tribute rate for extreme hoarding - IncentiveRateHigh uint64 // Incentive rate for high velocity - IncentiveRateVeryHigh uint64 // Incentive rate for very high velocity - StagnancyPeriod uint32 // Blocks before balance considered stagnant - AssessmentPeriod uint32 // Blocks between assessments - GracePeriod uint32 // Blocks before tribute due after assessment - MinBalanceForTribute uint64 // Minimum balance to assess tribute - ExemptionThreshold uint64 // Balance below which exempt from tribute -} - -// ToStackItem implements stackitem.Convertible. -func (c *TributeConfig) ToStackItem() stackitem.Item { - return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdMild)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdModerate)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdSevere)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdExtreme)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateMild)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateModerate)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateSevere)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateExtreme)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateHigh)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateVeryHigh)), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.StagnancyPeriod))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.AssessmentPeriod))), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.GracePeriod))), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.MinBalanceForTribute)), - stackitem.NewBigInteger(new(big.Int).SetUint64(c.ExemptionThreshold)), - }) -} - -// FromStackItem implements stackitem.Convertible. -func (c *TributeConfig) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok || len(arr) < 15 { - return errors.New("not a struct") - } - - threshMild, err := arr[0].TryInteger() - if err != nil { - return err - } - c.VelocityThresholdMild = threshMild.Uint64() - - threshMod, err := arr[1].TryInteger() - if err != nil { - return err - } - c.VelocityThresholdModerate = threshMod.Uint64() - - threshSev, err := arr[2].TryInteger() - if err != nil { - return err - } - c.VelocityThresholdSevere = threshSev.Uint64() - - threshExt, err := arr[3].TryInteger() - if err != nil { - return err - } - c.VelocityThresholdExtreme = threshExt.Uint64() - - rateMild, err := arr[4].TryInteger() - if err != nil { - return err - } - c.TributeRateMild = rateMild.Uint64() - - rateMod, err := arr[5].TryInteger() - if err != nil { - return err - } - c.TributeRateModerate = rateMod.Uint64() - - rateSev, err := arr[6].TryInteger() - if err != nil { - return err - } - c.TributeRateSevere = rateSev.Uint64() - - rateExt, err := arr[7].TryInteger() - if err != nil { - return err - } - c.TributeRateExtreme = rateExt.Uint64() - - incHigh, err := arr[8].TryInteger() - if err != nil { - return err - } - c.IncentiveRateHigh = incHigh.Uint64() - - incVeryHigh, err := arr[9].TryInteger() - if err != nil { - return err - } - c.IncentiveRateVeryHigh = incVeryHigh.Uint64() - - stagnancy, err := arr[10].TryInteger() - if err != nil { - return err - } - c.StagnancyPeriod = uint32(stagnancy.Uint64()) - - assessPeriod, err := arr[11].TryInteger() - if err != nil { - return err - } - c.AssessmentPeriod = uint32(assessPeriod.Uint64()) - - grace, err := arr[12].TryInteger() - if err != nil { - return err - } - c.GracePeriod = uint32(grace.Uint64()) - - minBal, err := arr[13].TryInteger() - if err != nil { - return err - } - c.MinBalanceForTribute = minBal.Uint64() - - exemptThresh, err := arr[14].TryInteger() - if err != nil { - return err - } - c.ExemptionThreshold = exemptThresh.Uint64() - - return nil -} +package state + +import ( + "errors" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// VelocityAccountStatus represents the velocity tracking account status. +type VelocityAccountStatus uint8 + +const ( + // VelocityAccountActive indicates the account is actively tracked. + VelocityAccountActive VelocityAccountStatus = 0 + // VelocityAccountExempt indicates the account is exempt from tribute. + VelocityAccountExempt VelocityAccountStatus = 1 + // VelocityAccountSuspended indicates velocity tracking is suspended. + VelocityAccountSuspended VelocityAccountStatus = 2 +) + +// AssessmentStatus represents the status of a tribute assessment. +type AssessmentStatus uint8 + +const ( + // AssessmentPending indicates the assessment is pending collection. + AssessmentPending AssessmentStatus = 0 + // AssessmentCollected indicates the tribute has been collected. + AssessmentCollected AssessmentStatus = 1 + // AssessmentWaived indicates the assessment was waived. + AssessmentWaived AssessmentStatus = 2 + // AssessmentAppealed indicates the assessment is under appeal. + AssessmentAppealed AssessmentStatus = 3 +) + +// HoardingLevel represents the severity of resource hoarding. +type HoardingLevel uint8 + +const ( + // HoardingNone indicates no hoarding detected. + HoardingNone HoardingLevel = 0 + // HoardingMild indicates mild hoarding (below normal velocity). + HoardingMild HoardingLevel = 1 + // HoardingModerate indicates moderate hoarding. + HoardingModerate HoardingLevel = 2 + // HoardingSevere indicates severe hoarding. + HoardingSevere HoardingLevel = 3 + // HoardingExtreme indicates extreme hoarding (stagnant resources). + HoardingExtreme HoardingLevel = 4 +) + +// IncentiveType represents types of circulation incentives. +type IncentiveType uint8 + +const ( + // IncentiveVelocityBonus rewards high resource velocity. + IncentiveVelocityBonus IncentiveType = 0 + // IncentiveProductiveUse rewards productive resource use. + IncentiveProductiveUse IncentiveType = 1 + // IncentiveCommunitySupport rewards community contributions. + IncentiveCommunitySupport IncentiveType = 2 + // IncentiveEducationSpending rewards education investment. + IncentiveEducationSpending IncentiveType = 3 + // IncentiveHealthcareSpending rewards healthcare investment. + IncentiveHealthcareSpending IncentiveType = 4 +) + +// VelocityAccount tracks resource velocity for a Vita holder. +type VelocityAccount struct { + VitaID uint64 // Owner's Vita token ID + Owner util.Uint160 // Owner's address + CurrentVelocity uint64 // Current velocity score (0-10000 basis points) + AverageVelocity uint64 // Rolling average velocity + LastActivityBlock uint32 // Last transaction block + TotalInflow uint64 // Total resources received + TotalOutflow uint64 // Total resources spent/transferred + StagnantBalance uint64 // Balance considered stagnant + HoardingLevel HoardingLevel // Current hoarding assessment + ExemptionReason string // Reason for exemption (if any) + TotalTributePaid uint64 // Lifetime tribute paid + TotalIncentivesRcvd uint64 // Lifetime incentives received + Status VelocityAccountStatus // Account status + CreatedAt uint32 // Block height when created + UpdatedAt uint32 // Last update block +} + +// ToStackItem implements stackitem.Convertible. +func (a *VelocityAccount) ToStackItem() stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)), + stackitem.NewByteArray(a.Owner.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.CurrentVelocity)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.AverageVelocity)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.LastActivityBlock))), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalInflow)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalOutflow)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantBalance)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))), + stackitem.NewByteArray([]byte(a.ExemptionReason)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalTributePaid)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalIncentivesRcvd)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CreatedAt))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.UpdatedAt))), + }) +} + +// FromStackItem implements stackitem.Convertible. +func (a *VelocityAccount) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 15 { + return errors.New("not a struct") + } + vitaID, err := arr[0].TryInteger() + if err != nil { + return err + } + a.VitaID = vitaID.Uint64() + + ownerBytes, err := arr[1].TryBytes() + if err != nil { + return err + } + a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes) + if err != nil { + return err + } + + currentVel, err := arr[2].TryInteger() + if err != nil { + return err + } + a.CurrentVelocity = currentVel.Uint64() + + avgVel, err := arr[3].TryInteger() + if err != nil { + return err + } + a.AverageVelocity = avgVel.Uint64() + + lastActivity, err := arr[4].TryInteger() + if err != nil { + return err + } + a.LastActivityBlock = uint32(lastActivity.Uint64()) + + totalIn, err := arr[5].TryInteger() + if err != nil { + return err + } + a.TotalInflow = totalIn.Uint64() + + totalOut, err := arr[6].TryInteger() + if err != nil { + return err + } + a.TotalOutflow = totalOut.Uint64() + + stagnant, err := arr[7].TryInteger() + if err != nil { + return err + } + a.StagnantBalance = stagnant.Uint64() + + hoardLevel, err := arr[8].TryInteger() + if err != nil { + return err + } + a.HoardingLevel = HoardingLevel(hoardLevel.Uint64()) + + exemptReason, err := arr[9].TryBytes() + if err != nil { + return err + } + a.ExemptionReason = string(exemptReason) + + totalTrib, err := arr[10].TryInteger() + if err != nil { + return err + } + a.TotalTributePaid = totalTrib.Uint64() + + totalInc, err := arr[11].TryInteger() + if err != nil { + return err + } + a.TotalIncentivesRcvd = totalInc.Uint64() + + status, err := arr[12].TryInteger() + if err != nil { + return err + } + a.Status = VelocityAccountStatus(status.Uint64()) + + created, err := arr[13].TryInteger() + if err != nil { + return err + } + a.CreatedAt = uint32(created.Uint64()) + + updated, err := arr[14].TryInteger() + if err != nil { + return err + } + a.UpdatedAt = uint32(updated.Uint64()) + + return nil +} + +// TributeAssessment represents a hoarding tribute assessment. +type TributeAssessment struct { + ID uint64 // Unique assessment ID + VitaID uint64 // Owner's Vita ID + Owner util.Uint160 // Owner's address + AssessmentBlock uint32 // Block when assessment made + HoardingLevel HoardingLevel // Hoarding level at assessment + StagnantAmount uint64 // Amount considered stagnant + TributeRate uint64 // Rate applied (basis points) + TributeAmount uint64 // Tribute amount due + DueBlock uint32 // Block by which tribute is due + CollectedBlock uint32 // Block when collected (0 if not) + Status AssessmentStatus // Assessment status + AppealReason string // Reason for appeal (if any) +} + +// ToStackItem implements stackitem.Convertible. +func (a *TributeAssessment) ToStackItem() stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(a.ID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)), + stackitem.NewByteArray(a.Owner.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.AssessmentBlock))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantAmount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeRate)), + stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeAmount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.DueBlock))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CollectedBlock))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))), + stackitem.NewByteArray([]byte(a.AppealReason)), + }) +} + +// FromStackItem implements stackitem.Convertible. +func (a *TributeAssessment) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 12 { + return errors.New("not a struct") + } + + id, err := arr[0].TryInteger() + if err != nil { + return err + } + a.ID = id.Uint64() + + vitaID, err := arr[1].TryInteger() + if err != nil { + return err + } + a.VitaID = vitaID.Uint64() + + ownerBytes, err := arr[2].TryBytes() + if err != nil { + return err + } + a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes) + if err != nil { + return err + } + + assessBlock, err := arr[3].TryInteger() + if err != nil { + return err + } + a.AssessmentBlock = uint32(assessBlock.Uint64()) + + hoardLevel, err := arr[4].TryInteger() + if err != nil { + return err + } + a.HoardingLevel = HoardingLevel(hoardLevel.Uint64()) + + stagnant, err := arr[5].TryInteger() + if err != nil { + return err + } + a.StagnantAmount = stagnant.Uint64() + + rate, err := arr[6].TryInteger() + if err != nil { + return err + } + a.TributeRate = rate.Uint64() + + amount, err := arr[7].TryInteger() + if err != nil { + return err + } + a.TributeAmount = amount.Uint64() + + due, err := arr[8].TryInteger() + if err != nil { + return err + } + a.DueBlock = uint32(due.Uint64()) + + collected, err := arr[9].TryInteger() + if err != nil { + return err + } + a.CollectedBlock = uint32(collected.Uint64()) + + status, err := arr[10].TryInteger() + if err != nil { + return err + } + a.Status = AssessmentStatus(status.Uint64()) + + appealReason, err := arr[11].TryBytes() + if err != nil { + return err + } + a.AppealReason = string(appealReason) + + return nil +} + +// CirculationIncentive represents a reward for resource circulation. +type CirculationIncentive struct { + ID uint64 // Unique incentive ID + VitaID uint64 // Recipient's Vita ID + Recipient util.Uint160 // Recipient's address + IncentiveType IncentiveType // Type of incentive + Amount uint64 // Incentive amount + Reason string // Reason for incentive + VelocityScore uint64 // Velocity score that triggered incentive + GrantedBlock uint32 // Block when granted + ClaimedBlock uint32 // Block when claimed (0 if not) + Claimed bool // Whether incentive was claimed +} + +// ToStackItem implements stackitem.Convertible. +func (i *CirculationIncentive) ToStackItem() stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(i.ID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(i.VitaID)), + stackitem.NewByteArray(i.Recipient.BytesBE()), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.IncentiveType))), + stackitem.NewBigInteger(new(big.Int).SetUint64(i.Amount)), + stackitem.NewByteArray([]byte(i.Reason)), + stackitem.NewBigInteger(new(big.Int).SetUint64(i.VelocityScore)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.GrantedBlock))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.ClaimedBlock))), + stackitem.NewBool(i.Claimed), + }) +} + +// FromStackItem implements stackitem.Convertible. +func (i *CirculationIncentive) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 10 { + return errors.New("not a struct") + } + + id, err := arr[0].TryInteger() + if err != nil { + return err + } + i.ID = id.Uint64() + + vitaID, err := arr[1].TryInteger() + if err != nil { + return err + } + i.VitaID = vitaID.Uint64() + + recipBytes, err := arr[2].TryBytes() + if err != nil { + return err + } + i.Recipient, err = util.Uint160DecodeBytesBE(recipBytes) + if err != nil { + return err + } + + incType, err := arr[3].TryInteger() + if err != nil { + return err + } + i.IncentiveType = IncentiveType(incType.Uint64()) + + amount, err := arr[4].TryInteger() + if err != nil { + return err + } + i.Amount = amount.Uint64() + + reason, err := arr[5].TryBytes() + if err != nil { + return err + } + i.Reason = string(reason) + + velScore, err := arr[6].TryInteger() + if err != nil { + return err + } + i.VelocityScore = velScore.Uint64() + + granted, err := arr[7].TryInteger() + if err != nil { + return err + } + i.GrantedBlock = uint32(granted.Uint64()) + + claimed, err := arr[8].TryInteger() + if err != nil { + return err + } + i.ClaimedBlock = uint32(claimed.Uint64()) + + claimedBool, err := arr[9].TryBool() + if err != nil { + return err + } + i.Claimed = claimedBool + + return nil +} + +// RedistributionRecord tracks wealth redistribution events. +type RedistributionRecord struct { + ID uint64 // Unique record ID + SourceAssessment uint64 // Source assessment ID + TotalAmount uint64 // Total amount redistributed + RecipientCount uint64 // Number of recipients + PerCapitaAmount uint64 // Amount per recipient + RedistBlock uint32 // Block when redistribution occurred + TargetCategory string // Category of recipients (e.g., "low_velocity", "all_citizens") +} + +// ToStackItem implements stackitem.Convertible. +func (r *RedistributionRecord) ToStackItem() stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(r.ID)), + stackitem.NewBigInteger(new(big.Int).SetUint64(r.SourceAssessment)), + stackitem.NewBigInteger(new(big.Int).SetUint64(r.TotalAmount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(r.RecipientCount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(r.PerCapitaAmount)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(r.RedistBlock))), + stackitem.NewByteArray([]byte(r.TargetCategory)), + }) +} + +// FromStackItem implements stackitem.Convertible. +func (r *RedistributionRecord) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 7 { + return errors.New("not a struct") + } + + id, err := arr[0].TryInteger() + if err != nil { + return err + } + r.ID = id.Uint64() + + sourceAssess, err := arr[1].TryInteger() + if err != nil { + return err + } + r.SourceAssessment = sourceAssess.Uint64() + + total, err := arr[2].TryInteger() + if err != nil { + return err + } + r.TotalAmount = total.Uint64() + + recipCount, err := arr[3].TryInteger() + if err != nil { + return err + } + r.RecipientCount = recipCount.Uint64() + + perCapita, err := arr[4].TryInteger() + if err != nil { + return err + } + r.PerCapitaAmount = perCapita.Uint64() + + redistBlock, err := arr[5].TryInteger() + if err != nil { + return err + } + r.RedistBlock = uint32(redistBlock.Uint64()) + + targetCat, err := arr[6].TryBytes() + if err != nil { + return err + } + r.TargetCategory = string(targetCat) + + return nil +} + +// TributeConfig holds configurable parameters for the Tribute system. +type TributeConfig struct { + VelocityThresholdMild uint64 // Velocity below this = mild hoarding (basis points) + VelocityThresholdModerate uint64 // Velocity below this = moderate hoarding + VelocityThresholdSevere uint64 // Velocity below this = severe hoarding + VelocityThresholdExtreme uint64 // Velocity below this = extreme hoarding + TributeRateMild uint64 // Tribute rate for mild hoarding (basis points) + TributeRateModerate uint64 // Tribute rate for moderate hoarding + TributeRateSevere uint64 // Tribute rate for severe hoarding + TributeRateExtreme uint64 // Tribute rate for extreme hoarding + IncentiveRateHigh uint64 // Incentive rate for high velocity + IncentiveRateVeryHigh uint64 // Incentive rate for very high velocity + StagnancyPeriod uint32 // Blocks before balance considered stagnant + AssessmentPeriod uint32 // Blocks between assessments + GracePeriod uint32 // Blocks before tribute due after assessment + MinBalanceForTribute uint64 // Minimum balance to assess tribute + ExemptionThreshold uint64 // Balance below which exempt from tribute +} + +// ToStackItem implements stackitem.Convertible. +func (c *TributeConfig) ToStackItem() stackitem.Item { + return stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdMild)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdModerate)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdSevere)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdExtreme)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateMild)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateModerate)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateSevere)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateExtreme)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateHigh)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateVeryHigh)), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.StagnancyPeriod))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.AssessmentPeriod))), + stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.GracePeriod))), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.MinBalanceForTribute)), + stackitem.NewBigInteger(new(big.Int).SetUint64(c.ExemptionThreshold)), + }) +} + +// FromStackItem implements stackitem.Convertible. +func (c *TributeConfig) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok || len(arr) < 15 { + return errors.New("not a struct") + } + + threshMild, err := arr[0].TryInteger() + if err != nil { + return err + } + c.VelocityThresholdMild = threshMild.Uint64() + + threshMod, err := arr[1].TryInteger() + if err != nil { + return err + } + c.VelocityThresholdModerate = threshMod.Uint64() + + threshSev, err := arr[2].TryInteger() + if err != nil { + return err + } + c.VelocityThresholdSevere = threshSev.Uint64() + + threshExt, err := arr[3].TryInteger() + if err != nil { + return err + } + c.VelocityThresholdExtreme = threshExt.Uint64() + + rateMild, err := arr[4].TryInteger() + if err != nil { + return err + } + c.TributeRateMild = rateMild.Uint64() + + rateMod, err := arr[5].TryInteger() + if err != nil { + return err + } + c.TributeRateModerate = rateMod.Uint64() + + rateSev, err := arr[6].TryInteger() + if err != nil { + return err + } + c.TributeRateSevere = rateSev.Uint64() + + rateExt, err := arr[7].TryInteger() + if err != nil { + return err + } + c.TributeRateExtreme = rateExt.Uint64() + + incHigh, err := arr[8].TryInteger() + if err != nil { + return err + } + c.IncentiveRateHigh = incHigh.Uint64() + + incVeryHigh, err := arr[9].TryInteger() + if err != nil { + return err + } + c.IncentiveRateVeryHigh = incVeryHigh.Uint64() + + stagnancy, err := arr[10].TryInteger() + if err != nil { + return err + } + c.StagnancyPeriod = uint32(stagnancy.Uint64()) + + assessPeriod, err := arr[11].TryInteger() + if err != nil { + return err + } + c.AssessmentPeriod = uint32(assessPeriod.Uint64()) + + grace, err := arr[12].TryInteger() + if err != nil { + return err + } + c.GracePeriod = uint32(grace.Uint64()) + + minBal, err := arr[13].TryInteger() + if err != nil { + return err + } + c.MinBalanceForTribute = minBal.Uint64() + + exemptThresh, err := arr[14].TryInteger() + if err != nil { + return err + } + c.ExemptionThreshold = exemptThresh.Uint64() + + return nil +} diff --git a/pkg/core/state/validator.go b/pkg/core/state/validator.go index 414e5ee..0e4b927 100644 --- a/pkg/core/state/validator.go +++ b/pkg/core/state/validator.go @@ -3,7 +3,7 @@ package state import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" ) // Validator holds the state of a validator (its key and votes balance). diff --git a/pkg/core/state/vita.go b/pkg/core/state/vita.go index 0f827a0..619d9c8 100644 --- a/pkg/core/state/vita.go +++ b/pkg/core/state/vita.go @@ -1,480 +1,480 @@ -package state - -import ( - "errors" - "fmt" - "math" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// TokenStatus represents the status of a Vita token. -type TokenStatus uint8 - -const ( - // TokenStatusActive indicates an active token. - TokenStatusActive TokenStatus = 0 - // TokenStatusSuspended indicates a temporarily suspended token. - TokenStatusSuspended TokenStatus = 1 - // TokenStatusRevoked indicates a permanently revoked token. - TokenStatusRevoked TokenStatus = 2 - // TokenStatusRecovering indicates recovery is in progress. - TokenStatusRecovering TokenStatus = 3 -) - -// DisclosureLevel represents the visibility level of an attribute. -type DisclosureLevel uint8 - -const ( - // DisclosurePrivate means only the owner can see the attribute. - DisclosurePrivate DisclosureLevel = 0 - // DisclosureVerifier means owner and designated verifiers can see it. - DisclosureVerifier DisclosureLevel = 1 - // DisclosureRole means owner and callers with specified roles can see it. - DisclosureRole DisclosureLevel = 2 - // DisclosurePublic means anyone can see the attribute. - DisclosurePublic DisclosureLevel = 3 -) - -// RecoveryStatus represents the status of a recovery request. -type RecoveryStatus uint8 - -const ( - // RecoveryStatusPending indicates recovery is pending approval. - RecoveryStatusPending RecoveryStatus = 0 - // RecoveryStatusApproved indicates recovery has been approved. - RecoveryStatusApproved RecoveryStatus = 1 - // RecoveryStatusExecuted indicates recovery has been executed. - RecoveryStatusExecuted RecoveryStatus = 2 - // RecoveryStatusDenied indicates recovery has been denied. - RecoveryStatusDenied RecoveryStatus = 3 - // RecoveryStatusExpired indicates recovery request has expired. - RecoveryStatusExpired RecoveryStatus = 4 -) - -// Vita represents a soul-bound identity token. -type Vita struct { - TokenID uint64 // Unique sequential identifier - Owner util.Uint160 // Owner's script hash - PersonHash []byte // Hash of biometric/identity proof - IsEntity bool // True if organization, false if natural person - CreatedAt uint32 // Block height when created - UpdatedAt uint32 // Block height of last modification - Status TokenStatus // Current status - StatusReason string // Reason for status change - RecoveryHash []byte // Hash of recovery mechanism - VestedUntil uint32 // Block height until which the Vita is vesting (Sybil resistance) -} - -// ToStackItem implements stackitem.Convertible interface. -func (t *Vita) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(t.TokenID))), - stackitem.NewByteArray(t.Owner.BytesBE()), - stackitem.NewByteArray(t.PersonHash), - stackitem.NewBool(t.IsEntity), - stackitem.NewBigInteger(big.NewInt(int64(t.CreatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(t.UpdatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(t.Status))), - stackitem.NewByteArray([]byte(t.StatusReason)), - stackitem.NewByteArray(t.RecoveryHash), - stackitem.NewBigInteger(big.NewInt(int64(t.VestedUntil))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (t *Vita) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 10 { - return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items)) - } - - tokenID, err := items[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid tokenID: %w", err) - } - t.TokenID = tokenID.Uint64() - - ownerBytes, err := items[1].TryBytes() - if err != nil { - return fmt.Errorf("invalid owner: %w", err) - } - t.Owner, err = util.Uint160DecodeBytesBE(ownerBytes) - if err != nil { - return fmt.Errorf("invalid owner hash: %w", err) - } - - t.PersonHash, err = items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid personHash: %w", err) - } - - isEntity, err := items[3].TryBool() - if err != nil { - return fmt.Errorf("invalid isEntity: %w", err) - } - t.IsEntity = isEntity - - createdAt, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid createdAt: %w", err) - } - t.CreatedAt = uint32(createdAt.Int64()) - - updatedAt, err := items[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid updatedAt: %w", err) - } - t.UpdatedAt = uint32(updatedAt.Int64()) - - status, err := items[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid status: %w", err) - } - t.Status = TokenStatus(status.Int64()) - - statusReasonBytes, err := items[7].TryBytes() - if err != nil { - return fmt.Errorf("invalid statusReason: %w", err) - } - t.StatusReason = string(statusReasonBytes) - - t.RecoveryHash, err = items[8].TryBytes() - if err != nil { - return fmt.Errorf("invalid recoveryHash: %w", err) - } - - vestedUntil, err := items[9].TryInteger() - if err != nil { - return fmt.Errorf("invalid vestedUntil: %w", err) - } - t.VestedUntil = uint32(vestedUntil.Int64()) - - return nil -} - -// Attribute represents an identity attribute with disclosure control. -type Attribute struct { - Key string // Attribute name - ValueHash []byte // Hash of value (privacy) - ValueEnc []byte // Encrypted value (optional) - Attestor util.Uint160 // Who attested this (script hash) - AttestedAt uint32 // Block height when attested - ExpiresAt uint32 // Optional expiration (0 = never) - Revoked bool // Whether attribute has been revoked - RevokedAt uint32 // Block height when revoked - DisclosureLevel DisclosureLevel // Visibility level -} - -// ToStackItem implements stackitem.Convertible interface. -func (a *Attribute) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte(a.Key)), - stackitem.NewByteArray(a.ValueHash), - stackitem.NewByteArray(a.ValueEnc), - stackitem.NewByteArray(a.Attestor.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(a.AttestedAt))), - stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))), - stackitem.NewBool(a.Revoked), - stackitem.NewBigInteger(big.NewInt(int64(a.RevokedAt))), - stackitem.NewBigInteger(big.NewInt(int64(a.DisclosureLevel))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (a *Attribute) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 9 { - return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) - } - - keyBytes, err := items[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid key: %w", err) - } - a.Key = string(keyBytes) - - a.ValueHash, err = items[1].TryBytes() - if err != nil { - return fmt.Errorf("invalid valueHash: %w", err) - } - - a.ValueEnc, err = items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid valueEnc: %w", err) - } - - attestorBytes, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid attestor: %w", err) - } - a.Attestor, err = util.Uint160DecodeBytesBE(attestorBytes) - if err != nil { - return fmt.Errorf("invalid attestor hash: %w", err) - } - - attestedAt, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid attestedAt: %w", err) - } - a.AttestedAt = uint32(attestedAt.Int64()) - - expiresAt, err := items[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid expiresAt: %w", err) - } - a.ExpiresAt = uint32(expiresAt.Int64()) - - a.Revoked, err = items[6].TryBool() - if err != nil { - return fmt.Errorf("invalid revoked: %w", err) - } - - revokedAt, err := items[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid revokedAt: %w", err) - } - a.RevokedAt = uint32(revokedAt.Int64()) - - disclosureLevel, err := items[8].TryInteger() - if err != nil { - return fmt.Errorf("invalid disclosureLevel: %w", err) - } - a.DisclosureLevel = DisclosureLevel(disclosureLevel.Int64()) - - return nil -} - -// AuthChallenge represents a passwordless authentication challenge. -type AuthChallenge struct { - ChallengeID util.Uint256 // Unique challenge identifier - TokenID uint64 // Associated Vita token - Nonce []byte // Random bytes to sign - CreatedAt uint32 // Block height when created - ExpiresAt uint32 // Block height when expires - Purpose string // "login", "sign", "recover" - Fulfilled bool // Whether challenge has been fulfilled - FulfilledAt uint32 // Block height when fulfilled -} - -// ToStackItem implements stackitem.Convertible interface. -func (c *AuthChallenge) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray(c.ChallengeID.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(c.TokenID))), - stackitem.NewByteArray(c.Nonce), - stackitem.NewBigInteger(big.NewInt(int64(c.CreatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(c.ExpiresAt))), - stackitem.NewByteArray([]byte(c.Purpose)), - stackitem.NewBool(c.Fulfilled), - stackitem.NewBigInteger(big.NewInt(int64(c.FulfilledAt))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (c *AuthChallenge) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 8 { - return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) - } - - challengeIDBytes, err := items[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid challengeID: %w", err) - } - c.ChallengeID, err = util.Uint256DecodeBytesBE(challengeIDBytes) - if err != nil { - return fmt.Errorf("invalid challengeID hash: %w", err) - } - - tokenID, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid tokenID: %w", err) - } - c.TokenID = tokenID.Uint64() - - c.Nonce, err = items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid nonce: %w", err) - } - - createdAt, err := items[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid createdAt: %w", err) - } - c.CreatedAt = uint32(createdAt.Int64()) - - expiresAt, err := items[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid expiresAt: %w", err) - } - c.ExpiresAt = uint32(expiresAt.Int64()) - - purposeBytes, err := items[5].TryBytes() - if err != nil { - return fmt.Errorf("invalid purpose: %w", err) - } - c.Purpose = string(purposeBytes) - - c.Fulfilled, err = items[6].TryBool() - if err != nil { - return fmt.Errorf("invalid fulfilled: %w", err) - } - - fulfilledAt, err := items[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid fulfilledAt: %w", err) - } - c.FulfilledAt = uint32(fulfilledAt.Int64()) - - return nil -} - -// RecoveryRequest represents a key recovery request. -type RecoveryRequest struct { - RequestID util.Uint256 // Unique request identifier - TokenID uint64 // Token being recovered - NewOwner util.Uint160 // Proposed new owner - Requester util.Uint160 // Who initiated recovery - Evidence []byte // Encrypted evidence hash - Approvals []util.Uint160 // Approvers who have approved - RequiredApprovals int // Required number of approvals - CreatedAt uint32 // Block height when created - DelayUntil uint32 // Block height when executable - ExpiresAt uint32 // Block height when expires - Status RecoveryStatus // Current status -} - -// ToStackItem implements stackitem.Convertible interface. -func (r *RecoveryRequest) ToStackItem() (stackitem.Item, error) { - approvals := make([]stackitem.Item, len(r.Approvals)) - for i, a := range r.Approvals { - approvals[i] = stackitem.NewByteArray(a.BytesBE()) - } - - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray(r.RequestID.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(r.TokenID))), - stackitem.NewByteArray(r.NewOwner.BytesBE()), - stackitem.NewByteArray(r.Requester.BytesBE()), - stackitem.NewByteArray(r.Evidence), - stackitem.NewArray(approvals), - stackitem.NewBigInteger(big.NewInt(int64(r.RequiredApprovals))), - stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))), - stackitem.NewBigInteger(big.NewInt(int64(r.DelayUntil))), - stackitem.NewBigInteger(big.NewInt(int64(r.ExpiresAt))), - stackitem.NewBigInteger(big.NewInt(int64(r.Status))), - }), nil -} - -// FromStackItem implements stackitem.Convertible interface. -func (r *RecoveryRequest) FromStackItem(item stackitem.Item) error { - items, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("not a struct") - } - if len(items) != 11 { - return fmt.Errorf("wrong number of elements: expected 11, got %d", len(items)) - } - - requestIDBytes, err := items[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid requestID: %w", err) - } - r.RequestID, err = util.Uint256DecodeBytesBE(requestIDBytes) - if err != nil { - return fmt.Errorf("invalid requestID hash: %w", err) - } - - tokenID, err := items[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid tokenID: %w", err) - } - r.TokenID = tokenID.Uint64() - - newOwnerBytes, err := items[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid newOwner: %w", err) - } - r.NewOwner, err = util.Uint160DecodeBytesBE(newOwnerBytes) - if err != nil { - return fmt.Errorf("invalid newOwner hash: %w", err) - } - - requesterBytes, err := items[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid requester: %w", err) - } - r.Requester, err = util.Uint160DecodeBytesBE(requesterBytes) - if err != nil { - return fmt.Errorf("invalid requester hash: %w", err) - } - - r.Evidence, err = items[4].TryBytes() - if err != nil { - return fmt.Errorf("invalid evidence: %w", err) - } - - approvalsArray, ok := items[5].Value().([]stackitem.Item) - if !ok { - return errors.New("approvals is not an array") - } - r.Approvals = make([]util.Uint160, len(approvalsArray)) - for i, a := range approvalsArray { - approvalBytes, err := a.TryBytes() - if err != nil { - return fmt.Errorf("invalid approval %d: %w", i, err) - } - r.Approvals[i], err = util.Uint160DecodeBytesBE(approvalBytes) - if err != nil { - return fmt.Errorf("invalid approval %d hash: %w", i, err) - } - } - - requiredApprovals, err := items[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid requiredApprovals: %w", err) - } - ra := requiredApprovals.Int64() - if ra < 0 || ra > math.MaxInt { - return errors.New("requiredApprovals out of range") - } - r.RequiredApprovals = int(ra) - - createdAt, err := items[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid createdAt: %w", err) - } - r.CreatedAt = uint32(createdAt.Int64()) - - delayUntil, err := items[8].TryInteger() - if err != nil { - return fmt.Errorf("invalid delayUntil: %w", err) - } - r.DelayUntil = uint32(delayUntil.Int64()) - - expiresAt, err := items[9].TryInteger() - if err != nil { - return fmt.Errorf("invalid expiresAt: %w", err) - } - r.ExpiresAt = uint32(expiresAt.Int64()) - - status, err := items[10].TryInteger() - if err != nil { - return fmt.Errorf("invalid status: %w", err) - } - r.Status = RecoveryStatus(status.Int64()) - - return nil -} +package state + +import ( + "errors" + "fmt" + "math" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// TokenStatus represents the status of a Vita token. +type TokenStatus uint8 + +const ( + // TokenStatusActive indicates an active token. + TokenStatusActive TokenStatus = 0 + // TokenStatusSuspended indicates a temporarily suspended token. + TokenStatusSuspended TokenStatus = 1 + // TokenStatusRevoked indicates a permanently revoked token. + TokenStatusRevoked TokenStatus = 2 + // TokenStatusRecovering indicates recovery is in progress. + TokenStatusRecovering TokenStatus = 3 +) + +// DisclosureLevel represents the visibility level of an attribute. +type DisclosureLevel uint8 + +const ( + // DisclosurePrivate means only the owner can see the attribute. + DisclosurePrivate DisclosureLevel = 0 + // DisclosureVerifier means owner and designated verifiers can see it. + DisclosureVerifier DisclosureLevel = 1 + // DisclosureRole means owner and callers with specified roles can see it. + DisclosureRole DisclosureLevel = 2 + // DisclosurePublic means anyone can see the attribute. + DisclosurePublic DisclosureLevel = 3 +) + +// RecoveryStatus represents the status of a recovery request. +type RecoveryStatus uint8 + +const ( + // RecoveryStatusPending indicates recovery is pending approval. + RecoveryStatusPending RecoveryStatus = 0 + // RecoveryStatusApproved indicates recovery has been approved. + RecoveryStatusApproved RecoveryStatus = 1 + // RecoveryStatusExecuted indicates recovery has been executed. + RecoveryStatusExecuted RecoveryStatus = 2 + // RecoveryStatusDenied indicates recovery has been denied. + RecoveryStatusDenied RecoveryStatus = 3 + // RecoveryStatusExpired indicates recovery request has expired. + RecoveryStatusExpired RecoveryStatus = 4 +) + +// Vita represents a soul-bound identity token. +type Vita struct { + TokenID uint64 // Unique sequential identifier + Owner util.Uint160 // Owner's script hash + PersonHash []byte // Hash of biometric/identity proof + IsEntity bool // True if organization, false if natural person + CreatedAt uint32 // Block height when created + UpdatedAt uint32 // Block height of last modification + Status TokenStatus // Current status + StatusReason string // Reason for status change + RecoveryHash []byte // Hash of recovery mechanism + VestedUntil uint32 // Block height until which the Vita is vesting (Sybil resistance) +} + +// ToStackItem implements stackitem.Convertible interface. +func (t *Vita) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(t.TokenID))), + stackitem.NewByteArray(t.Owner.BytesBE()), + stackitem.NewByteArray(t.PersonHash), + stackitem.NewBool(t.IsEntity), + stackitem.NewBigInteger(big.NewInt(int64(t.CreatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(t.UpdatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(t.Status))), + stackitem.NewByteArray([]byte(t.StatusReason)), + stackitem.NewByteArray(t.RecoveryHash), + stackitem.NewBigInteger(big.NewInt(int64(t.VestedUntil))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (t *Vita) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 10 { + return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items)) + } + + tokenID, err := items[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid tokenID: %w", err) + } + t.TokenID = tokenID.Uint64() + + ownerBytes, err := items[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid owner: %w", err) + } + t.Owner, err = util.Uint160DecodeBytesBE(ownerBytes) + if err != nil { + return fmt.Errorf("invalid owner hash: %w", err) + } + + t.PersonHash, err = items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid personHash: %w", err) + } + + isEntity, err := items[3].TryBool() + if err != nil { + return fmt.Errorf("invalid isEntity: %w", err) + } + t.IsEntity = isEntity + + createdAt, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid createdAt: %w", err) + } + t.CreatedAt = uint32(createdAt.Int64()) + + updatedAt, err := items[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid updatedAt: %w", err) + } + t.UpdatedAt = uint32(updatedAt.Int64()) + + status, err := items[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid status: %w", err) + } + t.Status = TokenStatus(status.Int64()) + + statusReasonBytes, err := items[7].TryBytes() + if err != nil { + return fmt.Errorf("invalid statusReason: %w", err) + } + t.StatusReason = string(statusReasonBytes) + + t.RecoveryHash, err = items[8].TryBytes() + if err != nil { + return fmt.Errorf("invalid recoveryHash: %w", err) + } + + vestedUntil, err := items[9].TryInteger() + if err != nil { + return fmt.Errorf("invalid vestedUntil: %w", err) + } + t.VestedUntil = uint32(vestedUntil.Int64()) + + return nil +} + +// Attribute represents an identity attribute with disclosure control. +type Attribute struct { + Key string // Attribute name + ValueHash []byte // Hash of value (privacy) + ValueEnc []byte // Encrypted value (optional) + Attestor util.Uint160 // Who attested this (script hash) + AttestedAt uint32 // Block height when attested + ExpiresAt uint32 // Optional expiration (0 = never) + Revoked bool // Whether attribute has been revoked + RevokedAt uint32 // Block height when revoked + DisclosureLevel DisclosureLevel // Visibility level +} + +// ToStackItem implements stackitem.Convertible interface. +func (a *Attribute) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray([]byte(a.Key)), + stackitem.NewByteArray(a.ValueHash), + stackitem.NewByteArray(a.ValueEnc), + stackitem.NewByteArray(a.Attestor.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(a.AttestedAt))), + stackitem.NewBigInteger(big.NewInt(int64(a.ExpiresAt))), + stackitem.NewBool(a.Revoked), + stackitem.NewBigInteger(big.NewInt(int64(a.RevokedAt))), + stackitem.NewBigInteger(big.NewInt(int64(a.DisclosureLevel))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (a *Attribute) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 9 { + return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items)) + } + + keyBytes, err := items[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid key: %w", err) + } + a.Key = string(keyBytes) + + a.ValueHash, err = items[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid valueHash: %w", err) + } + + a.ValueEnc, err = items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid valueEnc: %w", err) + } + + attestorBytes, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid attestor: %w", err) + } + a.Attestor, err = util.Uint160DecodeBytesBE(attestorBytes) + if err != nil { + return fmt.Errorf("invalid attestor hash: %w", err) + } + + attestedAt, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid attestedAt: %w", err) + } + a.AttestedAt = uint32(attestedAt.Int64()) + + expiresAt, err := items[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid expiresAt: %w", err) + } + a.ExpiresAt = uint32(expiresAt.Int64()) + + a.Revoked, err = items[6].TryBool() + if err != nil { + return fmt.Errorf("invalid revoked: %w", err) + } + + revokedAt, err := items[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid revokedAt: %w", err) + } + a.RevokedAt = uint32(revokedAt.Int64()) + + disclosureLevel, err := items[8].TryInteger() + if err != nil { + return fmt.Errorf("invalid disclosureLevel: %w", err) + } + a.DisclosureLevel = DisclosureLevel(disclosureLevel.Int64()) + + return nil +} + +// AuthChallenge represents a passwordless authentication challenge. +type AuthChallenge struct { + ChallengeID util.Uint256 // Unique challenge identifier + TokenID uint64 // Associated Vita token + Nonce []byte // Random bytes to sign + CreatedAt uint32 // Block height when created + ExpiresAt uint32 // Block height when expires + Purpose string // "login", "sign", "recover" + Fulfilled bool // Whether challenge has been fulfilled + FulfilledAt uint32 // Block height when fulfilled +} + +// ToStackItem implements stackitem.Convertible interface. +func (c *AuthChallenge) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(c.ChallengeID.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(c.TokenID))), + stackitem.NewByteArray(c.Nonce), + stackitem.NewBigInteger(big.NewInt(int64(c.CreatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(c.ExpiresAt))), + stackitem.NewByteArray([]byte(c.Purpose)), + stackitem.NewBool(c.Fulfilled), + stackitem.NewBigInteger(big.NewInt(int64(c.FulfilledAt))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (c *AuthChallenge) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 8 { + return fmt.Errorf("wrong number of elements: expected 8, got %d", len(items)) + } + + challengeIDBytes, err := items[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid challengeID: %w", err) + } + c.ChallengeID, err = util.Uint256DecodeBytesBE(challengeIDBytes) + if err != nil { + return fmt.Errorf("invalid challengeID hash: %w", err) + } + + tokenID, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid tokenID: %w", err) + } + c.TokenID = tokenID.Uint64() + + c.Nonce, err = items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid nonce: %w", err) + } + + createdAt, err := items[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid createdAt: %w", err) + } + c.CreatedAt = uint32(createdAt.Int64()) + + expiresAt, err := items[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid expiresAt: %w", err) + } + c.ExpiresAt = uint32(expiresAt.Int64()) + + purposeBytes, err := items[5].TryBytes() + if err != nil { + return fmt.Errorf("invalid purpose: %w", err) + } + c.Purpose = string(purposeBytes) + + c.Fulfilled, err = items[6].TryBool() + if err != nil { + return fmt.Errorf("invalid fulfilled: %w", err) + } + + fulfilledAt, err := items[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid fulfilledAt: %w", err) + } + c.FulfilledAt = uint32(fulfilledAt.Int64()) + + return nil +} + +// RecoveryRequest represents a key recovery request. +type RecoveryRequest struct { + RequestID util.Uint256 // Unique request identifier + TokenID uint64 // Token being recovered + NewOwner util.Uint160 // Proposed new owner + Requester util.Uint160 // Who initiated recovery + Evidence []byte // Encrypted evidence hash + Approvals []util.Uint160 // Approvers who have approved + RequiredApprovals int // Required number of approvals + CreatedAt uint32 // Block height when created + DelayUntil uint32 // Block height when executable + ExpiresAt uint32 // Block height when expires + Status RecoveryStatus // Current status +} + +// ToStackItem implements stackitem.Convertible interface. +func (r *RecoveryRequest) ToStackItem() (stackitem.Item, error) { + approvals := make([]stackitem.Item, len(r.Approvals)) + for i, a := range r.Approvals { + approvals[i] = stackitem.NewByteArray(a.BytesBE()) + } + + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(r.RequestID.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(r.TokenID))), + stackitem.NewByteArray(r.NewOwner.BytesBE()), + stackitem.NewByteArray(r.Requester.BytesBE()), + stackitem.NewByteArray(r.Evidence), + stackitem.NewArray(approvals), + stackitem.NewBigInteger(big.NewInt(int64(r.RequiredApprovals))), + stackitem.NewBigInteger(big.NewInt(int64(r.CreatedAt))), + stackitem.NewBigInteger(big.NewInt(int64(r.DelayUntil))), + stackitem.NewBigInteger(big.NewInt(int64(r.ExpiresAt))), + stackitem.NewBigInteger(big.NewInt(int64(r.Status))), + }), nil +} + +// FromStackItem implements stackitem.Convertible interface. +func (r *RecoveryRequest) FromStackItem(item stackitem.Item) error { + items, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not a struct") + } + if len(items) != 11 { + return fmt.Errorf("wrong number of elements: expected 11, got %d", len(items)) + } + + requestIDBytes, err := items[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid requestID: %w", err) + } + r.RequestID, err = util.Uint256DecodeBytesBE(requestIDBytes) + if err != nil { + return fmt.Errorf("invalid requestID hash: %w", err) + } + + tokenID, err := items[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid tokenID: %w", err) + } + r.TokenID = tokenID.Uint64() + + newOwnerBytes, err := items[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid newOwner: %w", err) + } + r.NewOwner, err = util.Uint160DecodeBytesBE(newOwnerBytes) + if err != nil { + return fmt.Errorf("invalid newOwner hash: %w", err) + } + + requesterBytes, err := items[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid requester: %w", err) + } + r.Requester, err = util.Uint160DecodeBytesBE(requesterBytes) + if err != nil { + return fmt.Errorf("invalid requester hash: %w", err) + } + + r.Evidence, err = items[4].TryBytes() + if err != nil { + return fmt.Errorf("invalid evidence: %w", err) + } + + approvalsArray, ok := items[5].Value().([]stackitem.Item) + if !ok { + return errors.New("approvals is not an array") + } + r.Approvals = make([]util.Uint160, len(approvalsArray)) + for i, a := range approvalsArray { + approvalBytes, err := a.TryBytes() + if err != nil { + return fmt.Errorf("invalid approval %d: %w", i, err) + } + r.Approvals[i], err = util.Uint160DecodeBytesBE(approvalBytes) + if err != nil { + return fmt.Errorf("invalid approval %d hash: %w", i, err) + } + } + + requiredApprovals, err := items[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid requiredApprovals: %w", err) + } + ra := requiredApprovals.Int64() + if ra < 0 || ra > math.MaxInt { + return errors.New("requiredApprovals out of range") + } + r.RequiredApprovals = int(ra) + + createdAt, err := items[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid createdAt: %w", err) + } + r.CreatedAt = uint32(createdAt.Int64()) + + delayUntil, err := items[8].TryInteger() + if err != nil { + return fmt.Errorf("invalid delayUntil: %w", err) + } + r.DelayUntil = uint32(delayUntil.Int64()) + + expiresAt, err := items[9].TryInteger() + if err != nil { + return fmt.Errorf("invalid expiresAt: %w", err) + } + r.ExpiresAt = uint32(expiresAt.Int64()) + + status, err := items[10].TryInteger() + if err != nil { + return fmt.Errorf("invalid status: %w", err) + } + r.Status = RecoveryStatus(status.Int64()) + + return nil +} diff --git a/pkg/core/state/vts.go b/pkg/core/state/vts.go index 5c10df7..3b12e9f 100644 --- a/pkg/core/state/vts.go +++ b/pkg/core/state/vts.go @@ -1,463 +1,463 @@ -package state - -import ( - "errors" - "fmt" - "math/big" - - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// Spending category constants (bitmask). -const ( - CategoryUnrestricted uint8 = 0 // No restrictions (standard money) - CategoryFood uint8 = 1 << 0 // Bit 0: Food purchases - CategoryShelter uint8 = 1 << 1 // Bit 1: Rent/utilities/housing - CategoryMedical uint8 = 1 << 2 // Bit 2: Healthcare expenses - CategoryEducation uint8 = 1 << 3 // Bit 3: Tuition/books/supplies - CategoryTransport uint8 = 1 << 4 // Bit 4: Transportation - CategoryAll uint8 = 0xFF // All categories -) - -// TxType represents the type of VTS transaction. -type TxType uint8 - -const ( - TxTypeTransfer TxType = 0 // P2P transfer (neutral) - TxTypeIncome TxType = 1 // Wages, sales, dividends (taxable) - TxTypeExpense TxType = 2 // Purchases, bills (potentially deductible) - TxTypeBenefit TxType = 3 // Government benefits (tax-exempt income) - TxTypeGift TxType = 4 // Gift (may have gift tax implications) - TxTypeTax TxType = 5 // Tax payment/withholding -) - -// VTSBalance represents the balance state of a VTS token holder. -// It tracks both unrestricted and category-restricted balances. -type VTSBalance struct { - Unrestricted big.Int // Unrestricted balance (can be spent anywhere) - Restricted map[uint8]*big.Int // Category -> restricted balance -} - -// NewVTSBalance creates a new empty VTSBalance. -func NewVTSBalance() *VTSBalance { - return &VTSBalance{ - Restricted: make(map[uint8]*big.Int), - } -} - -// Total returns the total balance (unrestricted + all restricted). -func (b *VTSBalance) Total() *big.Int { - total := new(big.Int).Set(&b.Unrestricted) - for _, amt := range b.Restricted { - total.Add(total, amt) - } - return total -} - -// ToStackItem converts VTSBalance to stackitem. -func (b *VTSBalance) ToStackItem() (stackitem.Item, error) { - // Format: [unrestricted, [[category, amount], [category, amount], ...]] - restrictedItems := make([]stackitem.Item, 0, len(b.Restricted)) - for cat, amt := range b.Restricted { - if amt.Sign() > 0 { - restrictedItems = append(restrictedItems, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(cat))), - stackitem.NewBigInteger(amt), - })) - } - } - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(&b.Unrestricted), - stackitem.NewArray(restrictedItems), - }), nil -} - -// FromStackItem converts stackitem to VTSBalance. -func (b *VTSBalance) FromStackItem(item stackitem.Item) error { - structItems, ok := item.Value().([]stackitem.Item) - if !ok || len(structItems) < 2 { - return errors.New("invalid VTSBalance structure") - } - - unrestricted, err := structItems[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid unrestricted balance: %w", err) - } - b.Unrestricted = *unrestricted - - restrictedArr, ok := structItems[1].Value().([]stackitem.Item) - if !ok { - return errors.New("invalid restricted balances array") - } - - b.Restricted = make(map[uint8]*big.Int) - for _, ri := range restrictedArr { - pair, ok := ri.Value().([]stackitem.Item) - if !ok || len(pair) != 2 { - return errors.New("invalid restricted balance pair") - } - catBI, err := pair[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid category: %w", err) - } - amt, err := pair[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid amount: %w", err) - } - b.Restricted[uint8(catBI.Int64())] = amt - } - return nil -} - -// Vendor represents a registered vendor/merchant that can accept VTS payments. -type Vendor struct { - Address util.Uint160 // Vendor's script hash - Name string // Display name (max 64 chars) - Categories uint8 // Bitmask of accepted spending categories - RegisteredAt uint32 // Block height when registered - RegisteredBy util.Uint160 // Who registered this vendor - Active bool // Whether vendor is currently active - AgeRestricted bool // Whether vendor requires age verification (e.g., alcohol, tobacco) -} - -// ToStackItem converts Vendor to stackitem. -func (v *Vendor) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray(v.Address.BytesBE()), - stackitem.NewByteArray([]byte(v.Name)), - stackitem.NewBigInteger(big.NewInt(int64(v.Categories))), - stackitem.NewBigInteger(big.NewInt(int64(v.RegisteredAt))), - stackitem.NewByteArray(v.RegisteredBy.BytesBE()), - stackitem.NewBool(v.Active), - stackitem.NewBool(v.AgeRestricted), - }), nil -} - -// FromStackItem converts stackitem to Vendor. -func (v *Vendor) FromStackItem(item stackitem.Item) error { - structItems, ok := item.Value().([]stackitem.Item) - if !ok || len(structItems) < 6 { - return errors.New("invalid Vendor structure") - } - - addrBytes, err := structItems[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid address: %w", err) - } - v.Address, err = util.Uint160DecodeBytesBE(addrBytes) - if err != nil { - return fmt.Errorf("invalid address bytes: %w", err) - } - - nameBytes, err := structItems[1].TryBytes() - if err != nil { - return fmt.Errorf("invalid name: %w", err) - } - v.Name = string(nameBytes) - - catBI, err := structItems[2].TryInteger() - if err != nil { - return fmt.Errorf("invalid categories: %w", err) - } - v.Categories = uint8(catBI.Int64()) - - regAtBI, err := structItems[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid registeredAt: %w", err) - } - v.RegisteredAt = uint32(regAtBI.Int64()) - - regByBytes, err := structItems[4].TryBytes() - if err != nil { - return fmt.Errorf("invalid registeredBy: %w", err) - } - v.RegisteredBy, err = util.Uint160DecodeBytesBE(regByBytes) - if err != nil { - return fmt.Errorf("invalid registeredBy bytes: %w", err) - } - - active, err := structItems[5].TryBool() - if err != nil { - return fmt.Errorf("invalid active: %w", err) - } - v.Active = active - - // AgeRestricted is optional for backwards compatibility - if len(structItems) >= 7 { - ageRestricted, err := structItems[6].TryBool() - if err != nil { - return fmt.Errorf("invalid ageRestricted: %w", err) - } - v.AgeRestricted = ageRestricted - } - - return nil -} - -// TransactionRecord represents a recorded VTS transaction for tax accounting. -type TransactionRecord struct { - TxHash util.Uint256 // Transaction hash - BlockHeight uint32 // When it occurred - From util.Uint160 // Sender - To util.Uint160 // Recipient - Amount big.Int // Gross amount - TxType TxType // Type of transaction - Category uint8 // Spending category (if applicable) - TaxWithheld big.Int // Tax withheld at source - TaxRate uint16 // Rate applied (basis points, e.g., 2500 = 25%) - Memo string // Optional description (max 256 chars) -} - -// ToStackItem converts TransactionRecord to stackitem. -func (t *TransactionRecord) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray(t.TxHash.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(t.BlockHeight))), - stackitem.NewByteArray(t.From.BytesBE()), - stackitem.NewByteArray(t.To.BytesBE()), - stackitem.NewBigInteger(&t.Amount), - stackitem.NewBigInteger(big.NewInt(int64(t.TxType))), - stackitem.NewBigInteger(big.NewInt(int64(t.Category))), - stackitem.NewBigInteger(&t.TaxWithheld), - stackitem.NewBigInteger(big.NewInt(int64(t.TaxRate))), - stackitem.NewByteArray([]byte(t.Memo)), - }), nil -} - -// FromStackItem converts stackitem to TransactionRecord. -func (t *TransactionRecord) FromStackItem(item stackitem.Item) error { - structItems, ok := item.Value().([]stackitem.Item) - if !ok || len(structItems) < 10 { - return errors.New("invalid TransactionRecord structure") - } - - txHashBytes, err := structItems[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid txHash: %w", err) - } - t.TxHash, err = util.Uint256DecodeBytesBE(txHashBytes) - if err != nil { - return fmt.Errorf("invalid txHash bytes: %w", err) - } - - blockHeightBI, err := structItems[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid blockHeight: %w", err) - } - t.BlockHeight = uint32(blockHeightBI.Int64()) - - fromBytes, err := structItems[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid from: %w", err) - } - t.From, err = util.Uint160DecodeBytesBE(fromBytes) - if err != nil { - return fmt.Errorf("invalid from bytes: %w", err) - } - - toBytes, err := structItems[3].TryBytes() - if err != nil { - return fmt.Errorf("invalid to: %w", err) - } - t.To, err = util.Uint160DecodeBytesBE(toBytes) - if err != nil { - return fmt.Errorf("invalid to bytes: %w", err) - } - - amount, err := structItems[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid amount: %w", err) - } - t.Amount = *amount - - txTypeBI, err := structItems[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid txType: %w", err) - } - t.TxType = TxType(txTypeBI.Int64()) - - catBI, err := structItems[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid category: %w", err) - } - t.Category = uint8(catBI.Int64()) - - taxWithheld, err := structItems[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid taxWithheld: %w", err) - } - t.TaxWithheld = *taxWithheld - - taxRateBI, err := structItems[8].TryInteger() - if err != nil { - return fmt.Errorf("invalid taxRate: %w", err) - } - t.TaxRate = uint16(taxRateBI.Int64()) - - memoBytes, err := structItems[9].TryBytes() - if err != nil { - return fmt.Errorf("invalid memo: %w", err) - } - t.Memo = string(memoBytes) - - return nil -} - -// TaxConfig represents the tax configuration for VTS. -type TaxConfig struct { - DefaultIncomeRate uint16 // Default income tax rate (basis points) - DefaultSalesRate uint16 // Default sales tax rate (basis points) - TreasuryAddress util.Uint160 // Where taxes are sent - ExemptCategories uint8 // Categories exempt from sales tax (bitmask) -} - -// ToStackItem converts TaxConfig to stackitem. -func (c *TaxConfig) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(c.DefaultIncomeRate))), - stackitem.NewBigInteger(big.NewInt(int64(c.DefaultSalesRate))), - stackitem.NewByteArray(c.TreasuryAddress.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(c.ExemptCategories))), - }), nil -} - -// FromStackItem converts stackitem to TaxConfig. -func (c *TaxConfig) FromStackItem(item stackitem.Item) error { - structItems, ok := item.Value().([]stackitem.Item) - if !ok || len(structItems) < 4 { - return errors.New("invalid TaxConfig structure") - } - - incomeRateBI, err := structItems[0].TryInteger() - if err != nil { - return fmt.Errorf("invalid defaultIncomeRate: %w", err) - } - c.DefaultIncomeRate = uint16(incomeRateBI.Int64()) - - salesRateBI, err := structItems[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid defaultSalesRate: %w", err) - } - c.DefaultSalesRate = uint16(salesRateBI.Int64()) - - treasuryBytes, err := structItems[2].TryBytes() - if err != nil { - return fmt.Errorf("invalid treasuryAddress: %w", err) - } - c.TreasuryAddress, err = util.Uint160DecodeBytesBE(treasuryBytes) - if err != nil { - return fmt.Errorf("invalid treasuryAddress bytes: %w", err) - } - - exemptCatBI, err := structItems[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid exemptCategories: %w", err) - } - c.ExemptCategories = uint8(exemptCatBI.Int64()) - - return nil -} - -// TaxSummary represents a tax summary for a specific period. -type TaxSummary struct { - Account util.Uint160 // Account address - StartBlock uint32 // Period start - EndBlock uint32 // Period end - TotalIncome big.Int // Total taxable income - TotalBenefits big.Int // Total tax-exempt benefits - TotalExpenses big.Int // Total expenses (for deductions) - DeductibleExpenses big.Int // Expenses that are deductible - TaxWithheld big.Int // Total tax withheld - EstimatedOwed big.Int // Estimated tax owed (income * rate) - Balance big.Int // TaxWithheld - EstimatedOwed -} - -// ToStackItem converts TaxSummary to stackitem. -func (s *TaxSummary) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray(s.Account.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(s.StartBlock))), - stackitem.NewBigInteger(big.NewInt(int64(s.EndBlock))), - stackitem.NewBigInteger(&s.TotalIncome), - stackitem.NewBigInteger(&s.TotalBenefits), - stackitem.NewBigInteger(&s.TotalExpenses), - stackitem.NewBigInteger(&s.DeductibleExpenses), - stackitem.NewBigInteger(&s.TaxWithheld), - stackitem.NewBigInteger(&s.EstimatedOwed), - stackitem.NewBigInteger(&s.Balance), - }), nil -} - -// FromStackItem converts stackitem to TaxSummary. -func (s *TaxSummary) FromStackItem(item stackitem.Item) error { - structItems, ok := item.Value().([]stackitem.Item) - if !ok || len(structItems) < 10 { - return errors.New("invalid TaxSummary structure") - } - - accountBytes, err := structItems[0].TryBytes() - if err != nil { - return fmt.Errorf("invalid account: %w", err) - } - s.Account, err = util.Uint160DecodeBytesBE(accountBytes) - if err != nil { - return fmt.Errorf("invalid account bytes: %w", err) - } - - startBI, err := structItems[1].TryInteger() - if err != nil { - return fmt.Errorf("invalid startBlock: %w", err) - } - s.StartBlock = uint32(startBI.Int64()) - - endBI, err := structItems[2].TryInteger() - if err != nil { - return fmt.Errorf("invalid endBlock: %w", err) - } - s.EndBlock = uint32(endBI.Int64()) - - totalIncome, err := structItems[3].TryInteger() - if err != nil { - return fmt.Errorf("invalid totalIncome: %w", err) - } - s.TotalIncome = *totalIncome - - totalBenefits, err := structItems[4].TryInteger() - if err != nil { - return fmt.Errorf("invalid totalBenefits: %w", err) - } - s.TotalBenefits = *totalBenefits - - totalExpenses, err := structItems[5].TryInteger() - if err != nil { - return fmt.Errorf("invalid totalExpenses: %w", err) - } - s.TotalExpenses = *totalExpenses - - deductible, err := structItems[6].TryInteger() - if err != nil { - return fmt.Errorf("invalid deductibleExpenses: %w", err) - } - s.DeductibleExpenses = *deductible - - taxWithheld, err := structItems[7].TryInteger() - if err != nil { - return fmt.Errorf("invalid taxWithheld: %w", err) - } - s.TaxWithheld = *taxWithheld - - estimatedOwed, err := structItems[8].TryInteger() - if err != nil { - return fmt.Errorf("invalid estimatedOwed: %w", err) - } - s.EstimatedOwed = *estimatedOwed - - balance, err := structItems[9].TryInteger() - if err != nil { - return fmt.Errorf("invalid balance: %w", err) - } - s.Balance = *balance - - return nil -} +package state + +import ( + "errors" + "fmt" + "math/big" + + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// Spending category constants (bitmask). +const ( + CategoryUnrestricted uint8 = 0 // No restrictions (standard money) + CategoryFood uint8 = 1 << 0 // Bit 0: Food purchases + CategoryShelter uint8 = 1 << 1 // Bit 1: Rent/utilities/housing + CategoryMedical uint8 = 1 << 2 // Bit 2: Healthcare expenses + CategoryEducation uint8 = 1 << 3 // Bit 3: Tuition/books/supplies + CategoryTransport uint8 = 1 << 4 // Bit 4: Transportation + CategoryAll uint8 = 0xFF // All categories +) + +// TxType represents the type of VTS transaction. +type TxType uint8 + +const ( + TxTypeTransfer TxType = 0 // P2P transfer (neutral) + TxTypeIncome TxType = 1 // Wages, sales, dividends (taxable) + TxTypeExpense TxType = 2 // Purchases, bills (potentially deductible) + TxTypeBenefit TxType = 3 // Government benefits (tax-exempt income) + TxTypeGift TxType = 4 // Gift (may have gift tax implications) + TxTypeTax TxType = 5 // Tax payment/withholding +) + +// VTSBalance represents the balance state of a VTS token holder. +// It tracks both unrestricted and category-restricted balances. +type VTSBalance struct { + Unrestricted big.Int // Unrestricted balance (can be spent anywhere) + Restricted map[uint8]*big.Int // Category -> restricted balance +} + +// NewVTSBalance creates a new empty VTSBalance. +func NewVTSBalance() *VTSBalance { + return &VTSBalance{ + Restricted: make(map[uint8]*big.Int), + } +} + +// Total returns the total balance (unrestricted + all restricted). +func (b *VTSBalance) Total() *big.Int { + total := new(big.Int).Set(&b.Unrestricted) + for _, amt := range b.Restricted { + total.Add(total, amt) + } + return total +} + +// ToStackItem converts VTSBalance to stackitem. +func (b *VTSBalance) ToStackItem() (stackitem.Item, error) { + // Format: [unrestricted, [[category, amount], [category, amount], ...]] + restrictedItems := make([]stackitem.Item, 0, len(b.Restricted)) + for cat, amt := range b.Restricted { + if amt.Sign() > 0 { + restrictedItems = append(restrictedItems, stackitem.NewArray([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(cat))), + stackitem.NewBigInteger(amt), + })) + } + } + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(&b.Unrestricted), + stackitem.NewArray(restrictedItems), + }), nil +} + +// FromStackItem converts stackitem to VTSBalance. +func (b *VTSBalance) FromStackItem(item stackitem.Item) error { + structItems, ok := item.Value().([]stackitem.Item) + if !ok || len(structItems) < 2 { + return errors.New("invalid VTSBalance structure") + } + + unrestricted, err := structItems[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid unrestricted balance: %w", err) + } + b.Unrestricted = *unrestricted + + restrictedArr, ok := structItems[1].Value().([]stackitem.Item) + if !ok { + return errors.New("invalid restricted balances array") + } + + b.Restricted = make(map[uint8]*big.Int) + for _, ri := range restrictedArr { + pair, ok := ri.Value().([]stackitem.Item) + if !ok || len(pair) != 2 { + return errors.New("invalid restricted balance pair") + } + catBI, err := pair[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid category: %w", err) + } + amt, err := pair[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid amount: %w", err) + } + b.Restricted[uint8(catBI.Int64())] = amt + } + return nil +} + +// Vendor represents a registered vendor/merchant that can accept VTS payments. +type Vendor struct { + Address util.Uint160 // Vendor's script hash + Name string // Display name (max 64 chars) + Categories uint8 // Bitmask of accepted spending categories + RegisteredAt uint32 // Block height when registered + RegisteredBy util.Uint160 // Who registered this vendor + Active bool // Whether vendor is currently active + AgeRestricted bool // Whether vendor requires age verification (e.g., alcohol, tobacco) +} + +// ToStackItem converts Vendor to stackitem. +func (v *Vendor) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(v.Address.BytesBE()), + stackitem.NewByteArray([]byte(v.Name)), + stackitem.NewBigInteger(big.NewInt(int64(v.Categories))), + stackitem.NewBigInteger(big.NewInt(int64(v.RegisteredAt))), + stackitem.NewByteArray(v.RegisteredBy.BytesBE()), + stackitem.NewBool(v.Active), + stackitem.NewBool(v.AgeRestricted), + }), nil +} + +// FromStackItem converts stackitem to Vendor. +func (v *Vendor) FromStackItem(item stackitem.Item) error { + structItems, ok := item.Value().([]stackitem.Item) + if !ok || len(structItems) < 6 { + return errors.New("invalid Vendor structure") + } + + addrBytes, err := structItems[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid address: %w", err) + } + v.Address, err = util.Uint160DecodeBytesBE(addrBytes) + if err != nil { + return fmt.Errorf("invalid address bytes: %w", err) + } + + nameBytes, err := structItems[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid name: %w", err) + } + v.Name = string(nameBytes) + + catBI, err := structItems[2].TryInteger() + if err != nil { + return fmt.Errorf("invalid categories: %w", err) + } + v.Categories = uint8(catBI.Int64()) + + regAtBI, err := structItems[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid registeredAt: %w", err) + } + v.RegisteredAt = uint32(regAtBI.Int64()) + + regByBytes, err := structItems[4].TryBytes() + if err != nil { + return fmt.Errorf("invalid registeredBy: %w", err) + } + v.RegisteredBy, err = util.Uint160DecodeBytesBE(regByBytes) + if err != nil { + return fmt.Errorf("invalid registeredBy bytes: %w", err) + } + + active, err := structItems[5].TryBool() + if err != nil { + return fmt.Errorf("invalid active: %w", err) + } + v.Active = active + + // AgeRestricted is optional for backwards compatibility + if len(structItems) >= 7 { + ageRestricted, err := structItems[6].TryBool() + if err != nil { + return fmt.Errorf("invalid ageRestricted: %w", err) + } + v.AgeRestricted = ageRestricted + } + + return nil +} + +// TransactionRecord represents a recorded VTS transaction for tax accounting. +type TransactionRecord struct { + TxHash util.Uint256 // Transaction hash + BlockHeight uint32 // When it occurred + From util.Uint160 // Sender + To util.Uint160 // Recipient + Amount big.Int // Gross amount + TxType TxType // Type of transaction + Category uint8 // Spending category (if applicable) + TaxWithheld big.Int // Tax withheld at source + TaxRate uint16 // Rate applied (basis points, e.g., 2500 = 25%) + Memo string // Optional description (max 256 chars) +} + +// ToStackItem converts TransactionRecord to stackitem. +func (t *TransactionRecord) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(t.TxHash.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(t.BlockHeight))), + stackitem.NewByteArray(t.From.BytesBE()), + stackitem.NewByteArray(t.To.BytesBE()), + stackitem.NewBigInteger(&t.Amount), + stackitem.NewBigInteger(big.NewInt(int64(t.TxType))), + stackitem.NewBigInteger(big.NewInt(int64(t.Category))), + stackitem.NewBigInteger(&t.TaxWithheld), + stackitem.NewBigInteger(big.NewInt(int64(t.TaxRate))), + stackitem.NewByteArray([]byte(t.Memo)), + }), nil +} + +// FromStackItem converts stackitem to TransactionRecord. +func (t *TransactionRecord) FromStackItem(item stackitem.Item) error { + structItems, ok := item.Value().([]stackitem.Item) + if !ok || len(structItems) < 10 { + return errors.New("invalid TransactionRecord structure") + } + + txHashBytes, err := structItems[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid txHash: %w", err) + } + t.TxHash, err = util.Uint256DecodeBytesBE(txHashBytes) + if err != nil { + return fmt.Errorf("invalid txHash bytes: %w", err) + } + + blockHeightBI, err := structItems[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid blockHeight: %w", err) + } + t.BlockHeight = uint32(blockHeightBI.Int64()) + + fromBytes, err := structItems[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid from: %w", err) + } + t.From, err = util.Uint160DecodeBytesBE(fromBytes) + if err != nil { + return fmt.Errorf("invalid from bytes: %w", err) + } + + toBytes, err := structItems[3].TryBytes() + if err != nil { + return fmt.Errorf("invalid to: %w", err) + } + t.To, err = util.Uint160DecodeBytesBE(toBytes) + if err != nil { + return fmt.Errorf("invalid to bytes: %w", err) + } + + amount, err := structItems[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid amount: %w", err) + } + t.Amount = *amount + + txTypeBI, err := structItems[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid txType: %w", err) + } + t.TxType = TxType(txTypeBI.Int64()) + + catBI, err := structItems[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid category: %w", err) + } + t.Category = uint8(catBI.Int64()) + + taxWithheld, err := structItems[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid taxWithheld: %w", err) + } + t.TaxWithheld = *taxWithheld + + taxRateBI, err := structItems[8].TryInteger() + if err != nil { + return fmt.Errorf("invalid taxRate: %w", err) + } + t.TaxRate = uint16(taxRateBI.Int64()) + + memoBytes, err := structItems[9].TryBytes() + if err != nil { + return fmt.Errorf("invalid memo: %w", err) + } + t.Memo = string(memoBytes) + + return nil +} + +// TaxConfig represents the tax configuration for VTS. +type TaxConfig struct { + DefaultIncomeRate uint16 // Default income tax rate (basis points) + DefaultSalesRate uint16 // Default sales tax rate (basis points) + TreasuryAddress util.Uint160 // Where taxes are sent + ExemptCategories uint8 // Categories exempt from sales tax (bitmask) +} + +// ToStackItem converts TaxConfig to stackitem. +func (c *TaxConfig) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewBigInteger(big.NewInt(int64(c.DefaultIncomeRate))), + stackitem.NewBigInteger(big.NewInt(int64(c.DefaultSalesRate))), + stackitem.NewByteArray(c.TreasuryAddress.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(c.ExemptCategories))), + }), nil +} + +// FromStackItem converts stackitem to TaxConfig. +func (c *TaxConfig) FromStackItem(item stackitem.Item) error { + structItems, ok := item.Value().([]stackitem.Item) + if !ok || len(structItems) < 4 { + return errors.New("invalid TaxConfig structure") + } + + incomeRateBI, err := structItems[0].TryInteger() + if err != nil { + return fmt.Errorf("invalid defaultIncomeRate: %w", err) + } + c.DefaultIncomeRate = uint16(incomeRateBI.Int64()) + + salesRateBI, err := structItems[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid defaultSalesRate: %w", err) + } + c.DefaultSalesRate = uint16(salesRateBI.Int64()) + + treasuryBytes, err := structItems[2].TryBytes() + if err != nil { + return fmt.Errorf("invalid treasuryAddress: %w", err) + } + c.TreasuryAddress, err = util.Uint160DecodeBytesBE(treasuryBytes) + if err != nil { + return fmt.Errorf("invalid treasuryAddress bytes: %w", err) + } + + exemptCatBI, err := structItems[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid exemptCategories: %w", err) + } + c.ExemptCategories = uint8(exemptCatBI.Int64()) + + return nil +} + +// TaxSummary represents a tax summary for a specific period. +type TaxSummary struct { + Account util.Uint160 // Account address + StartBlock uint32 // Period start + EndBlock uint32 // Period end + TotalIncome big.Int // Total taxable income + TotalBenefits big.Int // Total tax-exempt benefits + TotalExpenses big.Int // Total expenses (for deductions) + DeductibleExpenses big.Int // Expenses that are deductible + TaxWithheld big.Int // Total tax withheld + EstimatedOwed big.Int // Estimated tax owed (income * rate) + Balance big.Int // TaxWithheld - EstimatedOwed +} + +// ToStackItem converts TaxSummary to stackitem. +func (s *TaxSummary) ToStackItem() (stackitem.Item, error) { + return stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(s.Account.BytesBE()), + stackitem.NewBigInteger(big.NewInt(int64(s.StartBlock))), + stackitem.NewBigInteger(big.NewInt(int64(s.EndBlock))), + stackitem.NewBigInteger(&s.TotalIncome), + stackitem.NewBigInteger(&s.TotalBenefits), + stackitem.NewBigInteger(&s.TotalExpenses), + stackitem.NewBigInteger(&s.DeductibleExpenses), + stackitem.NewBigInteger(&s.TaxWithheld), + stackitem.NewBigInteger(&s.EstimatedOwed), + stackitem.NewBigInteger(&s.Balance), + }), nil +} + +// FromStackItem converts stackitem to TaxSummary. +func (s *TaxSummary) FromStackItem(item stackitem.Item) error { + structItems, ok := item.Value().([]stackitem.Item) + if !ok || len(structItems) < 10 { + return errors.New("invalid TaxSummary structure") + } + + accountBytes, err := structItems[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid account: %w", err) + } + s.Account, err = util.Uint160DecodeBytesBE(accountBytes) + if err != nil { + return fmt.Errorf("invalid account bytes: %w", err) + } + + startBI, err := structItems[1].TryInteger() + if err != nil { + return fmt.Errorf("invalid startBlock: %w", err) + } + s.StartBlock = uint32(startBI.Int64()) + + endBI, err := structItems[2].TryInteger() + if err != nil { + return fmt.Errorf("invalid endBlock: %w", err) + } + s.EndBlock = uint32(endBI.Int64()) + + totalIncome, err := structItems[3].TryInteger() + if err != nil { + return fmt.Errorf("invalid totalIncome: %w", err) + } + s.TotalIncome = *totalIncome + + totalBenefits, err := structItems[4].TryInteger() + if err != nil { + return fmt.Errorf("invalid totalBenefits: %w", err) + } + s.TotalBenefits = *totalBenefits + + totalExpenses, err := structItems[5].TryInteger() + if err != nil { + return fmt.Errorf("invalid totalExpenses: %w", err) + } + s.TotalExpenses = *totalExpenses + + deductible, err := structItems[6].TryInteger() + if err != nil { + return fmt.Errorf("invalid deductibleExpenses: %w", err) + } + s.DeductibleExpenses = *deductible + + taxWithheld, err := structItems[7].TryInteger() + if err != nil { + return fmt.Errorf("invalid taxWithheld: %w", err) + } + s.TaxWithheld = *taxWithheld + + estimatedOwed, err := structItems[8].TryInteger() + if err != nil { + return fmt.Errorf("invalid estimatedOwed: %w", err) + } + s.EstimatedOwed = *estimatedOwed + + balance, err := structItems[9].TryInteger() + if err != nil { + return fmt.Errorf("invalid balance: %w", err) + } + s.Balance = *balance + + return nil +} diff --git a/pkg/core/stateroot/callbacks.go b/pkg/core/stateroot/callbacks.go index fb5bca3..a8b1f5a 100644 --- a/pkg/core/stateroot/callbacks.go +++ b/pkg/core/stateroot/callbacks.go @@ -1,7 +1,7 @@ package stateroot import ( - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" ) // SetUpdateValidatorsCallback sets callback for sending signed root. diff --git a/pkg/core/stateroot/module.go b/pkg/core/stateroot/module.go index 43b16d0..a4ccfe0 100644 --- a/pkg/core/stateroot/module.go +++ b/pkg/core/stateroot/module.go @@ -9,16 +9,16 @@ import ( "sync/atomic" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "go.uber.org/zap" ) diff --git a/pkg/core/stateroot/store.go b/pkg/core/stateroot/store.go index 7b8d758..b879e5a 100644 --- a/pkg/core/stateroot/store.go +++ b/pkg/core/stateroot/store.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) var ( diff --git a/pkg/core/stateroot/validators.go b/pkg/core/stateroot/validators.go index 948456a..f397909 100644 --- a/pkg/core/stateroot/validators.go +++ b/pkg/core/stateroot/validators.go @@ -1,9 +1,9 @@ package stateroot import ( - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" ) // UpdateStateValidators updates list of state validator keys. diff --git a/pkg/core/statesync/module.go b/pkg/core/statesync/module.go index 5330907..8732496 100644 --- a/pkg/core/statesync/module.go +++ b/pkg/core/statesync/module.go @@ -27,16 +27,16 @@ import ( "fmt" "sync" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/stateroot" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/stateroot" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "go.uber.org/zap" ) diff --git a/pkg/core/statesync/module_test.go b/pkg/core/statesync/module_test.go index 72b3ecd..e87e381 100644 --- a/pkg/core/statesync/module_test.go +++ b/pkg/core/statesync/module_test.go @@ -5,11 +5,11 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/pkg/core/dao" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/dao" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) diff --git a/pkg/core/statesync/mptpool.go b/pkg/core/statesync/mptpool.go index b953aec..fda1e0d 100644 --- a/pkg/core/statesync/mptpool.go +++ b/pkg/core/statesync/mptpool.go @@ -5,7 +5,7 @@ import ( "slices" "sync" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Pool stores unknown MPT nodes along with the corresponding paths (single node is diff --git a/pkg/core/statesync/mptpool_test.go b/pkg/core/statesync/mptpool_test.go index 0427048..d8ec63b 100644 --- a/pkg/core/statesync/mptpool_test.go +++ b/pkg/core/statesync/mptpool_test.go @@ -4,8 +4,8 @@ import ( "encoding/hex" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/statesync/neotest_test.go b/pkg/core/statesync/neotest_test.go index 4504adf..b92e710 100644 --- a/pkg/core/statesync/neotest_test.go +++ b/pkg/core/statesync/neotest_test.go @@ -4,14 +4,14 @@ import ( "bytes" "testing" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/storage/boltdb_store.go b/pkg/core/storage/boltdb_store.go index 50a56df..2aed022 100644 --- a/pkg/core/storage/boltdb_store.go +++ b/pkg/core/storage/boltdb_store.go @@ -7,9 +7,9 @@ import ( "os" "time" - "github.com/tutus-one/tutus-bolt" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-bolt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // Bucket represents bucket used in boltdb to store all the data. diff --git a/pkg/core/storage/boltdb_store_test.go b/pkg/core/storage/boltdb_store_test.go index b9b436b..775e3a5 100644 --- a/pkg/core/storage/boltdb_store_test.go +++ b/pkg/core/storage/boltdb_store_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-bolt" - "github.com/tutus-one/tutus-bolt/errors" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-bolt" + "git.marketally.com/tutus-one/tutus-bolt/errors" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/storage/leveldb_store.go b/pkg/core/storage/leveldb_store.go index 2ce0b49..758ee2f 100644 --- a/pkg/core/storage/leveldb_store.go +++ b/pkg/core/storage/leveldb_store.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/filter" "github.com/syndtr/goleveldb/leveldb/iterator" diff --git a/pkg/core/storage/leveldb_store_test.go b/pkg/core/storage/leveldb_store_test.go index 41a6b6f..e097a65 100644 --- a/pkg/core/storage/leveldb_store_test.go +++ b/pkg/core/storage/leveldb_store_test.go @@ -3,7 +3,7 @@ package storage import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/stretchr/testify/require" "github.com/syndtr/goleveldb/leveldb" ) diff --git a/pkg/core/storage/memcached_store_test.go b/pkg/core/storage/memcached_store_test.go index 4e0f5b0..2fc3188 100644 --- a/pkg/core/storage/memcached_store_test.go +++ b/pkg/core/storage/memcached_store_test.go @@ -6,7 +6,7 @@ import ( "slices" "testing" - "github.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/random" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/storage/memory_store_test.go b/pkg/core/storage/memory_store_test.go index 7581367..e363d42 100644 --- a/pkg/core/storage/memory_store_test.go +++ b/pkg/core/storage/memory_store_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/random" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index 323aeb7..6ae8784 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -5,8 +5,8 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dboper" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dboper" "github.com/syndtr/goleveldb/leveldb/util" ) diff --git a/pkg/core/storage/store_test.go b/pkg/core/storage/store_test.go index 6350e64..052f62d 100644 --- a/pkg/core/storage/store_test.go +++ b/pkg/core/storage/store_test.go @@ -3,7 +3,7 @@ package storage import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dboper" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dboper" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/storage/store_type_test.go b/pkg/core/storage/store_type_test.go index 7050ff5..99b8ba7 100644 --- a/pkg/core/storage/store_type_test.go +++ b/pkg/core/storage/store_type_test.go @@ -4,7 +4,7 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dbconfig" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/attribute.go b/pkg/core/transaction/attribute.go index 1ea158e..4c357e6 100644 --- a/pkg/core/transaction/attribute.go +++ b/pkg/core/transaction/attribute.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // AttrValue represents a Transaction Attribute value. diff --git a/pkg/core/transaction/attribute_test.go b/pkg/core/transaction/attribute_test.go index 441ffac..283ab73 100644 --- a/pkg/core/transaction/attribute_test.go +++ b/pkg/core/transaction/attribute_test.go @@ -5,10 +5,10 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/bench_test.go b/pkg/core/transaction/bench_test.go index 62fca0f..9e484f4 100644 --- a/pkg/core/transaction/bench_test.go +++ b/pkg/core/transaction/bench_test.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "testing" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/conflicts.go b/pkg/core/transaction/conflicts.go index 0e5fac1..1d36e5c 100644 --- a/pkg/core/transaction/conflicts.go +++ b/pkg/core/transaction/conflicts.go @@ -1,8 +1,8 @@ package transaction import ( - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Conflicts represents attribute for conflicting transactions. diff --git a/pkg/core/transaction/fuzz_test.go b/pkg/core/transaction/fuzz_test.go index 3ff522a..e799319 100644 --- a/pkg/core/transaction/fuzz_test.go +++ b/pkg/core/transaction/fuzz_test.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "testing" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/not_valid_before.go b/pkg/core/transaction/not_valid_before.go index f7bf36e..5df0136 100644 --- a/pkg/core/transaction/not_valid_before.go +++ b/pkg/core/transaction/not_valid_before.go @@ -1,7 +1,7 @@ package transaction import ( - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // NotValidBefore represents attribute with the height transaction is not valid before. diff --git a/pkg/core/transaction/notary_assisted.go b/pkg/core/transaction/notary_assisted.go index 745dccc..0dd36f1 100644 --- a/pkg/core/transaction/notary_assisted.go +++ b/pkg/core/transaction/notary_assisted.go @@ -1,8 +1,8 @@ package transaction import ( - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // NotaryAssistedActivation stores the hardfork of NotaryAssisted transaction attribute diff --git a/pkg/core/transaction/oracle.go b/pkg/core/transaction/oracle.go index df0d277..758c895 100644 --- a/pkg/core/transaction/oracle.go +++ b/pkg/core/transaction/oracle.go @@ -7,7 +7,7 @@ import ( "math" "strings" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) //go:generate stringer -type=OracleResponseCode diff --git a/pkg/core/transaction/oracle_test.go b/pkg/core/transaction/oracle_test.go index 2c65331..a0bd2ff 100644 --- a/pkg/core/transaction/oracle_test.go +++ b/pkg/core/transaction/oracle_test.go @@ -5,7 +5,7 @@ import ( "math/rand/v2" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/reserved.go b/pkg/core/transaction/reserved.go index 1e785a7..cd8239a 100644 --- a/pkg/core/transaction/reserved.go +++ b/pkg/core/transaction/reserved.go @@ -1,7 +1,7 @@ package transaction import ( - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // Reserved represents an attribute for experimental or private usage. diff --git a/pkg/core/transaction/signer.go b/pkg/core/transaction/signer.go index d66472d..28e3570 100644 --- a/pkg/core/transaction/signer.go +++ b/pkg/core/transaction/signer.go @@ -5,10 +5,10 @@ import ( "math/big" "slices" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // The maximum number of AllowedContracts or AllowedGroups. diff --git a/pkg/core/transaction/signer_test.go b/pkg/core/transaction/signer_test.go index 653e2f9..419d0ca 100644 --- a/pkg/core/transaction/signer_test.go +++ b/pkg/core/transaction/signer_test.go @@ -3,9 +3,9 @@ package transaction import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index c09959b..30dde09 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -10,12 +10,12 @@ import ( "math/big" "math/rand/v2" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/util/bitfield" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util/bitfield" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) const ( diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go index ed4c65a..07e06b6 100644 --- a/pkg/core/transaction/transaction_test.go +++ b/pkg/core/transaction/transaction_test.go @@ -7,12 +7,12 @@ import ( "math" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/witness.go b/pkg/core/transaction/witness.go index 0e436b7..28e74e2 100644 --- a/pkg/core/transaction/witness.go +++ b/pkg/core/transaction/witness.go @@ -3,9 +3,9 @@ package transaction import ( "bytes" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/core/transaction/witness_condition.go b/pkg/core/transaction/witness_condition.go index 4f14314..fe53cd2 100644 --- a/pkg/core/transaction/witness_condition.go +++ b/pkg/core/transaction/witness_condition.go @@ -5,10 +5,10 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) //go:generate stringer -type=WitnessConditionType -linecomment diff --git a/pkg/core/transaction/witness_condition_test.go b/pkg/core/transaction/witness_condition_test.go index 5f3143d..3a59004 100644 --- a/pkg/core/transaction/witness_condition_test.go +++ b/pkg/core/transaction/witness_condition_test.go @@ -5,10 +5,10 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/witness_rule.go b/pkg/core/transaction/witness_rule.go index 207a5d2..74b2d38 100644 --- a/pkg/core/transaction/witness_rule.go +++ b/pkg/core/transaction/witness_rule.go @@ -5,8 +5,8 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) //go:generate stringer -type=WitnessAction -linecomment diff --git a/pkg/core/transaction/witness_rule_test.go b/pkg/core/transaction/witness_rule_test.go index c06d51f..d705ac5 100644 --- a/pkg/core/transaction/witness_rule_test.go +++ b/pkg/core/transaction/witness_rule_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/transaction/witness_test.go b/pkg/core/transaction/witness_test.go index 4c9792c..ec36394 100644 --- a/pkg/core/transaction/witness_test.go +++ b/pkg/core/transaction/witness_test.go @@ -3,7 +3,7 @@ package transaction import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" ) diff --git a/pkg/core/util.go b/pkg/core/util.go index 4f4b776..39fc25a 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -4,14 +4,14 @@ import ( "fmt" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // CreateGenesisBlock creates a genesis block based on the given configuration. diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 87c1c04..6bfaf48 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -3,11 +3,11 @@ package core import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/crypto/hash/hash.go b/pkg/crypto/hash/hash.go index adb7eb3..bb1f7ce 100644 --- a/pkg/crypto/hash/hash.go +++ b/pkg/crypto/hash/hash.go @@ -5,7 +5,7 @@ import ( "encoding/binary" "github.com/decred/dcrd/crypto/ripemd160" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Hashable represents an object which can be hashed. Usually, these objects diff --git a/pkg/crypto/hash/merkle_bench_test.go b/pkg/crypto/hash/merkle_bench_test.go index 95d2635..6ea4f8b 100644 --- a/pkg/crypto/hash/merkle_bench_test.go +++ b/pkg/crypto/hash/merkle_bench_test.go @@ -3,9 +3,9 @@ package hash_test import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/crypto/hash/merkle_tree.go b/pkg/crypto/hash/merkle_tree.go index 5867c48..405410c 100644 --- a/pkg/crypto/hash/merkle_tree.go +++ b/pkg/crypto/hash/merkle_tree.go @@ -3,7 +3,7 @@ package hash import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // MerkleTree implementation. diff --git a/pkg/crypto/hash/merkle_tree_test.go b/pkg/crypto/hash/merkle_tree_test.go index 8c4d1cd..cca589b 100644 --- a/pkg/crypto/hash/merkle_tree_test.go +++ b/pkg/crypto/hash/merkle_tree_test.go @@ -3,7 +3,7 @@ package hash import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/crypto/keys/nep2.go b/pkg/crypto/keys/nep2.go index 90c8106..799baf6 100644 --- a/pkg/crypto/keys/nep2.go +++ b/pkg/crypto/keys/nep2.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys/internal/scrypt" - "github.com/tutus-one/tutus-chain/pkg/encoding/base58" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys/internal/scrypt" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/base58" "golang.org/x/text/unicode/norm" ) diff --git a/pkg/crypto/keys/nep2_test.go b/pkg/crypto/keys/nep2_test.go index 4bd7e4d..6244dda 100644 --- a/pkg/crypto/keys/nep2_test.go +++ b/pkg/crypto/keys/nep2_test.go @@ -3,7 +3,7 @@ package keys import ( "testing" - "github.com/tutus-one/tutus-chain/internal/keytestcases" + "git.marketally.com/tutus-one/tutus-chain/internal/keytestcases" "github.com/stretchr/testify/assert" ) diff --git a/pkg/crypto/keys/private_key.go b/pkg/crypto/keys/private_key.go index fc8f710..857432a 100644 --- a/pkg/crypto/keys/private_key.go +++ b/pkg/crypto/keys/private_key.go @@ -11,9 +11,9 @@ import ( "math/big" "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-rfc6979" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-rfc6979" ) // PrivateKey represents a Neo private key and provides a high level API around diff --git a/pkg/crypto/keys/private_key_test.go b/pkg/crypto/keys/private_key_test.go index 17c354f..e73acef 100644 --- a/pkg/crypto/keys/private_key_test.go +++ b/pkg/crypto/keys/private_key_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/keytestcases" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/internal/keytestcases" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 2fa6e73..58f746c 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -13,11 +13,11 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" lru "github.com/hashicorp/golang-lru/v2" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" "gopkg.in/yaml.v3" ) diff --git a/pkg/crypto/keys/publickey_test.go b/pkg/crypto/keys/publickey_test.go index 0f226c0..654c2e1 100644 --- a/pkg/crypto/keys/publickey_test.go +++ b/pkg/crypto/keys/publickey_test.go @@ -9,7 +9,7 @@ import ( "sort" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/pkg/crypto/keys/sign_verify_test.go b/pkg/crypto/keys/sign_verify_test.go index b20c957..cc430f3 100644 --- a/pkg/crypto/keys/sign_verify_test.go +++ b/pkg/crypto/keys/sign_verify_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/decred/dcrd/dcrec/secp256k1/v4" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/crypto/keys/wif.go b/pkg/crypto/keys/wif.go index 2b90c4e..d45be26 100644 --- a/pkg/crypto/keys/wif.go +++ b/pkg/crypto/keys/wif.go @@ -3,7 +3,7 @@ package keys import ( "fmt" - "github.com/tutus-one/tutus-chain/pkg/encoding/base58" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/base58" ) const ( diff --git a/pkg/crypto/keys/wif_test.go b/pkg/crypto/keys/wif_test.go index 69f94bb..e6d5cc4 100644 --- a/pkg/crypto/keys/wif_test.go +++ b/pkg/crypto/keys/wif_test.go @@ -4,7 +4,7 @@ import ( "encoding/hex" "testing" - "github.com/tutus-one/tutus-chain/pkg/encoding/base58" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/base58" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/crypto/verifiable.go b/pkg/crypto/verifiable.go index ef95860..9a10c81 100644 --- a/pkg/crypto/verifiable.go +++ b/pkg/crypto/verifiable.go @@ -1,6 +1,6 @@ package crypto -import "github.com/tutus-one/tutus-chain/pkg/crypto/hash" +import "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" // VerifiableDecodable represents an object which can be verified and // those hashable part of which can be encoded/decoded. diff --git a/pkg/encoding/address/address.go b/pkg/encoding/address/address.go index 7e3725b..600bb34 100644 --- a/pkg/encoding/address/address.go +++ b/pkg/encoding/address/address.go @@ -3,8 +3,8 @@ package address import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/encoding/base58" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/base58" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/encoding/address/address_test.go b/pkg/encoding/address/address_test.go index 596e959..e89ab76 100644 --- a/pkg/encoding/address/address_test.go +++ b/pkg/encoding/address/address_test.go @@ -3,7 +3,7 @@ package address import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/encoding/base58/base58.go b/pkg/encoding/base58/base58.go index ee1a07e..2e0555c 100644 --- a/pkg/encoding/base58/base58.go +++ b/pkg/encoding/base58/base58.go @@ -5,7 +5,7 @@ import ( "errors" "github.com/mr-tron/base58" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" ) // CheckDecode implements base58-encoded string decoding with a hash-based diff --git a/pkg/encoding/fixedn/fixed8.go b/pkg/encoding/fixedn/fixed8.go index f7239a8..eeabe77 100644 --- a/pkg/encoding/fixedn/fixed8.go +++ b/pkg/encoding/fixedn/fixed8.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "gopkg.in/yaml.v3" ) diff --git a/pkg/encoding/fixedn/fixed8_test.go b/pkg/encoding/fixedn/fixed8_test.go index f9670b1..c6eed34 100644 --- a/pkg/encoding/fixedn/fixed8_test.go +++ b/pkg/encoding/fixedn/fixed8_test.go @@ -6,7 +6,7 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" ) diff --git a/pkg/interop/contract/contract.go b/pkg/interop/contract/contract.go index a7d272c..0a099c6 100644 --- a/pkg/interop/contract/contract.go +++ b/pkg/interop/contract/contract.go @@ -4,8 +4,8 @@ Package contract provides functions to work with contracts. package contract import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // CallFlag specifies valid call flags. diff --git a/pkg/interop/convert/convert.go b/pkg/interop/convert/convert.go index c42c7dc..dac7332 100644 --- a/pkg/interop/convert/convert.go +++ b/pkg/interop/convert/convert.go @@ -1,7 +1,7 @@ // Package convert provides functions for type conversion. package convert -import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" // ToInteger converts it's argument to an Integer. func ToInteger(v any) int { diff --git a/pkg/interop/crypto/crypto.go b/pkg/interop/crypto/crypto.go index b69e2ea..8a73c77 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -4,8 +4,8 @@ Package crypto provides an interface to cryptographic syscalls. package crypto import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // CheckMultisig checks that the script container (transaction) is signed by multiple diff --git a/pkg/interop/go.mod b/pkg/interop/go.mod index 9471ccf..1c4d84c 100644 --- a/pkg/interop/go.mod +++ b/pkg/interop/go.mod @@ -1,3 +1,3 @@ -module github.com/tutus-one/tutus-chain/pkg/interop +module git.marketally.com/tutus-one/tutus-chain/pkg/interop go 1.24 diff --git a/pkg/interop/iterator/iterator.go b/pkg/interop/iterator/iterator.go index b4e73d8..eb642c9 100644 --- a/pkg/interop/iterator/iterator.go +++ b/pkg/interop/iterator/iterator.go @@ -3,7 +3,7 @@ Package iterator provides functions to work with Neo iterators. */ package iterator -import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" // Iterator represents a Neo iterator, it's an opaque data structure that can // be properly created by storage.Find. Iterators range over key-value pairs, diff --git a/pkg/interop/lib/address/address.go b/pkg/interop/lib/address/address.go index 14e9a98..db72b82 100644 --- a/pkg/interop/lib/address/address.go +++ b/pkg/interop/lib/address/address.go @@ -1,9 +1,9 @@ package address import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/native/std" - "github.com/tutus-one/tutus-chain/pkg/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/std" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/runtime" ) // ToHash160 is a utility function that converts a Neo address to its hash diff --git a/pkg/interop/lib/contract/contract.go b/pkg/interop/lib/contract/contract.go index 63bde6d..ca59335 100644 --- a/pkg/interop/lib/contract/contract.go +++ b/pkg/interop/lib/contract/contract.go @@ -1,9 +1,9 @@ package contract import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/native/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/management" ) // CallWithVersion is a utility function that executes the previously deployed diff --git a/pkg/interop/math/math.go b/pkg/interop/math/math.go index c36c38e..5aebe08 100644 --- a/pkg/interop/math/math.go +++ b/pkg/interop/math/math.go @@ -3,7 +3,7 @@ Package math provides access to useful numeric functions available in Neo VM. */ package math -import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" // Pow returns a^b using POW VM opcode. // b must be >= 0 and <= 2^31-1. diff --git a/pkg/interop/native/crypto/crypto.go b/pkg/interop/native/crypto/crypto.go index e714e7b..ef01cae 100644 --- a/pkg/interop/native/crypto/crypto.go +++ b/pkg/interop/native/crypto/crypto.go @@ -5,9 +5,9 @@ It implements some cryptographic functions. package crypto import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents CryptoLib contract hash. diff --git a/pkg/interop/native/ledger/block.go b/pkg/interop/native/ledger/block.go index 3a59d00..e231e7e 100644 --- a/pkg/interop/native/ledger/block.go +++ b/pkg/interop/native/ledger/block.go @@ -1,6 +1,6 @@ package ledger -import "github.com/tutus-one/tutus-chain/pkg/interop" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" // Block represents a NEO block, it's a data structure where you can get // block-related data from. It's similar to the Block class in the Neo .net diff --git a/pkg/interop/native/ledger/ledger.go b/pkg/interop/native/ledger/ledger.go index 95fc97f..0ce7401 100644 --- a/pkg/interop/native/ledger/ledger.go +++ b/pkg/interop/native/ledger/ledger.go @@ -5,9 +5,9 @@ It allows to access ledger contents like transactions and blocks. package ledger import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents Ledger contract hash. diff --git a/pkg/interop/native/ledger/transaction.go b/pkg/interop/native/ledger/transaction.go index 991db7f..3157065 100644 --- a/pkg/interop/native/ledger/transaction.go +++ b/pkg/interop/native/ledger/transaction.go @@ -1,6 +1,6 @@ package ledger -import "github.com/tutus-one/tutus-chain/pkg/interop" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" // Transaction represents a NEO transaction. It's similar to Transaction class // in Neo .net framework. diff --git a/pkg/interop/native/ledger/transaction_signer.go b/pkg/interop/native/ledger/transaction_signer.go index b1b2412..7dba753 100644 --- a/pkg/interop/native/ledger/transaction_signer.go +++ b/pkg/interop/native/ledger/transaction_signer.go @@ -1,6 +1,6 @@ package ledger -import "github.com/tutus-one/tutus-chain/pkg/interop" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" // TransactionSigner represent the signer of a NEO transaction. It's similar to // Signer class in Neo .net framework. diff --git a/pkg/interop/native/lub/lub.go b/pkg/interop/native/lub/lub.go index dbd9094..ddb195b 100644 --- a/pkg/interop/native/lub/lub.go +++ b/pkg/interop/native/lub/lub.go @@ -5,9 +5,9 @@ It implements regular NEP-17 functions for Lub token. package lub import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents Lub contract hash. diff --git a/pkg/interop/native/management/contract.go b/pkg/interop/native/management/contract.go index a81265b..3a6c380 100644 --- a/pkg/interop/native/management/contract.go +++ b/pkg/interop/native/management/contract.go @@ -1,6 +1,6 @@ package management -import "github.com/tutus-one/tutus-chain/pkg/interop" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" // Contract represents a deployed contract. type Contract struct { diff --git a/pkg/interop/native/management/management.go b/pkg/interop/native/management/management.go index bdbd78e..e0348ff 100644 --- a/pkg/interop/native/management/management.go +++ b/pkg/interop/native/management/management.go @@ -5,10 +5,10 @@ It allows to get/deploy/update contracts as well as get/set deployment fee. package management import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents Management contract hash. diff --git a/pkg/interop/native/notary/notary.go b/pkg/interop/native/notary/notary.go index dec1209..3f4d083 100644 --- a/pkg/interop/native/notary/notary.go +++ b/pkg/interop/native/notary/notary.go @@ -6,9 +6,9 @@ networks. To use it, you need to have this extension enabled on the network. package notary import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents Notary contract hash. diff --git a/pkg/interop/native/oracle/oracle.go b/pkg/interop/native/oracle/oracle.go index 242b616..bc1a417 100644 --- a/pkg/interop/native/oracle/oracle.go +++ b/pkg/interop/native/oracle/oracle.go @@ -6,8 +6,8 @@ protocols. package oracle import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // These are potential response codes you get in your callback completing diff --git a/pkg/interop/native/policy/policy.go b/pkg/interop/native/policy/policy.go index 8428c9d..04475e6 100644 --- a/pkg/interop/native/policy/policy.go +++ b/pkg/interop/native/policy/policy.go @@ -5,10 +5,10 @@ This contract holds various network-wide settings. package policy import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents Policy contract hash. diff --git a/pkg/interop/native/roles/roles.go b/pkg/interop/native/roles/roles.go index a362c9a..eea363c 100644 --- a/pkg/interop/native/roles/roles.go +++ b/pkg/interop/native/roles/roles.go @@ -6,9 +6,9 @@ providing some service on the network. package roles import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents RoleManagement contract hash. diff --git a/pkg/interop/native/std/std.go b/pkg/interop/native/std/std.go index 8773974..c7211dc 100644 --- a/pkg/interop/native/std/std.go +++ b/pkg/interop/native/std/std.go @@ -5,8 +5,8 @@ It implements various useful conversion functions. package std import ( - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Hash represents StdLib contract hash. diff --git a/pkg/interop/native/tutus/tutus.go b/pkg/interop/native/tutus/tutus.go index 8d4f4b0..610c872 100644 --- a/pkg/interop/native/tutus/tutus.go +++ b/pkg/interop/native/tutus/tutus.go @@ -7,10 +7,10 @@ voting system. package tutus import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // AccountState contains info about a Tutus holder. diff --git a/pkg/interop/native/tutus/tutus_candidate.go b/pkg/interop/native/tutus/tutus_candidate.go index bff5a24..186d5e6 100644 --- a/pkg/interop/native/tutus/tutus_candidate.go +++ b/pkg/interop/native/tutus/tutus_candidate.go @@ -1,6 +1,6 @@ package tutus -import "github.com/tutus-one/tutus-chain/pkg/interop" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop" // Candidate represents a single native Tutus candidate. type Candidate struct { diff --git a/pkg/interop/runtime/engine.go b/pkg/interop/runtime/engine.go index 62270f6..e9b0b30 100644 --- a/pkg/interop/runtime/engine.go +++ b/pkg/interop/runtime/engine.go @@ -1,9 +1,9 @@ package runtime import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // GetScriptContainer returns the transaction that initially triggered current diff --git a/pkg/interop/runtime/runtime.go b/pkg/interop/runtime/runtime.go index 6905234..b008490 100644 --- a/pkg/interop/runtime/runtime.go +++ b/pkg/interop/runtime/runtime.go @@ -5,10 +5,10 @@ It has similar function to Runtime class in .net framework for Neo. package runtime import ( - "github.com/tutus-one/tutus-chain/pkg/interop" - "github.com/tutus-one/tutus-chain/pkg/interop/contract" - "github.com/tutus-one/tutus-chain/pkg/interop/native/ledger" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/ledger" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Trigger values to compare with GetTrigger result. diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index f947177..ff70d0b 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -7,8 +7,8 @@ contract. package storage import ( - "github.com/tutus-one/tutus-chain/pkg/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Context represents storage context that is mandatory for Put/Get/Delete diff --git a/pkg/interop/types.go b/pkg/interop/types.go index 7aacbbb..6487d37 100644 --- a/pkg/interop/types.go +++ b/pkg/interop/types.go @@ -1,6 +1,6 @@ package interop -import "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" +import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" const ( // Hash160Len is the length of proper Hash160 in bytes, use it to diff --git a/pkg/interop/util/util.go b/pkg/interop/util/util.go index 4f8f928..d0c2beb 100644 --- a/pkg/interop/util/util.go +++ b/pkg/interop/util/util.go @@ -4,7 +4,7 @@ Package util contains some special useful functions that are provided by compile package util import ( - "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal" ) // Abort terminates current execution, unlike exception throwing with panic() it diff --git a/pkg/io/size_test.go b/pkg/io/size_test.go index aa26bf0..1d65eb7 100644 --- a/pkg/io/size_test.go +++ b/pkg/io/size_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" ) diff --git a/pkg/network/bqueue/queue_test.go b/pkg/network/bqueue/queue_test.go index 8182e22..4708fcf 100644 --- a/pkg/network/bqueue/queue_test.go +++ b/pkg/network/bqueue/queue_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" "github.com/stretchr/testify/assert" "go.uber.org/zap/zaptest" ) diff --git a/pkg/network/bqueue_adapters.go b/pkg/network/bqueue_adapters.go index ed21cdc..c22c60e 100644 --- a/pkg/network/bqueue_adapters.go +++ b/pkg/network/bqueue_adapters.go @@ -1,8 +1,8 @@ package network import ( - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/network/bqueue" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/bqueue" ) var ( diff --git a/pkg/network/capability/capability.go b/pkg/network/capability/capability.go index d3318a2..571bb60 100644 --- a/pkg/network/capability/capability.go +++ b/pkg/network/capability/capability.go @@ -3,7 +3,7 @@ package capability import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) const ( diff --git a/pkg/network/capability/capability_test.go b/pkg/network/capability/capability_test.go index 40850c3..321e0db 100644 --- a/pkg/network/capability/capability_test.go +++ b/pkg/network/capability/capability_test.go @@ -3,7 +3,7 @@ package capability import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/compress.go b/pkg/network/compress.go index 718dba3..edac82a 100644 --- a/pkg/network/compress.go +++ b/pkg/network/compress.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "errors" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" "github.com/pierrec/lz4" ) diff --git a/pkg/network/discovery.go b/pkg/network/discovery.go index 19193f9..c492345 100644 --- a/pkg/network/discovery.go +++ b/pkg/network/discovery.go @@ -7,7 +7,7 @@ import ( "sync/atomic" "time" - "github.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" ) const ( diff --git a/pkg/network/discovery_test.go b/pkg/network/discovery_test.go index c7f2559..74a0a7e 100644 --- a/pkg/network/discovery_test.go +++ b/pkg/network/discovery_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/network/capability" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/extpool/pool.go b/pkg/network/extpool/pool.go index 109aeee..186e286 100644 --- a/pkg/network/extpool/pool.go +++ b/pkg/network/extpool/pool.go @@ -5,10 +5,10 @@ import ( "errors" "sync" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Ledger is enough of Blockchain to satisfy Pool. diff --git a/pkg/network/extpool/pool_test.go b/pkg/network/extpool/pool_test.go index a576d76..78ca167 100644 --- a/pkg/network/extpool/pool_test.go +++ b/pkg/network/extpool/pool_test.go @@ -4,10 +4,10 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/fuzz_test.go b/pkg/network/fuzz_test.go index e579506..7de5843 100644 --- a/pkg/network/fuzz_test.go +++ b/pkg/network/fuzz_test.go @@ -4,8 +4,8 @@ import ( "math/rand/v2" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 744503b..6fd119d 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -10,10 +10,10 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) diff --git a/pkg/network/message.go b/pkg/network/message.go index 40e692e..8cbb582 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" ) //go:generate stringer -type=CommandType -output=message_string.go diff --git a/pkg/network/message_test.go b/pkg/network/message_test.go index e24c303..95478c4 100644 --- a/pkg/network/message_test.go +++ b/pkg/network/message_test.go @@ -7,15 +7,15 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/capability" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/notary_feer.go b/pkg/network/notary_feer.go index 1c76b3a..d29947d 100644 --- a/pkg/network/notary_feer.go +++ b/pkg/network/notary_feer.go @@ -3,7 +3,7 @@ package network import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // NotaryFeer implements mempool.Feer interface for Notary balance handling. diff --git a/pkg/network/payload/address.go b/pkg/network/payload/address.go index 6c93172..fb6d8f5 100644 --- a/pkg/network/payload/address.go +++ b/pkg/network/payload/address.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" ) // MaxAddrsCount is the maximum number of addresses that could be packed into diff --git a/pkg/network/payload/address_test.go b/pkg/network/payload/address_test.go index 0900ad7..39c83bf 100644 --- a/pkg/network/payload/address_test.go +++ b/pkg/network/payload/address_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/extensible.go b/pkg/network/payload/extensible.go index e7b4801..44d867f 100644 --- a/pkg/network/payload/extensible.go +++ b/pkg/network/payload/extensible.go @@ -3,10 +3,10 @@ package payload import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const maxExtensibleCategorySize = 32 diff --git a/pkg/network/payload/extensible_test.go b/pkg/network/payload/extensible_test.go index bd0972f..6ef731c 100644 --- a/pkg/network/payload/extensible_test.go +++ b/pkg/network/payload/extensible_test.go @@ -4,10 +4,10 @@ import ( gio "io" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/getblockbyindex.go b/pkg/network/payload/getblockbyindex.go index 31b716b..3df5dba 100644 --- a/pkg/network/payload/getblockbyindex.go +++ b/pkg/network/payload/getblockbyindex.go @@ -3,7 +3,7 @@ package payload import ( "fmt" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // GetBlockByIndex payload. diff --git a/pkg/network/payload/getblockbyindex_test.go b/pkg/network/payload/getblockbyindex_test.go index d86ce8d..4b48b2a 100644 --- a/pkg/network/payload/getblockbyindex_test.go +++ b/pkg/network/payload/getblockbyindex_test.go @@ -3,7 +3,7 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/getblocks.go b/pkg/network/payload/getblocks.go index 6a4c55d..96671a1 100644 --- a/pkg/network/payload/getblocks.go +++ b/pkg/network/payload/getblocks.go @@ -3,8 +3,8 @@ package payload import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Maximum inventory hashes number is limited to 500. diff --git a/pkg/network/payload/getblocks_test.go b/pkg/network/payload/getblocks_test.go index d9dc0f6..a827e2b 100644 --- a/pkg/network/payload/getblocks_test.go +++ b/pkg/network/payload/getblocks_test.go @@ -3,8 +3,8 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/headers.go b/pkg/network/payload/headers.go index 07ad95b..c23f637 100644 --- a/pkg/network/payload/headers.go +++ b/pkg/network/payload/headers.go @@ -4,8 +4,8 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // Headers payload. diff --git a/pkg/network/payload/headers_test.go b/pkg/network/payload/headers_test.go index 69ad81f..a13651b 100644 --- a/pkg/network/payload/headers_test.go +++ b/pkg/network/payload/headers_test.go @@ -3,9 +3,9 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" "github.com/stretchr/testify/assert" ) diff --git a/pkg/network/payload/inventory.go b/pkg/network/payload/inventory.go index 16951fe..920ff15 100644 --- a/pkg/network/payload/inventory.go +++ b/pkg/network/payload/inventory.go @@ -1,8 +1,8 @@ package payload import ( - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // A node can broadcast the object information it owns by this message. diff --git a/pkg/network/payload/inventory_test.go b/pkg/network/payload/inventory_test.go index b421eda..433e1c5 100644 --- a/pkg/network/payload/inventory_test.go +++ b/pkg/network/payload/inventory_test.go @@ -4,9 +4,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/merkleblock.go b/pkg/network/payload/merkleblock.go index 57278f9..ab05bb1 100644 --- a/pkg/network/payload/merkleblock.go +++ b/pkg/network/payload/merkleblock.go @@ -3,9 +3,9 @@ package payload import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // MerkleBlock represents a merkle block packet payload. diff --git a/pkg/network/payload/merkleblock_test.go b/pkg/network/payload/merkleblock_test.go index e020e66..7ee1972 100644 --- a/pkg/network/payload/merkleblock_test.go +++ b/pkg/network/payload/merkleblock_test.go @@ -5,11 +5,11 @@ import ( "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) func newDumbBlock() *block.Header { diff --git a/pkg/network/payload/mptdata.go b/pkg/network/payload/mptdata.go index 38d81f4..43d3d01 100644 --- a/pkg/network/payload/mptdata.go +++ b/pkg/network/payload/mptdata.go @@ -3,7 +3,7 @@ package payload import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // MPTData represents a set of serialized MPT nodes. diff --git a/pkg/network/payload/mptdata_test.go b/pkg/network/payload/mptdata_test.go index 5db5db3..69d9e06 100644 --- a/pkg/network/payload/mptdata_test.go +++ b/pkg/network/payload/mptdata_test.go @@ -3,7 +3,7 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/mptinventory.go b/pkg/network/payload/mptinventory.go index 0bdf56b..c8f11e9 100644 --- a/pkg/network/payload/mptinventory.go +++ b/pkg/network/payload/mptinventory.go @@ -1,8 +1,8 @@ package payload import ( - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // MaxMPTHashesCount is the maximum number of the requested MPT nodes hashes. diff --git a/pkg/network/payload/mptinventory_test.go b/pkg/network/payload/mptinventory_test.go index 8a2fe31..aad60e3 100644 --- a/pkg/network/payload/mptinventory_test.go +++ b/pkg/network/payload/mptinventory_test.go @@ -3,8 +3,8 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/notary_request.go b/pkg/network/payload/notary_request.go index 7e3677f..2cc5074 100644 --- a/pkg/network/payload/notary_request.go +++ b/pkg/network/payload/notary_request.go @@ -4,12 +4,12 @@ import ( "bytes" "errors" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // P2PNotaryRequest contains main and fallback transactions for the Notary service. diff --git a/pkg/network/payload/notary_request_test.go b/pkg/network/payload/notary_request_test.go index e53882e..1054379 100644 --- a/pkg/network/payload/notary_request_test.go +++ b/pkg/network/payload/notary_request_test.go @@ -3,11 +3,11 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/network/payload/payload.go b/pkg/network/payload/payload.go index ec5d85f..7c21079 100644 --- a/pkg/network/payload/payload.go +++ b/pkg/network/payload/payload.go @@ -1,6 +1,6 @@ package payload -import "github.com/tutus-one/tutus-chain/pkg/io" +import "git.marketally.com/tutus-one/tutus-chain/pkg/io" // MaxSize is the maximum payload size in decompressed form. const MaxSize = 0x02000000 diff --git a/pkg/network/payload/ping.go b/pkg/network/payload/ping.go index e79e8b2..ebcdfe4 100644 --- a/pkg/network/payload/ping.go +++ b/pkg/network/payload/ping.go @@ -3,7 +3,7 @@ package payload import ( "time" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // Ping payload for ping/pong payloads. diff --git a/pkg/network/payload/ping_test.go b/pkg/network/payload/ping_test.go index 6558ae8..d2b024e 100644 --- a/pkg/network/payload/ping_test.go +++ b/pkg/network/payload/ping_test.go @@ -3,7 +3,7 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/assert" ) diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index 4c44eaf..c9b4d0d 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -3,9 +3,9 @@ package payload import ( "time" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" ) // MaxUserAgentLength is the limit for the user agent field. diff --git a/pkg/network/payload/version_test.go b/pkg/network/payload/version_test.go index ccacced..07cd76f 100644 --- a/pkg/network/payload/version_test.go +++ b/pkg/network/payload/version_test.go @@ -3,9 +3,9 @@ package payload import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" "github.com/stretchr/testify/assert" ) diff --git a/pkg/network/peer.go b/pkg/network/peer.go index 364ba67..2467ad7 100644 --- a/pkg/network/peer.go +++ b/pkg/network/peer.go @@ -4,7 +4,7 @@ import ( "context" "net" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" ) // PeerInfo represents the info for a connected peer. diff --git a/pkg/network/server.go b/pkg/network/server.go index 1a2a011..7542b97 100644 --- a/pkg/network/server.go +++ b/pkg/network/server.go @@ -16,22 +16,22 @@ import ( "sync/atomic" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/bqueue" - "github.com/tutus-one/tutus-chain/pkg/network/capability" - "github.com/tutus-one/tutus-chain/pkg/network/extpool" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/services/blockfetcher" - "github.com/tutus-one/tutus-chain/pkg/services/statefetcher" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/bqueue" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/extpool" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/blockfetcher" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/statefetcher" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "go.uber.org/zap" ) diff --git a/pkg/network/server_config.go b/pkg/network/server_config.go index 2c303fe..cb63ba1 100644 --- a/pkg/network/server_config.go +++ b/pkg/network/server_config.go @@ -4,8 +4,8 @@ import ( "fmt" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" "go.uber.org/zap/zapcore" ) diff --git a/pkg/network/server_test.go b/pkg/network/server_test.go index 717ceef..a67b55a 100644 --- a/pkg/network/server_test.go +++ b/pkg/network/server_test.go @@ -12,21 +12,21 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/consensus" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/network/capability" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/consensus" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" diff --git a/pkg/network/state_sync.go b/pkg/network/state_sync.go index a1ec57e..42e9c1b 100644 --- a/pkg/network/state_sync.go +++ b/pkg/network/state_sync.go @@ -1,10 +1,10 @@ package network import ( - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // StateSync represents state sync module. diff --git a/pkg/network/tcp_peer.go b/pkg/network/tcp_peer.go index ccfe6b7..db209e9 100644 --- a/pkg/network/tcp_peer.go +++ b/pkg/network/tcp_peer.go @@ -10,9 +10,9 @@ import ( "sync/atomic" "time" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/capability" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/capability" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" ) type handShakeStage uint8 diff --git a/pkg/network/tcp_peer_test.go b/pkg/network/tcp_peer_test.go index 5b45b57..4295efd 100644 --- a/pkg/network/tcp_peer_test.go +++ b/pkg/network/tcp_peer_test.go @@ -4,7 +4,7 @@ import ( "net" "testing" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/actor/actor.go b/pkg/rpcclient/actor/actor.go index d5e5fe9..2168544 100644 --- a/pkg/rpcclient/actor/actor.go +++ b/pkg/rpcclient/actor/actor.go @@ -14,15 +14,15 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) var ( diff --git a/pkg/rpcclient/actor/actor_test.go b/pkg/rpcclient/actor/actor_test.go index 553ea81..aaf5ab4 100644 --- a/pkg/rpcclient/actor/actor_test.go +++ b/pkg/rpcclient/actor/actor_test.go @@ -7,16 +7,16 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/actor/compat_test.go b/pkg/rpcclient/actor/compat_test.go index 94d3029..6813399 100644 --- a/pkg/rpcclient/actor/compat_test.go +++ b/pkg/rpcclient/actor/compat_test.go @@ -3,8 +3,8 @@ package actor_test import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" ) func TestRPCActorRPCClientCompat(t *testing.T) { diff --git a/pkg/rpcclient/actor/doc_test.go b/pkg/rpcclient/actor/doc_test.go index 4274aab..80ffc57 100644 --- a/pkg/rpcclient/actor/doc_test.go +++ b/pkg/rpcclient/actor/doc_test.go @@ -5,16 +5,16 @@ import ( "encoding/json" "os" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/policy" - sccontext "github.com/tutus-one/tutus-chain/pkg/smartcontract/context" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/policy" + sccontext "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/context" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) func ExampleActor() { diff --git a/pkg/rpcclient/actor/maker.go b/pkg/rpcclient/actor/maker.go index 8aeb42a..870c165 100644 --- a/pkg/rpcclient/actor/maker.go +++ b/pkg/rpcclient/actor/maker.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) // TransactionCheckerModifier is a callback that receives the result of diff --git a/pkg/rpcclient/actor/maker_test.go b/pkg/rpcclient/actor/maker_test.go index 3e004e1..4e6142e 100644 --- a/pkg/rpcclient/actor/maker_test.go +++ b/pkg/rpcclient/actor/maker_test.go @@ -4,9 +4,9 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/client.go b/pkg/rpcclient/client.go index 5a86c37..fd9ceb5 100644 --- a/pkg/rpcclient/client.go +++ b/pkg/rpcclient/client.go @@ -13,10 +13,10 @@ import ( "sync/atomic" "time" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/rpcclient/doc_test.go b/pkg/rpcclient/doc_test.go index 622cf0f..eaca652 100644 --- a/pkg/rpcclient/doc_test.go +++ b/pkg/rpcclient/doc_test.go @@ -5,8 +5,8 @@ import ( "fmt" "os" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" ) func Example() { diff --git a/pkg/rpcclient/gas/gas.go b/pkg/rpcclient/gas/gas.go index 9fdcf38..9c9364b 100644 --- a/pkg/rpcclient/gas/gas.go +++ b/pkg/rpcclient/gas/gas.go @@ -8,8 +8,8 @@ package for more details on NEP-17 interface. package gas import ( - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" ) // Hash stores the hash of the native GAS contract. diff --git a/pkg/rpcclient/gas/gas_test.go b/pkg/rpcclient/gas/gas_test.go index 2e05225..fe59448 100644 --- a/pkg/rpcclient/gas/gas_test.go +++ b/pkg/rpcclient/gas/gas_test.go @@ -3,9 +3,9 @@ package gas import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/invoker/compat_test.go b/pkg/rpcclient/invoker/compat_test.go index 9ed155e..151e4af 100644 --- a/pkg/rpcclient/invoker/compat_test.go +++ b/pkg/rpcclient/invoker/compat_test.go @@ -3,8 +3,8 @@ package invoker_test import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" ) func TestRPCInvokerRPCClientCompat(t *testing.T) { diff --git a/pkg/rpcclient/invoker/doc_test.go b/pkg/rpcclient/invoker/doc_test.go index 3d4e61f..0ab66c2 100644 --- a/pkg/rpcclient/invoker/doc_test.go +++ b/pkg/rpcclient/invoker/doc_test.go @@ -4,14 +4,14 @@ import ( "context" "errors" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) func ExampleInvoker() { diff --git a/pkg/rpcclient/invoker/invoker.go b/pkg/rpcclient/invoker/invoker.go index 3fbf554..d944719 100644 --- a/pkg/rpcclient/invoker/invoker.go +++ b/pkg/rpcclient/invoker/invoker.go @@ -14,11 +14,11 @@ import ( "fmt" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // DefaultIteratorResultItems is the default number of results to diff --git a/pkg/rpcclient/invoker/invoker_test.go b/pkg/rpcclient/invoker/invoker_test.go index 934e925..87283de 100644 --- a/pkg/rpcclient/invoker/invoker_test.go +++ b/pkg/rpcclient/invoker/invoker_test.go @@ -5,11 +5,11 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/local.go b/pkg/rpcclient/local.go index 69d3911..0d7cf2e 100644 --- a/pkg/rpcclient/local.go +++ b/pkg/rpcclient/local.go @@ -3,7 +3,7 @@ package rpcclient import ( "context" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" ) // InternalHook is a function signature that is required to create a local client diff --git a/pkg/rpcclient/local_test.go b/pkg/rpcclient/local_test.go index 59b081b..7c1e42a 100644 --- a/pkg/rpcclient/local_test.go +++ b/pkg/rpcclient/local_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" - "github.com/tutus-one/tutus-chain/internal/testcli" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/internal/testcli" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/management/management.go b/pkg/rpcclient/management/management.go index 45a493a..4d81c91 100644 --- a/pkg/rpcclient/management/management.go +++ b/pkg/rpcclient/management/management.go @@ -14,16 +14,16 @@ import ( "math/big" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Invoker is used by ContractReader to call various methods. diff --git a/pkg/rpcclient/management/management_test.go b/pkg/rpcclient/management/management_test.go index 3eb221a..47c0798 100644 --- a/pkg/rpcclient/management/management_test.go +++ b/pkg/rpcclient/management/management_test.go @@ -7,12 +7,12 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nep11/base.go b/pkg/rpcclient/nep11/base.go index a79df7b..12c6d01 100644 --- a/pkg/rpcclient/nep11/base.go +++ b/pkg/rpcclient/nep11/base.go @@ -16,13 +16,13 @@ import ( "unicode/utf8" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Invoker is used by reader types to call various methods. diff --git a/pkg/rpcclient/nep11/base_test.go b/pkg/rpcclient/nep11/base_test.go index 661700f..173a5fa 100644 --- a/pkg/rpcclient/nep11/base_test.go +++ b/pkg/rpcclient/nep11/base_test.go @@ -6,10 +6,10 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nep11/divisible.go b/pkg/rpcclient/nep11/divisible.go index c0ac634..eac73c8 100644 --- a/pkg/rpcclient/nep11/divisible.go +++ b/pkg/rpcclient/nep11/divisible.go @@ -5,10 +5,10 @@ import ( "math/big" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // DivisibleReader is a reader interface for divisible NEP-11 contract. diff --git a/pkg/rpcclient/nep11/divisible_test.go b/pkg/rpcclient/nep11/divisible_test.go index 0372633..2b2ec66 100644 --- a/pkg/rpcclient/nep11/divisible_test.go +++ b/pkg/rpcclient/nep11/divisible_test.go @@ -6,10 +6,10 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nep11/doc_test.go b/pkg/rpcclient/nep11/doc_test.go index a71028d..b307630 100644 --- a/pkg/rpcclient/nep11/doc_test.go +++ b/pkg/rpcclient/nep11/doc_test.go @@ -4,13 +4,13 @@ import ( "context" "math/big" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) func ExampleNonDivisibleReader() { diff --git a/pkg/rpcclient/nep11/nondivisible.go b/pkg/rpcclient/nep11/nondivisible.go index d717fa9..0f4cf59 100644 --- a/pkg/rpcclient/nep11/nondivisible.go +++ b/pkg/rpcclient/nep11/nondivisible.go @@ -1,8 +1,8 @@ package nep11 import ( - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // NonDivisibleReader is a reader interface for non-divisble NEP-11 contract. diff --git a/pkg/rpcclient/nep11/nondivisible_test.go b/pkg/rpcclient/nep11/nondivisible_test.go index e190805..fdde4fc 100644 --- a/pkg/rpcclient/nep11/nondivisible_test.go +++ b/pkg/rpcclient/nep11/nondivisible_test.go @@ -4,9 +4,9 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nep17/doc_test.go b/pkg/rpcclient/nep17/doc_test.go index 08ac2ec..451001e 100644 --- a/pkg/rpcclient/nep17/doc_test.go +++ b/pkg/rpcclient/nep17/doc_test.go @@ -4,13 +4,13 @@ import ( "context" "math/big" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) func ExampleTokenReader() { diff --git a/pkg/rpcclient/nep17/nep17.go b/pkg/rpcclient/nep17/nep17.go index 340306e..54e412d 100644 --- a/pkg/rpcclient/nep17/nep17.go +++ b/pkg/rpcclient/nep17/nep17.go @@ -11,12 +11,12 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Invoker is used by TokenReader to call various safe methods. diff --git a/pkg/rpcclient/nep17/nep17_test.go b/pkg/rpcclient/nep17/nep17_test.go index 2781c66..8c2b8c9 100644 --- a/pkg/rpcclient/nep17/nep17_test.go +++ b/pkg/rpcclient/nep17/nep17_test.go @@ -5,10 +5,10 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nep22/nep22.go b/pkg/rpcclient/nep22/nep22.go index 29b6b81..6cf6274 100644 --- a/pkg/rpcclient/nep22/nep22.go +++ b/pkg/rpcclient/nep22/nep22.go @@ -6,9 +6,9 @@ Contract provides state-changing method of upgradeable contracts. package nep22 import ( - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Actor is used by Contract wrapper to create and send transactions. diff --git a/pkg/rpcclient/nep22/nep22_test.go b/pkg/rpcclient/nep22/nep22_test.go index 2b062b4..0159842 100644 --- a/pkg/rpcclient/nep22/nep22_test.go +++ b/pkg/rpcclient/nep22/nep22_test.go @@ -4,9 +4,9 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nep24/doc_test.go b/pkg/rpcclient/nep24/doc_test.go index 6586eb1..af7c6f9 100644 --- a/pkg/rpcclient/nep24/doc_test.go +++ b/pkg/rpcclient/nep24/doc_test.go @@ -4,10 +4,10 @@ import ( "context" "math/big" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) func ExampleRoyaltyReader() { diff --git a/pkg/rpcclient/nep24/royalty.go b/pkg/rpcclient/nep24/royalty.go index 303ac31..51a126a 100644 --- a/pkg/rpcclient/nep24/royalty.go +++ b/pkg/rpcclient/nep24/royalty.go @@ -13,10 +13,10 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // RoyaltyRecipient contains information about the recipient and the royalty amount. diff --git a/pkg/rpcclient/nep24/royalty_test.go b/pkg/rpcclient/nep24/royalty_test.go index 791cfb0..38fea19 100644 --- a/pkg/rpcclient/nep24/royalty_test.go +++ b/pkg/rpcclient/nep24/royalty_test.go @@ -6,10 +6,10 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nep31/nep31.go b/pkg/rpcclient/nep31/nep31.go index 9aab5ce..88c1f21 100644 --- a/pkg/rpcclient/nep31/nep31.go +++ b/pkg/rpcclient/nep31/nep31.go @@ -6,9 +6,9 @@ Contract provides state-changing method of destroyable contracts. package nep31 import ( - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Actor is used by Contract wrapper to create and send transactions. diff --git a/pkg/rpcclient/nep31/nep31_test.go b/pkg/rpcclient/nep31/nep31_test.go index 4bdf3f4..f803c7f 100644 --- a/pkg/rpcclient/nep31/nep31_test.go +++ b/pkg/rpcclient/nep31/nep31_test.go @@ -4,8 +4,8 @@ import ( "errors" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/neptoken/base.go b/pkg/rpcclient/neptoken/base.go index ee02b15..bb03b8a 100644 --- a/pkg/rpcclient/neptoken/base.go +++ b/pkg/rpcclient/neptoken/base.go @@ -8,9 +8,9 @@ package neptoken import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/rpcclient/neptoken/base_test.go b/pkg/rpcclient/neptoken/base_test.go index c1962de..572a082 100644 --- a/pkg/rpcclient/neptoken/base_test.go +++ b/pkg/rpcclient/neptoken/base_test.go @@ -5,9 +5,9 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/neptoken/info.go b/pkg/rpcclient/neptoken/info.go index f8d3f1c..a47e9e8 100644 --- a/pkg/rpcclient/neptoken/info.go +++ b/pkg/rpcclient/neptoken/info.go @@ -3,11 +3,11 @@ package neptoken import ( "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) // InfoClient is a set of RPC methods required to get all of the NEP-11/NEP-17 diff --git a/pkg/rpcclient/neptoken/info_test.go b/pkg/rpcclient/neptoken/info_test.go index b78fd41..224c40b 100644 --- a/pkg/rpcclient/neptoken/info_test.go +++ b/pkg/rpcclient/neptoken/info_test.go @@ -5,16 +5,16 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nns/contract.go b/pkg/rpcclient/nns/contract.go index a744dbd..9feaf54 100644 --- a/pkg/rpcclient/nns/contract.go +++ b/pkg/rpcclient/nns/contract.go @@ -9,13 +9,13 @@ import ( "math/big" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // MaxNameLength is the max length of domain name. diff --git a/pkg/rpcclient/nns/contract_test.go b/pkg/rpcclient/nns/contract_test.go index 12c952c..f206998 100644 --- a/pkg/rpcclient/nns/contract_test.go +++ b/pkg/rpcclient/nns/contract_test.go @@ -6,11 +6,11 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/nns/iterators.go b/pkg/rpcclient/nns/iterators.go index 70defa4..cd5dc70 100644 --- a/pkg/rpcclient/nns/iterators.go +++ b/pkg/rpcclient/nns/iterators.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // RecordIterator is used for iterating over GetAllRecords results. diff --git a/pkg/rpcclient/nns/record.go b/pkg/rpcclient/nns/record.go index c6175e7..a8f7384 100644 --- a/pkg/rpcclient/nns/record.go +++ b/pkg/rpcclient/nns/record.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // RecordState is a type that registered entities are saved as. diff --git a/pkg/rpcclient/nns/record_test.go b/pkg/rpcclient/nns/record_test.go index a4fc4d6..050a97c 100644 --- a/pkg/rpcclient/nns/record_test.go +++ b/pkg/rpcclient/nns/record_test.go @@ -3,7 +3,7 @@ package nns import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/notary/accounts.go b/pkg/rpcclient/notary/accounts.go index 794bed3..4ec8a0f 100644 --- a/pkg/rpcclient/notary/accounts.go +++ b/pkg/rpcclient/notary/accounts.go @@ -1,12 +1,12 @@ package notary import ( - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) // FakeSimpleAccount creates a fake account belonging to the given public key. diff --git a/pkg/rpcclient/notary/accounts_test.go b/pkg/rpcclient/notary/accounts_test.go index 077c6b2..6bbeb87 100644 --- a/pkg/rpcclient/notary/accounts_test.go +++ b/pkg/rpcclient/notary/accounts_test.go @@ -3,9 +3,9 @@ package notary import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/notary/actor.go b/pkg/rpcclient/notary/actor.go index 5d6b101..aaf66d9 100644 --- a/pkg/rpcclient/notary/actor.go +++ b/pkg/rpcclient/notary/actor.go @@ -5,19 +5,19 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) var ( diff --git a/pkg/rpcclient/notary/actor_test.go b/pkg/rpcclient/notary/actor_test.go index f319041..73c8028 100644 --- a/pkg/rpcclient/notary/actor_test.go +++ b/pkg/rpcclient/notary/actor_test.go @@ -6,23 +6,23 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/notary/contract.go b/pkg/rpcclient/notary/contract.go index 6b1a15d..ff7445f 100644 --- a/pkg/rpcclient/notary/contract.go +++ b/pkg/rpcclient/notary/contract.go @@ -13,13 +13,13 @@ import ( "math" "math/big" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) const ( diff --git a/pkg/rpcclient/notary/contract_test.go b/pkg/rpcclient/notary/contract_test.go index 34aebe6..5314381 100644 --- a/pkg/rpcclient/notary/contract_test.go +++ b/pkg/rpcclient/notary/contract_test.go @@ -7,11 +7,11 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/notary/doc_test.go b/pkg/rpcclient/notary/doc_test.go index c585a23..8bee3bf 100644 --- a/pkg/rpcclient/notary/doc_test.go +++ b/pkg/rpcclient/notary/doc_test.go @@ -5,14 +5,14 @@ import ( "math/big" "time" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/notary" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/policy" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/gas" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/policy" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) func ExampleActor() { diff --git a/pkg/rpcclient/oracle/oracle.go b/pkg/rpcclient/oracle/oracle.go index 21eb7de..8de4cf8 100644 --- a/pkg/rpcclient/oracle/oracle.go +++ b/pkg/rpcclient/oracle/oracle.go @@ -9,11 +9,11 @@ package oracle import ( "math/big" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Invoker is used by ContractReader to call various methods. diff --git a/pkg/rpcclient/oracle/oracle_test.go b/pkg/rpcclient/oracle/oracle_test.go index 67ea1b5..a6beda0 100644 --- a/pkg/rpcclient/oracle/oracle_test.go +++ b/pkg/rpcclient/oracle/oracle_test.go @@ -5,10 +5,10 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/policy/policy.go b/pkg/rpcclient/policy/policy.go index 0b40b8c..afb55d5 100644 --- a/pkg/rpcclient/policy/policy.go +++ b/pkg/rpcclient/policy/policy.go @@ -12,13 +12,13 @@ import ( "fmt" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Invoker is used by ContractReader to call various methods. diff --git a/pkg/rpcclient/policy/policy_test.go b/pkg/rpcclient/policy/policy_test.go index 57e6c30..f242c3c 100644 --- a/pkg/rpcclient/policy/policy_test.go +++ b/pkg/rpcclient/policy/policy_test.go @@ -5,10 +5,10 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/rolemgmt/roles.go b/pkg/rpcclient/rolemgmt/roles.go index 27f558d..d492ac8 100644 --- a/pkg/rpcclient/rolemgmt/roles.go +++ b/pkg/rpcclient/rolemgmt/roles.go @@ -11,14 +11,14 @@ import ( "fmt" "math" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Invoker is used by ContractReader to call various methods. diff --git a/pkg/rpcclient/rolemgmt/roles_test.go b/pkg/rpcclient/rolemgmt/roles_test.go index 57bfa49..5c7c3e5 100644 --- a/pkg/rpcclient/rolemgmt/roles_test.go +++ b/pkg/rpcclient/rolemgmt/roles_test.go @@ -6,12 +6,12 @@ import ( "math" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/rpc.go b/pkg/rpcclient/rpc.go index fcb6d89..34adf48 100644 --- a/pkg/rpcclient/rpc.go +++ b/pkg/rpcclient/rpc.go @@ -8,20 +8,20 @@ import ( "fmt" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var errNetworkNotInitialized = errors.New("RPC client network is not initialized") diff --git a/pkg/rpcclient/rpc_test.go b/pkg/rpcclient/rpc_test.go index 049db59..c326892 100644 --- a/pkg/rpcclient/rpc_test.go +++ b/pkg/rpcclient/rpc_test.go @@ -14,28 +14,28 @@ import ( "time" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/tutus/doc_test.go b/pkg/rpcclient/tutus/doc_test.go index 2297323..e220e7c 100644 --- a/pkg/rpcclient/tutus/doc_test.go +++ b/pkg/rpcclient/tutus/doc_test.go @@ -6,13 +6,13 @@ import ( "math/big" "slices" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) func ExampleContractReader() { diff --git a/pkg/rpcclient/tutus/tutus.go b/pkg/rpcclient/tutus/tutus.go index 5d72f33..558a6ac 100644 --- a/pkg/rpcclient/tutus/tutus.go +++ b/pkg/rpcclient/tutus/tutus.go @@ -12,16 +12,16 @@ import ( "math/big" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) const ( diff --git a/pkg/rpcclient/tutus/tutus_test.go b/pkg/rpcclient/tutus/tutus_test.go index 92ba0cc..e829bee 100644 --- a/pkg/rpcclient/tutus/tutus_test.go +++ b/pkg/rpcclient/tutus/tutus_test.go @@ -6,12 +6,12 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/unwrap/unwrap.go b/pkg/rpcclient/unwrap/unwrap.go index 683f30b..a0c45c9 100644 --- a/pkg/rpcclient/unwrap/unwrap.go +++ b/pkg/rpcclient/unwrap/unwrap.go @@ -18,11 +18,11 @@ import ( "unicode/utf8" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) // Exception is a type used for VM fault messages (aka exceptions). If any of diff --git a/pkg/rpcclient/unwrap/unwrap_test.go b/pkg/rpcclient/unwrap/unwrap_test.go index 469b6ae..016168c 100644 --- a/pkg/rpcclient/unwrap/unwrap_test.go +++ b/pkg/rpcclient/unwrap/unwrap_test.go @@ -8,11 +8,11 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/waiter/waiter.go b/pkg/rpcclient/waiter/waiter.go index 1899e77..1cb4dd4 100644 --- a/pkg/rpcclient/waiter/waiter.go +++ b/pkg/rpcclient/waiter/waiter.go @@ -6,12 +6,12 @@ import ( "fmt" "time" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // DefaultPollRetryCount is a threshold for a number of subsequent failed diff --git a/pkg/rpcclient/waiter/waiter_test.go b/pkg/rpcclient/waiter/waiter_test.go index dfefb8f..d8ba9c1 100644 --- a/pkg/rpcclient/waiter/waiter_test.go +++ b/pkg/rpcclient/waiter/waiter_test.go @@ -9,18 +9,18 @@ import ( "time" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/rpcclient/wsclient.go b/pkg/rpcclient/wsclient.go index ca7e4e6..c924892 100644 --- a/pkg/rpcclient/wsclient.go +++ b/pkg/rpcclient/wsclient.go @@ -11,12 +11,12 @@ import ( "time" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/rpcevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/rpcevent" ) // WSClient is a websocket-enabled RPC client that can be used with appropriate @@ -44,7 +44,7 @@ import ( // still the user responsibility to unsubscribe. // // All Receive* methods provide notifications ordering and persistence guarantees. -// See https://github.com/tutus-one/tutus-chain/blob/master/docs/notifications.md#ordering-and-persistence-guarantees +// See https://git.marketally.com/tutus-one/tutus-chain/blob/master/docs/notifications.md#ordering-and-persistence-guarantees // for more details on this topic. // // Any received subscription items (blocks/transactions/notifications) are passed diff --git a/pkg/rpcclient/wsclient_test.go b/pkg/rpcclient/wsclient_test.go index 6200d1a..8046ba1 100644 --- a/pkg/rpcclient/wsclient_test.go +++ b/pkg/rpcclient/wsclient_test.go @@ -15,18 +15,18 @@ import ( "time" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/blockfetcher/blockfetcher.go b/pkg/services/blockfetcher/blockfetcher.go index 6f0056b..2ceb38a 100644 --- a/pkg/services/blockfetcher/blockfetcher.go +++ b/pkg/services/blockfetcher/blockfetcher.go @@ -12,11 +12,11 @@ import ( "sync" "sync/atomic" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/block" - gio "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/bqueue" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + gio "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/bqueue" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" "github.com/nspcc-dev/neofs-sdk-go/client" "github.com/nspcc-dev/neofs-sdk-go/container" "github.com/nspcc-dev/neofs-sdk-go/object" diff --git a/pkg/services/blockfetcher/blockfetcher_test.go b/pkg/services/blockfetcher/blockfetcher_test.go index d37a8af..4b2bec0 100644 --- a/pkg/services/blockfetcher/blockfetcher_test.go +++ b/pkg/services/blockfetcher/blockfetcher_test.go @@ -3,9 +3,9 @@ package blockfetcher import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/network/bqueue" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/bqueue" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/pkg/services/helpers/neofs/blockstorage.go b/pkg/services/helpers/neofs/blockstorage.go index ff91c8e..56b83e6 100644 --- a/pkg/services/helpers/neofs/blockstorage.go +++ b/pkg/services/helpers/neofs/blockstorage.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/pool" "github.com/nspcc-dev/neofs-sdk-go/user" diff --git a/pkg/services/helpers/neofs/neofs.go b/pkg/services/helpers/neofs/neofs.go index 4c2c702..aa8ac30 100644 --- a/pkg/services/helpers/neofs/neofs.go +++ b/pkg/services/helpers/neofs/neofs.go @@ -11,8 +11,8 @@ import ( "strings" "time" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/nspcc-dev/neofs-sdk-go/client" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" diff --git a/pkg/services/helpers/rpcbroadcaster/client.go b/pkg/services/helpers/rpcbroadcaster/client.go index 2ab8d77..2ceb416 100644 --- a/pkg/services/helpers/rpcbroadcaster/client.go +++ b/pkg/services/helpers/rpcbroadcaster/client.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" "go.uber.org/zap" ) diff --git a/pkg/services/metrics/metrics.go b/pkg/services/metrics/metrics.go index 5918974..684022f 100644 --- a/pkg/services/metrics/metrics.go +++ b/pkg/services/metrics/metrics.go @@ -8,7 +8,7 @@ import ( "net/http" "sync/atomic" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "go.uber.org/zap" ) diff --git a/pkg/services/metrics/pprof.go b/pkg/services/metrics/pprof.go index 114f3c3..6b41c31 100644 --- a/pkg/services/metrics/pprof.go +++ b/pkg/services/metrics/pprof.go @@ -4,7 +4,7 @@ import ( "net/http" "net/http/pprof" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "go.uber.org/zap" ) diff --git a/pkg/services/metrics/prometheus.go b/pkg/services/metrics/prometheus.go index 1fc49b1..a07a94e 100644 --- a/pkg/services/metrics/prometheus.go +++ b/pkg/services/metrics/prometheus.go @@ -3,7 +3,7 @@ package metrics import ( "net/http" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/zap" ) diff --git a/pkg/services/notary/core_test.go b/pkg/services/notary/core_test.go index 68e73a4..2aab006 100644 --- a/pkg/services/notary/core_test.go +++ b/pkg/services/notary/core_test.go @@ -9,28 +9,28 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/network" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/services/notary" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) diff --git a/pkg/services/notary/node.go b/pkg/services/notary/node.go index 0816cdb..bef8d6d 100644 --- a/pkg/services/notary/node.go +++ b/pkg/services/notary/node.go @@ -3,10 +3,10 @@ package notary import ( "slices" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) diff --git a/pkg/services/notary/node_test.go b/pkg/services/notary/node_test.go index f2d53e2..72fb08b 100644 --- a/pkg/services/notary/node_test.go +++ b/pkg/services/notary/node_test.go @@ -3,12 +3,12 @@ package notary import ( "testing" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) diff --git a/pkg/services/notary/notary.go b/pkg/services/notary/notary.go index df0bae8..527d70f 100644 --- a/pkg/services/notary/notary.go +++ b/pkg/services/notary/notary.go @@ -10,21 +10,21 @@ import ( "sync" "sync/atomic" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) @@ -527,7 +527,7 @@ func (n *Notary) verifyIncompleteWitnesses(tx *transaction.Transaction, nKeysExp } continue } - if !tx.Signers[i].Account.Equals(hash.Hash160(w.VerificationScript)) { // https://github.com/tutus-one/tutus-chain/pull/1658#discussion_r564265987 + if !tx.Signers[i].Account.Equals(hash.Hash160(w.VerificationScript)) { // https://git.marketally.com/tutus-one/tutus-chain/pull/1658#discussion_r564265987 return nil, fmt.Errorf("transaction should have valid verification script for signer #%d", i) } // Each verification script is allowed to have either one signature or zero signatures. If signature is provided, then need to verify it. diff --git a/pkg/services/notary/notary_test.go b/pkg/services/notary/notary_test.go index 17bb9b9..eef6441 100644 --- a/pkg/services/notary/notary_test.go +++ b/pkg/services/notary/notary_test.go @@ -3,16 +3,16 @@ package notary import ( "testing" - "github.com/tutus-one/tutus-chain/internal/fakechain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/internal/fakechain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) diff --git a/pkg/services/oracle/broadcaster/oracle.go b/pkg/services/oracle/broadcaster/oracle.go index e60726b..d388845 100644 --- a/pkg/services/oracle/broadcaster/oracle.go +++ b/pkg/services/oracle/broadcaster/oracle.go @@ -5,11 +5,11 @@ import ( "encoding/binary" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/rpcbroadcaster" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/rpcbroadcaster" "go.uber.org/zap" ) diff --git a/pkg/services/oracle/filter.go b/pkg/services/oracle/filter.go index 67ed3ee..c0bfad1 100644 --- a/pkg/services/oracle/filter.go +++ b/pkg/services/oracle/filter.go @@ -5,9 +5,9 @@ import ( "errors" "unicode/utf8" - json "github.com/tutus-one/tutus-ordered-json" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/services/oracle/jsonpath" + json "git.marketally.com/tutus-one/tutus-ordered-json" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/oracle/jsonpath" ) func filter(value []byte, path string) ([]byte, error) { diff --git a/pkg/services/oracle/jsonpath/jsonpath.go b/pkg/services/oracle/jsonpath/jsonpath.go index af2b70f..707c1d8 100644 --- a/pkg/services/oracle/jsonpath/jsonpath.go +++ b/pkg/services/oracle/jsonpath/jsonpath.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - json "github.com/tutus-one/tutus-ordered-json" + json "git.marketally.com/tutus-one/tutus-ordered-json" ) type ( diff --git a/pkg/services/oracle/jsonpath/jsonpath_test.go b/pkg/services/oracle/jsonpath/jsonpath_test.go index 439a474..c0b32ca 100644 --- a/pkg/services/oracle/jsonpath/jsonpath_test.go +++ b/pkg/services/oracle/jsonpath/jsonpath_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - json "github.com/tutus-one/tutus-ordered-json" + json "git.marketally.com/tutus-one/tutus-ordered-json" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/oracle/network.go b/pkg/services/oracle/network.go index ec9a79c..a393b9b 100644 --- a/pkg/services/oracle/network.go +++ b/pkg/services/oracle/network.go @@ -7,7 +7,7 @@ import ( "slices" "syscall" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" ) // reservedCIDRs is a list of ip addresses for private networks. diff --git a/pkg/services/oracle/network_test.go b/pkg/services/oracle/network_test.go index 656b990..9fbe75e 100644 --- a/pkg/services/oracle/network_test.go +++ b/pkg/services/oracle/network_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/oracle/nodes.go b/pkg/services/oracle/nodes.go index f1e4d8a..5533fa1 100644 --- a/pkg/services/oracle/nodes.go +++ b/pkg/services/oracle/nodes.go @@ -3,10 +3,10 @@ package oracle import ( "slices" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) diff --git a/pkg/services/oracle/oracle.go b/pkg/services/oracle/oracle.go index c6d8c4b..31ac307 100644 --- a/pkg/services/oracle/oracle.go +++ b/pkg/services/oracle/oracle.go @@ -8,17 +8,17 @@ import ( "sync" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/services/oracle/broadcaster" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/oracle/broadcaster" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) diff --git a/pkg/services/oracle/oracle_test.go b/pkg/services/oracle/oracle_test.go index 5ad64bc..b28e5ae 100644 --- a/pkg/services/oracle/oracle_test.go +++ b/pkg/services/oracle/oracle_test.go @@ -14,25 +14,25 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/contracts" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/interop/native/roles" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/services/oracle" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/contracts" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/roles" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" diff --git a/pkg/services/oracle/request.go b/pkg/services/oracle/request.go index 597d155..18875ba 100644 --- a/pkg/services/oracle/request.go +++ b/pkg/services/oracle/request.go @@ -10,11 +10,11 @@ import ( "slices" "time" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" "go.uber.org/zap" ) diff --git a/pkg/services/oracle/response.go b/pkg/services/oracle/response.go index 5a25ba8..9f26224 100644 --- a/pkg/services/oracle/response.go +++ b/pkg/services/oracle/response.go @@ -6,15 +6,15 @@ import ( gio "io" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" "go.uber.org/zap" ) diff --git a/pkg/services/oracle/transaction.go b/pkg/services/oracle/transaction.go index 2bf56b9..03c20ba 100644 --- a/pkg/services/oracle/transaction.go +++ b/pkg/services/oracle/transaction.go @@ -4,14 +4,14 @@ import ( "sync" "time" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" ) type ( diff --git a/pkg/services/rpcsrv/client_test.go b/pkg/services/rpcsrv/client_test.go index 7c0f7ee..e9ebdee 100644 --- a/pkg/services/rpcsrv/client_test.go +++ b/pkg/services/rpcsrv/client_test.go @@ -18,54 +18,54 @@ import ( "github.com/google/uuid" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/network" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/management" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/nns" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/notary" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/oracle" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/policy" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/rolemgmt" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/gas" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/management" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep24" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/neptoken" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nns" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/notary" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/policy" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/rolemgmt" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/waiter" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/rpcsrv/error.go b/pkg/services/rpcsrv/error.go index d2f6c33..39a917e 100644 --- a/pkg/services/rpcsrv/error.go +++ b/pkg/services/rpcsrv/error.go @@ -1,7 +1,7 @@ package rpcsrv import ( - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" ) // abstractResult is an interface which represents either single JSON-RPC 2.0 response diff --git a/pkg/services/rpcsrv/local_test.go b/pkg/services/rpcsrv/local_test.go index 45e14d7..914ea15 100644 --- a/pkg/services/rpcsrv/local_test.go +++ b/pkg/services/rpcsrv/local_test.go @@ -5,14 +5,14 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/gas" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/invoker" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/rpcsrv/notification_comparator.go b/pkg/services/rpcsrv/notification_comparator.go index 7229b6c..f167e53 100644 --- a/pkg/services/rpcsrv/notification_comparator.go +++ b/pkg/services/rpcsrv/notification_comparator.go @@ -1,11 +1,11 @@ package rpcsrv import ( - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/rpcevent" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/rpcevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) // notificationEventComparator is a comparator for notification events. diff --git a/pkg/services/rpcsrv/params/param.go b/pkg/services/rpcsrv/params/param.go index 5c5e3ec..196807e 100644 --- a/pkg/services/rpcsrv/params/param.go +++ b/pkg/services/rpcsrv/params/param.go @@ -12,12 +12,12 @@ import ( "strings" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) type ( diff --git a/pkg/services/rpcsrv/params/param_test.go b/pkg/services/rpcsrv/params/param_test.go index 5c65414..5a42a69 100644 --- a/pkg/services/rpcsrv/params/param_test.go +++ b/pkg/services/rpcsrv/params/param_test.go @@ -10,11 +10,11 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/rpcsrv/params/params_test.go b/pkg/services/rpcsrv/params/params_test.go index 791cd8f..40c354b 100644 --- a/pkg/services/rpcsrv/params/params_test.go +++ b/pkg/services/rpcsrv/params/params_test.go @@ -3,7 +3,7 @@ package params import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/rpcsrv/params/txBuilder.go b/pkg/services/rpcsrv/params/txBuilder.go index ba3fdda..a51fc85 100644 --- a/pkg/services/rpcsrv/params/txBuilder.go +++ b/pkg/services/rpcsrv/params/txBuilder.go @@ -4,13 +4,13 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // ExpandFuncParameterIntoScript pushes provided FuncParam parameter diff --git a/pkg/services/rpcsrv/params/tx_builder_test.go b/pkg/services/rpcsrv/params/tx_builder_test.go index b6a972e..c6a549a 100644 --- a/pkg/services/rpcsrv/params/tx_builder_test.go +++ b/pkg/services/rpcsrv/params/tx_builder_test.go @@ -6,9 +6,9 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/rpcsrv/params/types.go b/pkg/services/rpcsrv/params/types.go index e924397..a05022f 100644 --- a/pkg/services/rpcsrv/params/types.go +++ b/pkg/services/rpcsrv/params/types.go @@ -7,7 +7,7 @@ import ( "fmt" "io" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" ) const ( diff --git a/pkg/services/rpcsrv/server.go b/pkg/services/rpcsrv/server.go index 978fff6..f95a5f4 100644 --- a/pkg/services/rpcsrv/server.go +++ b/pkg/services/rpcsrv/server.go @@ -21,41 +21,41 @@ import ( "github.com/google/uuid" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/limits" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/iterator" - "github.com/tutus-one/tutus-chain/pkg/core/mempool" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/rpcevent" - "github.com/tutus-one/tutus-chain/pkg/network" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/services/oracle/broadcaster" - "github.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest/standard" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/limits" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/iterator" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempool" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/rpcevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/oracle/broadcaster" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest/standard" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "go.uber.org/zap" ) diff --git a/pkg/services/rpcsrv/server_helper_test.go b/pkg/services/rpcsrv/server_helper_test.go index 6218fa6..77a4c3a 100644 --- a/pkg/services/rpcsrv/server_helper_test.go +++ b/pkg/services/rpcsrv/server_helper_test.go @@ -8,17 +8,17 @@ import ( "os" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network" - "github.com/tutus-one/tutus-chain/pkg/services/oracle" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/oracle" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zaptest" diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go index 1c8f133..3404fdc 100644 --- a/pkg/services/rpcsrv/server_test.go +++ b/pkg/services/rpcsrv/server_test.go @@ -23,38 +23,38 @@ import ( "github.com/google/uuid" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dboper" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/network" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - rpc2 "github.com/tutus-one/tutus-chain/pkg/services/oracle/broadcaster" - "github.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/invocations" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativehashes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dboper" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + rpc2 "git.marketally.com/tutus-one/tutus-chain/pkg/services/oracle/broadcaster" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/rpcsrv/params" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/invocations" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" diff --git a/pkg/services/rpcsrv/subscription.go b/pkg/services/rpcsrv/subscription.go index 24ae2b9..97ed237 100644 --- a/pkg/services/rpcsrv/subscription.go +++ b/pkg/services/rpcsrv/subscription.go @@ -4,7 +4,7 @@ import ( "sync/atomic" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" ) type ( diff --git a/pkg/services/rpcsrv/subscription_test.go b/pkg/services/rpcsrv/subscription_test.go index 14c16c1..e827dee 100644 --- a/pkg/services/rpcsrv/subscription_test.go +++ b/pkg/services/rpcsrv/subscription_test.go @@ -11,14 +11,14 @@ import ( "time" "github.com/gorilla/websocket" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/services/rpcsrv/tokens.go b/pkg/services/rpcsrv/tokens.go index c581a9f..588adc3 100644 --- a/pkg/services/rpcsrv/tokens.go +++ b/pkg/services/rpcsrv/tokens.go @@ -1,7 +1,7 @@ package rpcsrv import ( - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" ) // tokenTransfers is a generic type used to represent NEP-11 and NEP-17 transfers. diff --git a/pkg/services/statefetcher/statefetcher.go b/pkg/services/statefetcher/statefetcher.go index 5632fa9..8346f8f 100644 --- a/pkg/services/statefetcher/statefetcher.go +++ b/pkg/services/statefetcher/statefetcher.go @@ -11,11 +11,11 @@ import ( "sync" "sync/atomic" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - gio "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + gio "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/helpers/neofs" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/nspcc-dev/neofs-sdk-go/client" "github.com/nspcc-dev/neofs-sdk-go/container" "github.com/nspcc-dev/neofs-sdk-go/object" diff --git a/pkg/services/statefetcher/statefetcher_test.go b/pkg/services/statefetcher/statefetcher_test.go index 4d70312..a8832bb 100644 --- a/pkg/services/statefetcher/statefetcher_test.go +++ b/pkg/services/statefetcher/statefetcher_test.go @@ -3,9 +3,9 @@ package statefetcher import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/pkg/services/stateroot/message.go b/pkg/services/stateroot/message.go index 1106a82..f503955 100644 --- a/pkg/services/stateroot/message.go +++ b/pkg/services/stateroot/message.go @@ -3,8 +3,8 @@ package stateroot import ( "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) type ( diff --git a/pkg/services/stateroot/network.go b/pkg/services/stateroot/network.go index 2a00ae0..6a965ef 100644 --- a/pkg/services/stateroot/network.go +++ b/pkg/services/stateroot/network.go @@ -4,13 +4,13 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) diff --git a/pkg/services/stateroot/service.go b/pkg/services/stateroot/service.go index 93e2d98..878588f 100644 --- a/pkg/services/stateroot/service.go +++ b/pkg/services/stateroot/service.go @@ -7,16 +7,16 @@ import ( "sync" "sync/atomic" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/stateroot" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/stateroot" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) diff --git a/pkg/services/stateroot/service_test.go b/pkg/services/stateroot/service_test.go index 3d5a773..01cb2ff 100644 --- a/pkg/services/stateroot/service_test.go +++ b/pkg/services/stateroot/service_test.go @@ -8,30 +8,30 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/internal/basicchain" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/native/noderoles" - "github.com/tutus-one/tutus-chain/pkg/core/state" - corestate "github.com/tutus-one/tutus-chain/pkg/core/stateroot" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/interop/native/roles" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/tutustest/chain" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/services/stateroot" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/basicchain" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/noderoles" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + corestate "git.marketally.com/tutus-one/tutus-chain/pkg/core/stateroot" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/roles" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest/chain" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/services/stateroot" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) diff --git a/pkg/services/stateroot/signature.go b/pkg/services/stateroot/signature.go index cad8099..15b5011 100644 --- a/pkg/services/stateroot/signature.go +++ b/pkg/services/stateroot/signature.go @@ -3,14 +3,14 @@ package stateroot import ( "sync" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" ) type ( diff --git a/pkg/services/stateroot/validators.go b/pkg/services/stateroot/validators.go index ba8d394..106c6a3 100644 --- a/pkg/services/stateroot/validators.go +++ b/pkg/services/stateroot/validators.go @@ -3,12 +3,12 @@ package stateroot import ( "time" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" ) diff --git a/pkg/services/stateroot/vote.go b/pkg/services/stateroot/vote.go index 2029e57..c6823f1 100644 --- a/pkg/services/stateroot/vote.go +++ b/pkg/services/stateroot/vote.go @@ -1,8 +1,8 @@ package stateroot import ( - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // Vote represents a vote message. diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index ded2684..75b72b2 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -12,10 +12,10 @@ import ( "text/template" "unicode" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const srcTmpl = ` @@ -155,9 +155,9 @@ func NewConfig() Config { // lead to unexpected results. func Generate(cfg Config) error { ctr := TemplateFromManifest(cfg, scTypeToGo) - ctr.Imports = append(ctr.Imports, "github.com/tutus-one/tutus-chain/pkg/interop/contract") + ctr.Imports = append(ctr.Imports, "git.marketally.com/tutus-one/tutus-chain/pkg/interop/contract") if ctr.Hash != "" { - ctr.Imports = append(ctr.Imports, "github.com/tutus-one/tutus-chain/pkg/interop/tutusinternal") + ctr.Imports = append(ctr.Imports, "git.marketally.com/tutus-one/tutus-chain/pkg/interop/tutusinternal") } slices.Sort(ctr.Imports) @@ -206,13 +206,13 @@ func scTypeToGo(name string, typ smartcontract.ParamType, cfg *Config) (string, case smartcontract.StringType: return "string", "" case smartcontract.Hash160Type: - return "interop.Hash160", "github.com/tutus-one/tutus-chain/pkg/interop" + return "interop.Hash160", "git.marketally.com/tutus-one/tutus-chain/pkg/interop" case smartcontract.Hash256Type: - return "interop.Hash256", "github.com/tutus-one/tutus-chain/pkg/interop" + return "interop.Hash256", "git.marketally.com/tutus-one/tutus-chain/pkg/interop" case smartcontract.PublicKeyType: - return "interop.PublicKey", "github.com/tutus-one/tutus-chain/pkg/interop" + return "interop.PublicKey", "git.marketally.com/tutus-one/tutus-chain/pkg/interop" case smartcontract.SignatureType: - return "interop.Signature", "github.com/tutus-one/tutus-chain/pkg/interop" + return "interop.Signature", "git.marketally.com/tutus-one/tutus-chain/pkg/interop" case smartcontract.ArrayType: return "[]any", "" case smartcontract.MapType: diff --git a/pkg/smartcontract/binding/generate_test.go b/pkg/smartcontract/binding/generate_test.go index 62ecac3..21187cc 100644 --- a/pkg/smartcontract/binding/generate_test.go +++ b/pkg/smartcontract/binding/generate_test.go @@ -3,7 +3,7 @@ package binding import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" "github.com/stretchr/testify/assert" ) diff --git a/pkg/smartcontract/binding/override.go b/pkg/smartcontract/binding/override.go index 8892153..3326905 100644 --- a/pkg/smartcontract/binding/override.go +++ b/pkg/smartcontract/binding/override.go @@ -35,9 +35,9 @@ func NewOverrideFromString(s string) Override { switch over.Package { case "iterator", "storage": - over.Package = "github.com/tutus-one/tutus-chain/pkg/interop/" + over.Package + over.Package = "git.marketally.com/tutus-one/tutus-chain/pkg/interop/" + over.Package case "ledger", "management": - over.Package = "github.com/tutus-one/tutus-chain/pkg/interop/native/" + over.Package + over.Package = "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/" + over.Package } slashIndex := strings.LastIndexByte(s, '/') diff --git a/pkg/smartcontract/builder.go b/pkg/smartcontract/builder.go index 63a63f3..d554f52 100644 --- a/pkg/smartcontract/builder.go +++ b/pkg/smartcontract/builder.go @@ -1,11 +1,11 @@ package smartcontract import ( - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // Builder is used to create arbitrary scripts from the set of methods it provides. diff --git a/pkg/smartcontract/builder_test.go b/pkg/smartcontract/builder_test.go index e73a083..61327c8 100644 --- a/pkg/smartcontract/builder_test.go +++ b/pkg/smartcontract/builder_test.go @@ -3,7 +3,7 @@ package smartcontract import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/callflag/call_flags_test.go b/pkg/smartcontract/callflag/call_flags_test.go index 39d512e..b2d2fb5 100644 --- a/pkg/smartcontract/callflag/call_flags_test.go +++ b/pkg/smartcontract/callflag/call_flags_test.go @@ -3,7 +3,7 @@ package callflag import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) diff --git a/pkg/smartcontract/context/context.go b/pkg/smartcontract/context/context.go index dd7d41f..e835895 100644 --- a/pkg/smartcontract/context/context.go +++ b/pkg/smartcontract/context/context.go @@ -9,16 +9,16 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) // TransactionType is the ParameterContext Type used for transactions. diff --git a/pkg/smartcontract/context/context_test.go b/pkg/smartcontract/context/context_test.go index 884764c..67bf2ac 100644 --- a/pkg/smartcontract/context/context_test.go +++ b/pkg/smartcontract/context/context_test.go @@ -4,17 +4,17 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/interop/crypto" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/context/item.go b/pkg/smartcontract/context/item.go index 9e9ebf8..70476d6 100644 --- a/pkg/smartcontract/context/item.go +++ b/pkg/smartcontract/context/item.go @@ -1,8 +1,8 @@ package context import ( - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" ) // Item represents a transaction context item. diff --git a/pkg/smartcontract/context/item_test.go b/pkg/smartcontract/context/item_test.go index 5b8e125..66c5b7f 100644 --- a/pkg/smartcontract/context/item_test.go +++ b/pkg/smartcontract/context/item_test.go @@ -3,10 +3,10 @@ package context import ( "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/contract.go b/pkg/smartcontract/contract.go index 0000876..3e88e38 100644 --- a/pkg/smartcontract/contract.go +++ b/pkg/smartcontract/contract.go @@ -4,10 +4,10 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" ) // CreateMultiSigRedeemScript creates an "m out of n" type verification script diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go index 4708478..303f3e7 100644 --- a/pkg/smartcontract/contract_test.go +++ b/pkg/smartcontract/contract_test.go @@ -3,10 +3,10 @@ package smartcontract import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/doc_test.go b/pkg/smartcontract/doc_test.go index 9bb32c0..d4f6631 100644 --- a/pkg/smartcontract/doc_test.go +++ b/pkg/smartcontract/doc_test.go @@ -4,13 +4,13 @@ import ( "context" "encoding/hex" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/actor" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/gas" - "github.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/actor" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/gas" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/tutus" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" ) func ExampleBuilder() { diff --git a/pkg/smartcontract/entry.go b/pkg/smartcontract/entry.go index fc06df1..b012f21 100644 --- a/pkg/smartcontract/entry.go +++ b/pkg/smartcontract/entry.go @@ -3,12 +3,12 @@ package smartcontract import ( "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // CreateCallAndUnwrapIteratorScript creates a script that calls 'operation' method diff --git a/pkg/smartcontract/manifest/abi.go b/pkg/smartcontract/manifest/abi.go index 08cee65..12e2df1 100644 --- a/pkg/smartcontract/manifest/abi.go +++ b/pkg/smartcontract/manifest/abi.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) const ( diff --git a/pkg/smartcontract/manifest/abi_test.go b/pkg/smartcontract/manifest/abi_test.go index 2c69f91..a81dd6b 100644 --- a/pkg/smartcontract/manifest/abi_test.go +++ b/pkg/smartcontract/manifest/abi_test.go @@ -3,7 +3,7 @@ package manifest import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/container_test.go b/pkg/smartcontract/manifest/container_test.go index 33cd551..7195af8 100644 --- a/pkg/smartcontract/manifest/container_test.go +++ b/pkg/smartcontract/manifest/container_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/event.go b/pkg/smartcontract/manifest/event.go index 0d2626d..d79ae63 100644 --- a/pkg/smartcontract/manifest/event.go +++ b/pkg/smartcontract/manifest/event.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Event is a description of a single event. diff --git a/pkg/smartcontract/manifest/event_test.go b/pkg/smartcontract/manifest/event_test.go index 351fe93..dff22ce 100644 --- a/pkg/smartcontract/manifest/event_test.go +++ b/pkg/smartcontract/manifest/event_test.go @@ -4,8 +4,8 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/group.go b/pkg/smartcontract/manifest/group.go index 46dc0fb..0877ebc 100644 --- a/pkg/smartcontract/manifest/group.go +++ b/pkg/smartcontract/manifest/group.go @@ -7,10 +7,10 @@ import ( "errors" "slices" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Group represents a group of smartcontracts identified by a public key. diff --git a/pkg/smartcontract/manifest/group_test.go b/pkg/smartcontract/manifest/group_test.go index 94f8f11..7e4b34f 100644 --- a/pkg/smartcontract/manifest/group_test.go +++ b/pkg/smartcontract/manifest/group_test.go @@ -3,9 +3,9 @@ package manifest import ( "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index 54a9489..99dd2ee 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -10,9 +10,9 @@ import ( "slices" "strings" - ojson "github.com/tutus-one/tutus-ordered-json" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + ojson "git.marketally.com/tutus-one/tutus-ordered-json" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) const ( diff --git a/pkg/smartcontract/manifest/manifest_test.go b/pkg/smartcontract/manifest/manifest_test.go index 1132e35..4a393a7 100644 --- a/pkg/smartcontract/manifest/manifest_test.go +++ b/pkg/smartcontract/manifest/manifest_test.go @@ -7,11 +7,11 @@ import ( "slices" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/method.go b/pkg/smartcontract/manifest/method.go index d1ca7f2..6929408 100644 --- a/pkg/smartcontract/manifest/method.go +++ b/pkg/smartcontract/manifest/method.go @@ -3,8 +3,8 @@ package manifest import ( "errors" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Method represents method's metadata. diff --git a/pkg/smartcontract/manifest/method_test.go b/pkg/smartcontract/manifest/method_test.go index 0c6b92b..747e389 100644 --- a/pkg/smartcontract/manifest/method_test.go +++ b/pkg/smartcontract/manifest/method_test.go @@ -4,9 +4,9 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/parameter.go b/pkg/smartcontract/manifest/parameter.go index b9f5485..095d07f 100644 --- a/pkg/smartcontract/manifest/parameter.go +++ b/pkg/smartcontract/manifest/parameter.go @@ -6,8 +6,8 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Parameter represents smartcontract's parameter's definition. diff --git a/pkg/smartcontract/manifest/parameter_test.go b/pkg/smartcontract/manifest/parameter_test.go index 51570a2..c5f14e9 100644 --- a/pkg/smartcontract/manifest/parameter_test.go +++ b/pkg/smartcontract/manifest/parameter_test.go @@ -4,8 +4,8 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/permission.go b/pkg/smartcontract/manifest/permission.go index aedce72..559a6fa 100644 --- a/pkg/smartcontract/manifest/permission.go +++ b/pkg/smartcontract/manifest/permission.go @@ -8,9 +8,9 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // PermissionType represents permission type. diff --git a/pkg/smartcontract/manifest/permission_test.go b/pkg/smartcontract/manifest/permission_test.go index 0f41e74..6d84fc6 100644 --- a/pkg/smartcontract/manifest/permission_test.go +++ b/pkg/smartcontract/manifest/permission_test.go @@ -6,10 +6,10 @@ import ( "reflect" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/standard/check.go b/pkg/smartcontract/manifest/standard/check.go index 9c110b8..2bfeb4f 100644 --- a/pkg/smartcontract/manifest/standard/check.go +++ b/pkg/smartcontract/manifest/standard/check.go @@ -1,6 +1,6 @@ package standard -import "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" +import "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" // Standard represents smart-contract standard. type Standard struct { diff --git a/pkg/smartcontract/manifest/standard/comply.go b/pkg/smartcontract/manifest/standard/comply.go index b99a550..1a59c97 100644 --- a/pkg/smartcontract/manifest/standard/comply.go +++ b/pkg/smartcontract/manifest/standard/comply.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Various validation errors. diff --git a/pkg/smartcontract/manifest/standard/comply_test.go b/pkg/smartcontract/manifest/standard/comply_test.go index 33f4d48..ac9326b 100644 --- a/pkg/smartcontract/manifest/standard/comply_test.go +++ b/pkg/smartcontract/manifest/standard/comply_test.go @@ -3,8 +3,8 @@ package standard import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/manifest/standard/nep11.go b/pkg/smartcontract/manifest/standard/nep11.go index b71f2b8..8139632 100644 --- a/pkg/smartcontract/manifest/standard/nep11.go +++ b/pkg/smartcontract/manifest/standard/nep11.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep11Base is a Standard containing common NEP-11 methods. diff --git a/pkg/smartcontract/manifest/standard/nep17.go b/pkg/smartcontract/manifest/standard/nep17.go index a02f357..c2c6df5 100644 --- a/pkg/smartcontract/manifest/standard/nep17.go +++ b/pkg/smartcontract/manifest/standard/nep17.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep17 is a NEP-17 Standard. diff --git a/pkg/smartcontract/manifest/standard/nep22.go b/pkg/smartcontract/manifest/standard/nep22.go index 9ae569a..9bd5560 100644 --- a/pkg/smartcontract/manifest/standard/nep22.go +++ b/pkg/smartcontract/manifest/standard/nep22.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep22 is a NEP-22 Standard describing smart contract update functionality. diff --git a/pkg/smartcontract/manifest/standard/nep24.go b/pkg/smartcontract/manifest/standard/nep24.go index 2e407a7..ebe3396 100644 --- a/pkg/smartcontract/manifest/standard/nep24.go +++ b/pkg/smartcontract/manifest/standard/nep24.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // MethodRoyaltyInfo is the name of the method that returns royalty information. diff --git a/pkg/smartcontract/manifest/standard/nep26.go b/pkg/smartcontract/manifest/standard/nep26.go index d4442f3..260e29a 100644 --- a/pkg/smartcontract/manifest/standard/nep26.go +++ b/pkg/smartcontract/manifest/standard/nep26.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep26 is a NEP-26 Standard. diff --git a/pkg/smartcontract/manifest/standard/nep27.go b/pkg/smartcontract/manifest/standard/nep27.go index 748916c..b2e1bb0 100644 --- a/pkg/smartcontract/manifest/standard/nep27.go +++ b/pkg/smartcontract/manifest/standard/nep27.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep27 is a NEP-27 Standard. diff --git a/pkg/smartcontract/manifest/standard/nep29.go b/pkg/smartcontract/manifest/standard/nep29.go index 12500fa..1b76333 100644 --- a/pkg/smartcontract/manifest/standard/nep29.go +++ b/pkg/smartcontract/manifest/standard/nep29.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep29 is a NEP-29 Standard describing smart contract _deploy method functionality. diff --git a/pkg/smartcontract/manifest/standard/nep30.go b/pkg/smartcontract/manifest/standard/nep30.go index be8a1fd..51cd68c 100644 --- a/pkg/smartcontract/manifest/standard/nep30.go +++ b/pkg/smartcontract/manifest/standard/nep30.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep30 is a NEP-30 Standard describing smart contract verify method functionality. diff --git a/pkg/smartcontract/manifest/standard/nep31.go b/pkg/smartcontract/manifest/standard/nep31.go index bb67c20..061552c 100644 --- a/pkg/smartcontract/manifest/standard/nep31.go +++ b/pkg/smartcontract/manifest/standard/nep31.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // Nep31 is a NEP-31 Standard describing smart contract destroy functionality. diff --git a/pkg/smartcontract/manifest/standard/token.go b/pkg/smartcontract/manifest/standard/token.go index ba2d7b6..48bc1d2 100644 --- a/pkg/smartcontract/manifest/standard/token.go +++ b/pkg/smartcontract/manifest/standard/token.go @@ -1,8 +1,8 @@ package standard import ( - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" ) // DecimalTokenBase contains methods common to NEP-11 and NEP-17 token standards. diff --git a/pkg/smartcontract/nef/method_token.go b/pkg/smartcontract/nef/method_token.go index 4e8d901..843aa0e 100644 --- a/pkg/smartcontract/nef/method_token.go +++ b/pkg/smartcontract/nef/method_token.go @@ -4,9 +4,9 @@ import ( "errors" "strings" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // maxMethodLength is the maximum length of method. diff --git a/pkg/smartcontract/nef/method_token_test.go b/pkg/smartcontract/nef/method_token_test.go index 6662cb1..c5275b7 100644 --- a/pkg/smartcontract/nef/method_token_test.go +++ b/pkg/smartcontract/nef/method_token_test.go @@ -4,9 +4,9 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/nef/nef.go b/pkg/smartcontract/nef/nef.go index c863dc4..26aba9e 100644 --- a/pkg/smartcontract/nef/nef.go +++ b/pkg/smartcontract/nef/nef.go @@ -6,10 +6,10 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // NEO Executable Format 3 (NEF3) diff --git a/pkg/smartcontract/nef/nef_test.go b/pkg/smartcontract/nef/nef_test.go index 6482213..3709915 100644 --- a/pkg/smartcontract/nef/nef_test.go +++ b/pkg/smartcontract/nef/nef_test.go @@ -6,12 +6,12 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/param_type.go b/pkg/smartcontract/param_type.go index afb4721..1447e67 100644 --- a/pkg/smartcontract/param_type.go +++ b/pkg/smartcontract/param_type.go @@ -9,13 +9,13 @@ import ( "strings" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "gopkg.in/yaml.v3" ) diff --git a/pkg/smartcontract/param_type_test.go b/pkg/smartcontract/param_type_test.go index b74495f..409385b 100644 --- a/pkg/smartcontract/param_type_test.go +++ b/pkg/smartcontract/param_type_test.go @@ -5,9 +5,9 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index dc8aa8d..8d8b0af 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -14,9 +14,9 @@ import ( "strings" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Parameter represents a smart contract parameter. diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index eb9bca0..6de8d9f 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -10,12 +10,12 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 3844850..deeadd8 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -8,11 +8,11 @@ import ( "text/template" "unicode" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest/standard" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest/standard" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // The set of constants containing parts of RPC binding template. Each block of code @@ -486,7 +486,7 @@ func Generate(cfg binding.Config) error { for _, std := range cfg.Manifest.SupportedStandards { switch std { case manifest.NEP11StandardName: - imports["github.com/tutus-one/tutus-chain/pkg/rpcclient/nep11"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep11"] = struct{}{} if standard.ComplyABI(cfg.Manifest, standard.Nep11Divisible) == nil { ctr.IsNep11D = true } else if standard.ComplyABI(cfg.Manifest, standard.Nep11NonDivisible) == nil { @@ -494,18 +494,18 @@ func Generate(cfg binding.Config) error { } case manifest.NEP17StandardName: if standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil { - imports["github.com/tutus-one/tutus-chain/pkg/rpcclient/nep17"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep17"] = struct{}{} ctr.IsNep17 = true } case manifest.NEP22StandardName: if standard.ComplyABI(cfg.Manifest, standard.Nep22) == nil { - imports["github.com/tutus-one/tutus-chain/pkg/rpcclient/nep22"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep22"] = struct{}{} ctr.ContractWriterStandards = append(ctr.ContractWriterStandards, std) isNep22 = true } case manifest.NEP24StandardName: if standard.ComplyABI(cfg.Manifest, standard.Nep24) == nil { - imports["github.com/tutus-one/tutus-chain/pkg/rpcclient/nep24"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep24"] = struct{}{} ctr.IsNep24 = true } case manifest.NEP24Payable: @@ -518,7 +518,7 @@ func Generate(cfg binding.Config) error { } case manifest.NEP31StandardName: if standard.ComplyABI(cfg.Manifest, standard.Nep31) == nil { - imports["github.com/tutus-one/tutus-chain/pkg/rpcclient/nep31"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/nep31"] = struct{}{} ctr.ContractWriterStandards = append(ctr.ContractWriterStandards, std) isNep31 = true } @@ -703,19 +703,19 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended case smartcontract.StringType: return "string", "" case smartcontract.Hash160Type: - return "util.Uint160", "github.com/tutus-one/tutus-chain/pkg/util" + return "util.Uint160", "git.marketally.com/tutus-one/tutus-chain/pkg/util" case smartcontract.Hash256Type: - return "util.Uint256", "github.com/tutus-one/tutus-chain/pkg/util" + return "util.Uint256", "git.marketally.com/tutus-one/tutus-chain/pkg/util" case smartcontract.PublicKeyType: - return "*keys.PublicKey", "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + return "*keys.PublicKey", "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" case smartcontract.SignatureType: return "[]byte", "" case smartcontract.ArrayType: if len(et.Name) > 0 { - return "*" + toTypeName(et.Name), "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + return "*" + toTypeName(et.Name), "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" } else if et.Value != nil { if et.Value.Base == smartcontract.PublicKeyType { // Special array wrapper. - return "keys.PublicKeys", "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + return "keys.PublicKeys", "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" } sub, pkg := extendedTypeToGo(*et.Value, named) return "[]" + sub, pkg @@ -730,7 +730,7 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended } else { vt = "any" } - return "map[" + kt + "]" + vt, "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + return "map[" + kt + "]" + vt, "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" case smartcontract.InteropInterfaceType: return "any", "" case smartcontract.VoidType: @@ -1051,7 +1051,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st } else { ctr.Methods[i].Comment = fmt.Sprintf("creates a transaction invoking `%s` method of the contract.", ctr.Methods[i].NameABI) if ctr.Methods[i].ReturnType == "bool" { - imports["github.com/tutus-one/tutus-chain/pkg/smartcontract"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract"] = struct{}{} } } } @@ -1060,7 +1060,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st } if len(cfg.NamedTypes) > 0 { imports["errors"] = struct{}{} - imports["github.com/tutus-one/tutus-chain/pkg/smartcontract"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract"] = struct{}{} } for _, abiEvent := range cfg.Manifest.ABI.Events { eBindingName := ToEventBindingName(abiEvent.Name) @@ -1098,8 +1098,8 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st } if len(ctr.CustomEvents) > 0 { - imports["github.com/tutus-one/tutus-chain/pkg/tutusrpc/result"] = struct{}{} - imports["github.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} imports["fmt"] = struct{}{} imports["errors"] = struct{}{} } @@ -1110,13 +1110,13 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st abim := cfg.Manifest.ABI.GetMethod(ctr.SafeMethods[i].NameABI, len(ctr.SafeMethods[i].Arguments)) if abim.ReturnType == smartcontract.InteropInterfaceType { imports["github.com/google/uuid"] = struct{}{} - imports["github.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} - imports["github.com/tutus-one/tutus-chain/pkg/tutusrpc/result"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result"] = struct{}{} ctr.SafeMethods[i].ReturnType = "uuid.UUID, result.Iterator" ctr.SafeMethods[i].Unwrapper = "SessionIterator" ctr.HasIterator = true } else { - imports["github.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} ctr.SafeMethods[i].ReturnType = "any" ctr.SafeMethods[i].Unwrapper = "Item" } @@ -1135,7 +1135,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st case "[]byte": ctr.SafeMethods[i].Unwrapper = "Bytes" case "[]any": - imports["github.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"] = struct{}{} ctr.SafeMethods[i].ReturnType = "[]stackitem.Item" ctr.SafeMethods[i].Unwrapper = "Array" case "*stackitem.Map": @@ -1160,15 +1160,15 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st } } - imports["github.com/tutus-one/tutus-chain/pkg/util"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/util"] = struct{}{} if len(ctr.SafeMethods) > 0 { - imports["github.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient/unwrap"] = struct{}{} if !ctr.IsNep17 && !ctr.IsNep11D && !ctr.IsNep11ND && !ctr.IsNep24 { - imports["github.com/tutus-one/tutus-chain/pkg/tutusrpc/result"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result"] = struct{}{} } } if len(ctr.Methods) > 0 { - imports["github.com/tutus-one/tutus-chain/pkg/core/transaction"] = struct{}{} + imports["git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction"] = struct{}{} } if len(ctr.Methods) > 0 || ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND || len(ctr.ContractWriterStandards) > 0 { ctr.HasWriter = true diff --git a/pkg/smartcontract/scparser/context.go b/pkg/smartcontract/scparser/context.go index ae0b59a..737de0c 100644 --- a/pkg/smartcontract/scparser/context.go +++ b/pkg/smartcontract/scparser/context.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) var errNoInstParam = errors.New("failed to read instruction parameter") diff --git a/pkg/smartcontract/scparser/contract_checks.go b/pkg/smartcontract/scparser/contract_checks.go index 494986a..e4597e9 100644 --- a/pkg/smartcontract/scparser/contract_checks.go +++ b/pkg/smartcontract/scparser/contract_checks.go @@ -5,11 +5,11 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/util/bitfield" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/util/bitfield" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // MaxMultisigKeys is the maximum number of keys allowed for correct multisig contract. diff --git a/pkg/smartcontract/scparser/contract_checks_test.go b/pkg/smartcontract/scparser/contract_checks_test.go index de14e31..c89cd28 100644 --- a/pkg/smartcontract/scparser/contract_checks_test.go +++ b/pkg/smartcontract/scparser/contract_checks_test.go @@ -6,13 +6,13 @@ import ( "slices" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util/bitfield" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util/bitfield" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/scparser/fuzz_test.go b/pkg/smartcontract/scparser/fuzz_test.go index 9e49e35..acd44e1 100644 --- a/pkg/smartcontract/scparser/fuzz_test.go +++ b/pkg/smartcontract/scparser/fuzz_test.go @@ -4,9 +4,9 @@ import ( "crypto/rand" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/smartcontract/zkpbinding/binding.go b/pkg/smartcontract/zkpbinding/binding.go index 621204a..0ae588c 100644 --- a/pkg/smartcontract/zkpbinding/binding.go +++ b/pkg/smartcontract/zkpbinding/binding.go @@ -6,7 +6,7 @@ // // Please, check out the example of zkpbinding package usage to generate and // verify proofs on the Neo chain: -// https://github.com/tutus-one/tutus-chain/blob/91c928e8d35164055e5b2e8efbc898440cc2b486/examples/zkp/cubic_circuit/README.md +// https://git.marketally.com/tutus-one/tutus-chain/blob/91c928e8d35164055e5b2e8efbc898440cc2b486/examples/zkp/cubic_circuit/README.md package zkpbinding import ( @@ -22,7 +22,7 @@ import ( "github.com/consensys/gnark/backend/groth16" curve "github.com/consensys/gnark/backend/groth16/bls12-381" "github.com/consensys/gnark/backend/witness" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/binding" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/binding" ) // Config represents a configuration for Verifier Go smart contract generator. @@ -71,16 +71,16 @@ const ( // to verify other circuits. // // Use NeoGo smart contract compiler to compile this contract: -// https://github.com/tutus-one/tutus-chain/blob/master/docs/compiler.md#compiling. +// https://git.marketally.com/tutus-one/tutus-chain/blob/master/docs/compiler.md#compiling. // You will need to create contract YAML configuration file and proper go.mod and // go.sum files required for compilation. Please, refer to the NeoGo ZKP example // to see how to verify proofs via the Verifier contract: -// https://github.com/tutus-one/tutus-chain/tree/master/examples/zkp/cubic_circuit. +// https://git.marketally.com/tutus-one/tutus-chain/tree/master/examples/zkp/cubic_circuit. package main import ( - "github.com/tutus-one/tutus-chain/pkg/interop/native/crypto" - "github.com/tutus-one/tutus-chain/pkg/interop/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/native/crypto" + "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util" ) // A set of circuit-specific variables required for verification. Should be generated @@ -158,7 +158,7 @@ func VerifyProof(a []byte, b []byte, c []byte, publicInput [][]byte) bool { // verifyCfg is a contract configuration file required to compile smart // contract. verifyCfg = `name: "Groth-16 Verifier contract" -sourceurl: https://github.com/tutus-one/tutus-chain/ +sourceurl: https://git.marketally.com/tutus-one/tutus-chain/ supportedstandards: []` // verifyGomod is a standard go.mod file containing module name, go version @@ -167,12 +167,12 @@ supportedstandards: []` go 1.24 -require github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20241223145456-80e18222bca2 +require git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20241223145456-80e18222bca2 ` // verifyGosum is a standard go.sum file needed for contract compilation. - verifyGosum = `github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0= + verifyGosum = `git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0= ` ) diff --git a/pkg/storage/config.go b/pkg/storage/config.go index 301b52b..c8035b4 100644 --- a/pkg/storage/config.go +++ b/pkg/storage/config.go @@ -1,106 +1,106 @@ -package storage - -import "time" - -// Config defines the storage configuration for a Tutus node. -type Config struct { - // Provider specifies the active storage backend - Provider string `yaml:"provider"` - - // NeoFS configuration (when Provider = "neofs") - NeoFS NeoFSConfig `yaml:"neofs"` - - // Local configuration (when Provider = "local") - Local LocalConfig `yaml:"local"` - - // S3 configuration (when Provider = "s3") - S3 S3Config `yaml:"s3"` -} - -// NeoFSConfig configures the NeoFS storage adapter. -type NeoFSConfig struct { - // Endpoints is a list of NeoFS node addresses - Endpoints []string `yaml:"endpoints"` - - // Container is the NeoFS container ID for block storage - Container string `yaml:"container"` - - // Timeout for NeoFS operations - Timeout time.Duration `yaml:"timeout"` - - // Key is the path to the wallet key file for authentication - Key string `yaml:"key"` - - // DialTimeout for initial connection - DialTimeout time.Duration `yaml:"dial_timeout"` - - // StreamTimeout for streaming operations - StreamTimeout time.Duration `yaml:"stream_timeout"` - - // HealthcheckTimeout for node health verification - HealthcheckTimeout time.Duration `yaml:"healthcheck_timeout"` - - // RebalanceInterval for endpoint rotation - RebalanceInterval time.Duration `yaml:"rebalance_interval"` - - // SessionExpirationDuration for session tokens - SessionExpirationDuration uint64 `yaml:"session_expiration_duration"` -} - -// LocalConfig configures local filesystem storage. -type LocalConfig struct { - // Path is the base directory for storage - Path string `yaml:"path"` - - // MaxSize is the maximum storage size in bytes (0 = unlimited) - MaxSize int64 `yaml:"max_size"` - - // Compression enables data compression - Compression bool `yaml:"compression"` -} - -// S3Config configures S3-compatible storage. -type S3Config struct { - // Endpoint is the S3 API endpoint (empty for AWS) - Endpoint string `yaml:"endpoint"` - - // Region is the AWS region or equivalent - Region string `yaml:"region"` - - // Bucket is the S3 bucket name - Bucket string `yaml:"bucket"` - - // AccessKey for authentication - AccessKey string `yaml:"access_key"` - - // SecretKey for authentication - SecretKey string `yaml:"secret_key"` - - // UsePathStyle forces path-style URLs (required for some providers) - UsePathStyle bool `yaml:"use_path_style"` - - // DisableSSL for non-TLS endpoints (not recommended for production) - DisableSSL bool `yaml:"disable_ssl"` -} - -// DefaultConfig returns a configuration with sensible defaults. -func DefaultConfig() Config { - return Config{ - Provider: "local", - NeoFS: NeoFSConfig{ - Timeout: 30 * time.Second, - DialTimeout: 5 * time.Second, - StreamTimeout: 60 * time.Second, - HealthcheckTimeout: 10 * time.Second, - RebalanceInterval: 60 * time.Second, - SessionExpirationDuration: 28800, // 8 hours in blocks - }, - Local: LocalConfig{ - Path: "./storage", - Compression: true, - }, - S3: S3Config{ - Region: "us-east-1", - }, - } -} +package storage + +import "time" + +// Config defines the storage configuration for a Tutus node. +type Config struct { + // Provider specifies the active storage backend + Provider string `yaml:"provider"` + + // NeoFS configuration (when Provider = "neofs") + NeoFS NeoFSConfig `yaml:"neofs"` + + // Local configuration (when Provider = "local") + Local LocalConfig `yaml:"local"` + + // S3 configuration (when Provider = "s3") + S3 S3Config `yaml:"s3"` +} + +// NeoFSConfig configures the NeoFS storage adapter. +type NeoFSConfig struct { + // Endpoints is a list of NeoFS node addresses + Endpoints []string `yaml:"endpoints"` + + // Container is the NeoFS container ID for block storage + Container string `yaml:"container"` + + // Timeout for NeoFS operations + Timeout time.Duration `yaml:"timeout"` + + // Key is the path to the wallet key file for authentication + Key string `yaml:"key"` + + // DialTimeout for initial connection + DialTimeout time.Duration `yaml:"dial_timeout"` + + // StreamTimeout for streaming operations + StreamTimeout time.Duration `yaml:"stream_timeout"` + + // HealthcheckTimeout for node health verification + HealthcheckTimeout time.Duration `yaml:"healthcheck_timeout"` + + // RebalanceInterval for endpoint rotation + RebalanceInterval time.Duration `yaml:"rebalance_interval"` + + // SessionExpirationDuration for session tokens + SessionExpirationDuration uint64 `yaml:"session_expiration_duration"` +} + +// LocalConfig configures local filesystem storage. +type LocalConfig struct { + // Path is the base directory for storage + Path string `yaml:"path"` + + // MaxSize is the maximum storage size in bytes (0 = unlimited) + MaxSize int64 `yaml:"max_size"` + + // Compression enables data compression + Compression bool `yaml:"compression"` +} + +// S3Config configures S3-compatible storage. +type S3Config struct { + // Endpoint is the S3 API endpoint (empty for AWS) + Endpoint string `yaml:"endpoint"` + + // Region is the AWS region or equivalent + Region string `yaml:"region"` + + // Bucket is the S3 bucket name + Bucket string `yaml:"bucket"` + + // AccessKey for authentication + AccessKey string `yaml:"access_key"` + + // SecretKey for authentication + SecretKey string `yaml:"secret_key"` + + // UsePathStyle forces path-style URLs (required for some providers) + UsePathStyle bool `yaml:"use_path_style"` + + // DisableSSL for non-TLS endpoints (not recommended for production) + DisableSSL bool `yaml:"disable_ssl"` +} + +// DefaultConfig returns a configuration with sensible defaults. +func DefaultConfig() Config { + return Config{ + Provider: "local", + NeoFS: NeoFSConfig{ + Timeout: 30 * time.Second, + DialTimeout: 5 * time.Second, + StreamTimeout: 60 * time.Second, + HealthcheckTimeout: 10 * time.Second, + RebalanceInterval: 60 * time.Second, + SessionExpirationDuration: 28800, // 8 hours in blocks + }, + Local: LocalConfig{ + Path: "./storage", + Compression: true, + }, + S3: S3Config{ + Region: "us-east-1", + }, + } +} diff --git a/pkg/storage/doc.go b/pkg/storage/doc.go index 16deb99..47b4cdf 100644 --- a/pkg/storage/doc.go +++ b/pkg/storage/doc.go @@ -1,92 +1,92 @@ -// Package storage provides a unified interface for sovereign data storage -// in Tutus blockchain deployments. -// -// # Overview -// -// Tutus is designed for government blockchain deployments where data sovereignty -// is critical. Different nations have different data residency requirements: -// -// - GDPR requires EU citizen data to remain in the EU -// - China's Cybersecurity Law requires data localization -// - Russia's Federal Law 242-FZ mandates domestic storage -// - Many nations have sector-specific requirements (healthcare, finance) -// -// The storage package provides a pluggable architecture that allows each -// government deployment to choose storage backends that comply with their -// legal requirements. -// -// # Supported Providers -// -// The following storage providers are available: -// -// - neofs: NeoFS decentralized storage (can be private or public) -// - local: Local filesystem (for single-node or NAS deployments) -// - s3: S3-compatible storage (AWS, Azure, GCP, sovereign clouds) -// -// Additional providers can be implemented by satisfying the Provider interface. -// -// # Usage -// -// Basic usage with the registry: -// -// registry := storage.NewRegistry() -// registry.Register(neofs.New(cfg.NeoFS)) -// registry.Register(local.New(cfg.Local)) -// -// provider, ok := registry.Get("neofs") -// if !ok { -// log.Fatal("provider not found") -// } -// -// id, err := provider.Put(ctx, data, storage.PutOptions{}) -// if err != nil { -// log.Fatal(err) -// } -// -// # Block Storage -// -// For blockchain-specific operations, use the BlockStorage interface: -// -// bs := neofs.NewBlockStorage(cfg) -// id, err := bs.PutBlock(ctx, blockIndex, blockData) -// block, err := bs.GetBlock(ctx, blockIndex) -// -// # State Storage -// -// For state snapshots, use the StateStorage interface: -// -// ss := local.NewStateStorage(cfg) -// id, err := ss.PutState(ctx, height, stateData) -// state, err := ss.GetState(ctx, height) -// -// # Configuration -// -// Storage is configured in the node configuration file: -// -// storage: -// provider: neofs -// neofs: -// endpoints: -// - "grpc://neofs.example.gov:8080" -// container: "7s23kG4..." -// timeout: 30s -// local: -// path: /var/lib/tutus/storage -// s3: -// endpoint: "s3.sovereign-cloud.gov" -// bucket: "tutus-blocks" -// region: "national-1" -// -// # Data Sovereignty -// -// When deploying Tutus for a government: -// -// 1. Assess data residency requirements for the jurisdiction -// 2. Choose storage providers that comply (sovereign cloud, on-premises, etc.) -// 3. Configure network policies to prevent cross-border data transfer -// 4. Enable encryption at rest and in transit -// 5. Implement access controls per government security standards -// -// For NeoFS in sovereign deployments, run a private NeoFS network within -// the nation's infrastructure rather than using the public network. -package storage +// Package storage provides a unified interface for sovereign data storage +// in Tutus blockchain deployments. +// +// # Overview +// +// Tutus is designed for government blockchain deployments where data sovereignty +// is critical. Different nations have different data residency requirements: +// +// - GDPR requires EU citizen data to remain in the EU +// - China's Cybersecurity Law requires data localization +// - Russia's Federal Law 242-FZ mandates domestic storage +// - Many nations have sector-specific requirements (healthcare, finance) +// +// The storage package provides a pluggable architecture that allows each +// government deployment to choose storage backends that comply with their +// legal requirements. +// +// # Supported Providers +// +// The following storage providers are available: +// +// - neofs: NeoFS decentralized storage (can be private or public) +// - local: Local filesystem (for single-node or NAS deployments) +// - s3: S3-compatible storage (AWS, Azure, GCP, sovereign clouds) +// +// Additional providers can be implemented by satisfying the Provider interface. +// +// # Usage +// +// Basic usage with the registry: +// +// registry := storage.NewRegistry() +// registry.Register(neofs.New(cfg.NeoFS)) +// registry.Register(local.New(cfg.Local)) +// +// provider, ok := registry.Get("neofs") +// if !ok { +// log.Fatal("provider not found") +// } +// +// id, err := provider.Put(ctx, data, storage.PutOptions{}) +// if err != nil { +// log.Fatal(err) +// } +// +// # Block Storage +// +// For blockchain-specific operations, use the BlockStorage interface: +// +// bs := neofs.NewBlockStorage(cfg) +// id, err := bs.PutBlock(ctx, blockIndex, blockData) +// block, err := bs.GetBlock(ctx, blockIndex) +// +// # State Storage +// +// For state snapshots, use the StateStorage interface: +// +// ss := local.NewStateStorage(cfg) +// id, err := ss.PutState(ctx, height, stateData) +// state, err := ss.GetState(ctx, height) +// +// # Configuration +// +// Storage is configured in the node configuration file: +// +// storage: +// provider: neofs +// neofs: +// endpoints: +// - "grpc://neofs.example.gov:8080" +// container: "7s23kG4..." +// timeout: 30s +// local: +// path: /var/lib/tutus/storage +// s3: +// endpoint: "s3.sovereign-cloud.gov" +// bucket: "tutus-blocks" +// region: "national-1" +// +// # Data Sovereignty +// +// When deploying Tutus for a government: +// +// 1. Assess data residency requirements for the jurisdiction +// 2. Choose storage providers that comply (sovereign cloud, on-premises, etc.) +// 3. Configure network policies to prevent cross-border data transfer +// 4. Enable encryption at rest and in transit +// 5. Implement access controls per government security standards +// +// For NeoFS in sovereign deployments, run a private NeoFS network within +// the nation's infrastructure rather than using the public network. +package storage diff --git a/pkg/storage/errors.go b/pkg/storage/errors.go index c08ce54..fe273ed 100644 --- a/pkg/storage/errors.go +++ b/pkg/storage/errors.go @@ -1,33 +1,33 @@ -package storage - -import "errors" - -var ( - // ErrNotFound is returned when an object doesn't exist. - ErrNotFound = errors.New("storage: object not found") - - // ErrAlreadyExists is returned when an object already exists - // and overwrite is not permitted. - ErrAlreadyExists = errors.New("storage: object already exists") - - // ErrAccessDenied is returned when the operation is not permitted. - ErrAccessDenied = errors.New("storage: access denied") - - // ErrInvalidID is returned when an object ID is malformed. - ErrInvalidID = errors.New("storage: invalid object ID") - - // ErrProviderNotFound is returned when a storage provider is not registered. - ErrProviderNotFound = errors.New("storage: provider not found") - - // ErrConnectionFailed is returned when the storage backend is unreachable. - ErrConnectionFailed = errors.New("storage: connection failed") - - // ErrQuotaExceeded is returned when storage limits are reached. - ErrQuotaExceeded = errors.New("storage: quota exceeded") - - // ErrChecksumMismatch is returned when data integrity verification fails. - ErrChecksumMismatch = errors.New("storage: checksum mismatch") - - // ErrTimeout is returned when an operation exceeds its deadline. - ErrTimeout = errors.New("storage: operation timeout") -) +package storage + +import "errors" + +var ( + // ErrNotFound is returned when an object doesn't exist. + ErrNotFound = errors.New("storage: object not found") + + // ErrAlreadyExists is returned when an object already exists + // and overwrite is not permitted. + ErrAlreadyExists = errors.New("storage: object already exists") + + // ErrAccessDenied is returned when the operation is not permitted. + ErrAccessDenied = errors.New("storage: access denied") + + // ErrInvalidID is returned when an object ID is malformed. + ErrInvalidID = errors.New("storage: invalid object ID") + + // ErrProviderNotFound is returned when a storage provider is not registered. + ErrProviderNotFound = errors.New("storage: provider not found") + + // ErrConnectionFailed is returned when the storage backend is unreachable. + ErrConnectionFailed = errors.New("storage: connection failed") + + // ErrQuotaExceeded is returned when storage limits are reached. + ErrQuotaExceeded = errors.New("storage: quota exceeded") + + // ErrChecksumMismatch is returned when data integrity verification fails. + ErrChecksumMismatch = errors.New("storage: checksum mismatch") + + // ErrTimeout is returned when an operation exceeds its deadline. + ErrTimeout = errors.New("storage: operation timeout") +) diff --git a/pkg/storage/local/adapter.go b/pkg/storage/local/adapter.go index aa13cc5..b66b0f6 100644 --- a/pkg/storage/local/adapter.go +++ b/pkg/storage/local/adapter.go @@ -1,598 +1,598 @@ -// Package local provides a local filesystem storage adapter for Tutus. -// -// This adapter is suitable for: -// - Development and testing -// - Single-node deployments -// - NAS or SAN-backed storage -// - Air-gapped government networks -package local - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "github.com/tutus-one/tutus-chain/pkg/storage" -) - -const providerName = "local" - -// Adapter implements the storage.Provider interface for local filesystem. -type Adapter struct { - basePath string - maxSize int64 - mu sync.RWMutex -} - -// Config holds local adapter configuration. -type Config struct { - // Path is the base directory for storage - Path string - // MaxSize is the maximum storage size in bytes (0 = unlimited) - MaxSize int64 -} - -// New creates a new local filesystem storage adapter. -func New(cfg Config) (*Adapter, error) { - path := cfg.Path - if path == "" { - path = "./storage" - } - - // Ensure base directory exists - if err := os.MkdirAll(path, 0755); err != nil { - return nil, fmt.Errorf("local: create storage directory: %w", err) - } - - // Create subdirectories - for _, sub := range []string{"objects", "meta", "blocks", "states"} { - if err := os.MkdirAll(filepath.Join(path, sub), 0755); err != nil { - return nil, fmt.Errorf("local: create %s directory: %w", sub, err) - } - } - - return &Adapter{ - basePath: path, - maxSize: cfg.MaxSize, - }, nil -} - -// Name returns the provider identifier. -func (a *Adapter) Name() string { - return providerName -} - -// Put stores data on the local filesystem. -func (a *Adapter) Put(ctx context.Context, data io.Reader, opts storage.PutOptions) (storage.ObjectID, error) { - a.mu.Lock() - defer a.mu.Unlock() - - // Read all data - content, err := io.ReadAll(data) - if err != nil { - return storage.ObjectID{}, fmt.Errorf("local: read data: %w", err) - } - - // Generate content-addressed ID - hash := sha256.Sum256(content) - id := hex.EncodeToString(hash[:]) - - // Check size limits - if a.maxSize > 0 { - currentSize, _ := a.calculateSize() - if currentSize+int64(len(content)) > a.maxSize { - return storage.ObjectID{}, storage.ErrQuotaExceeded - } - } - - // Write object data - objPath := a.objectPath(id) - if err := os.MkdirAll(filepath.Dir(objPath), 0755); err != nil { - return storage.ObjectID{}, fmt.Errorf("local: create object dir: %w", err) - } - - if err := os.WriteFile(objPath, content, 0644); err != nil { - return storage.ObjectID{}, fmt.Errorf("local: write object: %w", err) - } - - // Write metadata - meta := objectMeta{ - Size: int64(len(content)), - ContentType: opts.ContentType, - Created: time.Now(), - Modified: time.Now(), - Checksum: id, - Attributes: opts.Attributes, - } - - metaPath := a.metaPath(id) - if err := os.MkdirAll(filepath.Dir(metaPath), 0755); err != nil { - os.Remove(objPath) // Clean up on error - return storage.ObjectID{}, fmt.Errorf("local: create meta dir: %w", err) - } - metaData, _ := json.Marshal(meta) - if err := os.WriteFile(metaPath, metaData, 0644); err != nil { - os.Remove(objPath) // Clean up on error - return storage.ObjectID{}, fmt.Errorf("local: write metadata: %w", err) - } - - return storage.ObjectID{ - Provider: providerName, - Container: "default", - ID: id, - }, nil -} - -// Get retrieves data from the local filesystem. -func (a *Adapter) Get(ctx context.Context, id storage.ObjectID) (io.ReadCloser, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - objPath := a.objectPath(id.ID) - file, err := os.Open(objPath) - if err != nil { - if os.IsNotExist(err) { - return nil, storage.ErrNotFound - } - return nil, fmt.Errorf("local: open object: %w", err) - } - - return file, nil -} - -// Delete removes an object from the local filesystem. -func (a *Adapter) Delete(ctx context.Context, id storage.ObjectID) error { - a.mu.Lock() - defer a.mu.Unlock() - - objPath := a.objectPath(id.ID) - metaPath := a.metaPath(id.ID) - - if _, err := os.Stat(objPath); os.IsNotExist(err) { - return storage.ErrNotFound - } - - if err := os.Remove(objPath); err != nil { - return fmt.Errorf("local: remove object: %w", err) - } - - os.Remove(metaPath) // Best effort metadata removal - - return nil -} - -// Exists checks if an object exists on the local filesystem. -func (a *Adapter) Exists(ctx context.Context, id storage.ObjectID) (bool, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - objPath := a.objectPath(id.ID) - _, err := os.Stat(objPath) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, fmt.Errorf("local: stat object: %w", err) - } - return true, nil -} - -// List returns objects matching the given prefix. -func (a *Adapter) List(ctx context.Context, prefix string, opts storage.ListOptions) ([]storage.ObjectInfo, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - objectsDir := filepath.Join(a.basePath, "objects") - var results []storage.ObjectInfo - - err := filepath.Walk(objectsDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return nil // Skip errors - } - if info.IsDir() { - return nil - } - - // Get relative path as ID - rel, _ := filepath.Rel(objectsDir, path) - id := strings.ReplaceAll(rel, string(filepath.Separator), "") - - if prefix != "" && !strings.HasPrefix(id, prefix) { - return nil - } - - objInfo := storage.ObjectInfo{ - ID: storage.ObjectID{ - Provider: providerName, - Container: "default", - ID: id, - }, - Size: info.Size(), - Modified: info.ModTime(), - } - - if opts.IncludeMetadata { - if meta, err := a.loadMeta(id); err == nil { - objInfo.ContentType = meta.ContentType - objInfo.Created = meta.Created - objInfo.Checksum = meta.Checksum - objInfo.Attributes = meta.Attributes - } - } - - results = append(results, objInfo) - - if opts.MaxResults > 0 && len(results) >= opts.MaxResults { - return filepath.SkipAll - } - return nil - }) - - if err != nil { - return nil, fmt.Errorf("local: list objects: %w", err) - } - - return results, nil -} - -// Head retrieves object metadata. -func (a *Adapter) Head(ctx context.Context, id storage.ObjectID) (storage.ObjectInfo, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - objPath := a.objectPath(id.ID) - info, err := os.Stat(objPath) - if err != nil { - if os.IsNotExist(err) { - return storage.ObjectInfo{}, storage.ErrNotFound - } - return storage.ObjectInfo{}, fmt.Errorf("local: stat object: %w", err) - } - - result := storage.ObjectInfo{ - ID: id, - Size: info.Size(), - Modified: info.ModTime(), - } - - if meta, err := a.loadMeta(id.ID); err == nil { - result.ContentType = meta.ContentType - result.Created = meta.Created - result.Checksum = meta.Checksum - result.Attributes = meta.Attributes - } - - return result, nil -} - -// Close is a no-op for local storage. -func (a *Adapter) Close() error { - return nil -} - -// ============================================================================= -// BlockStorage Interface Implementation -// ============================================================================= - -// PutBlock stores a block with its index. -func (a *Adapter) PutBlock(ctx context.Context, index uint32, data []byte) (storage.ObjectID, error) { - a.mu.Lock() - defer a.mu.Unlock() - - // Check size limits - if a.maxSize > 0 { - currentSize, _ := a.calculateSize() - if currentSize+int64(len(data)) > a.maxSize { - return storage.ObjectID{}, storage.ErrQuotaExceeded - } - } - - // Write block file - blockPath := a.blockPath(index) - if err := os.MkdirAll(filepath.Dir(blockPath), 0755); err != nil { - return storage.ObjectID{}, fmt.Errorf("local: create block dir: %w", err) - } - - if err := os.WriteFile(blockPath, data, 0644); err != nil { - return storage.ObjectID{}, fmt.Errorf("local: write block: %w", err) - } - - // Generate content hash for ID - hash := sha256.Sum256(data) - id := hex.EncodeToString(hash[:]) - - return storage.ObjectID{ - Provider: providerName, - Container: "blocks", - ID: id, - }, nil -} - -// GetBlock retrieves a block by its index. -func (a *Adapter) GetBlock(ctx context.Context, index uint32) ([]byte, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - blockPath := a.blockPath(index) - data, err := os.ReadFile(blockPath) - if err != nil { - if os.IsNotExist(err) { - return nil, storage.ErrNotFound - } - return nil, fmt.Errorf("local: read block: %w", err) - } - - return data, nil -} - -// GetBlockRange retrieves multiple blocks efficiently. -func (a *Adapter) GetBlockRange(ctx context.Context, start, end uint32) ([][]byte, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - if start > end { - return nil, fmt.Errorf("local: invalid block range: start %d > end %d", start, end) - } - - blocks := make([][]byte, 0, end-start+1) - for i := start; i <= end; i++ { - blockPath := a.blockPath(i) - data, err := os.ReadFile(blockPath) - if err != nil { - if os.IsNotExist(err) { - return nil, storage.ErrNotFound - } - return nil, fmt.Errorf("local: read block %d: %w", i, err) - } - blocks = append(blocks, data) - } - - return blocks, nil -} - -// GetLatestBlockIndex returns the highest block index in storage. -func (a *Adapter) GetLatestBlockIndex(ctx context.Context) (uint32, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - blocksDir := filepath.Join(a.basePath, "blocks") - var latest uint32 - var found bool - - err := filepath.Walk(blocksDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return nil // Skip errors - } - if info.IsDir() { - return nil - } - - // Parse block index from filename - name := info.Name() - if !strings.HasSuffix(name, ".block") { - return nil - } - - var index uint32 - if _, err := fmt.Sscanf(name, "%d.block", &index); err != nil { - return nil - } - - if !found || index > latest { - latest = index - found = true - } - return nil - }) - - if err != nil { - return 0, fmt.Errorf("local: scan blocks: %w", err) - } - - if !found { - return 0, storage.ErrNotFound - } - - return latest, nil -} - -// ============================================================================= -// StateStorage Interface Implementation -// ============================================================================= - -// PutState stores a state snapshot at a specific height. -func (a *Adapter) PutState(ctx context.Context, height uint32, data io.Reader) (storage.ObjectID, error) { - a.mu.Lock() - defer a.mu.Unlock() - - // Read all data - content, err := io.ReadAll(data) - if err != nil { - return storage.ObjectID{}, fmt.Errorf("local: read state data: %w", err) - } - - // Check size limits - if a.maxSize > 0 { - currentSize, _ := a.calculateSize() - if currentSize+int64(len(content)) > a.maxSize { - return storage.ObjectID{}, storage.ErrQuotaExceeded - } - } - - // Write state file - statePath := a.statePath(height) - if err := os.MkdirAll(filepath.Dir(statePath), 0755); err != nil { - return storage.ObjectID{}, fmt.Errorf("local: create state dir: %w", err) - } - - if err := os.WriteFile(statePath, content, 0644); err != nil { - return storage.ObjectID{}, fmt.Errorf("local: write state: %w", err) - } - - // Generate content hash for ID - hash := sha256.Sum256(content) - id := hex.EncodeToString(hash[:]) - - return storage.ObjectID{ - Provider: providerName, - Container: "states", - ID: id, - }, nil -} - -// GetState retrieves the state snapshot for a given height. -func (a *Adapter) GetState(ctx context.Context, height uint32) (io.ReadCloser, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - statePath := a.statePath(height) - file, err := os.Open(statePath) - if err != nil { - if os.IsNotExist(err) { - return nil, storage.ErrNotFound - } - return nil, fmt.Errorf("local: open state: %w", err) - } - - return file, nil -} - -// GetLatestState returns the most recent state snapshot. -func (a *Adapter) GetLatestState(ctx context.Context) (uint32, io.ReadCloser, error) { - a.mu.RLock() - defer a.mu.RUnlock() - - statesDir := filepath.Join(a.basePath, "states") - var latest uint32 - var found bool - - err := filepath.Walk(statesDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return nil // Skip errors - } - if info.IsDir() { - return nil - } - - // Parse height from filename - name := info.Name() - if !strings.HasSuffix(name, ".state") { - return nil - } - - var height uint32 - if _, err := fmt.Sscanf(name, "%d.state", &height); err != nil { - return nil - } - - if !found || height > latest { - latest = height - found = true - } - return nil - }) - - if err != nil { - return 0, nil, fmt.Errorf("local: scan states: %w", err) - } - - if !found { - return 0, nil, storage.ErrNotFound - } - - // Open the latest state file - statePath := a.statePath(latest) - file, err := os.Open(statePath) - if err != nil { - return 0, nil, fmt.Errorf("local: open latest state: %w", err) - } - - return latest, file, nil -} - -// objectPath returns the filesystem path for an object. -func (a *Adapter) objectPath(id string) string { - // Use first 2 chars as subdirectory for better filesystem performance - if len(id) >= 2 { - return filepath.Join(a.basePath, "objects", id[:2], id) - } - return filepath.Join(a.basePath, "objects", id) -} - -// metaPath returns the filesystem path for object metadata. -func (a *Adapter) metaPath(id string) string { - if len(id) >= 2 { - return filepath.Join(a.basePath, "meta", id[:2], id+".json") - } - return filepath.Join(a.basePath, "meta", id+".json") -} - -// blockPath returns the filesystem path for a block by index. -func (a *Adapter) blockPath(index uint32) string { - // Use first 4 digits as subdirectory for better filesystem performance - // e.g., block 12345 -> blocks/0001/12345.block - subdir := fmt.Sprintf("%04d", index/10000) - return filepath.Join(a.basePath, "blocks", subdir, fmt.Sprintf("%d.block", index)) -} - -// statePath returns the filesystem path for a state snapshot by height. -func (a *Adapter) statePath(height uint32) string { - // Use first 4 digits as subdirectory for better filesystem performance - // e.g., state 12345 -> states/0001/12345.state - subdir := fmt.Sprintf("%04d", height/10000) - return filepath.Join(a.basePath, "states", subdir, fmt.Sprintf("%d.state", height)) -} - -// objectMeta holds object metadata. -type objectMeta struct { - Size int64 `json:"size"` - ContentType string `json:"content_type,omitempty"` - Created time.Time `json:"created"` - Modified time.Time `json:"modified"` - Checksum string `json:"checksum"` - Attributes map[string]string `json:"attributes,omitempty"` -} - -// loadMeta loads object metadata from disk. -func (a *Adapter) loadMeta(id string) (objectMeta, error) { - metaPath := a.metaPath(id) - data, err := os.ReadFile(metaPath) - if err != nil { - return objectMeta{}, err - } - var meta objectMeta - err = json.Unmarshal(data, &meta) - return meta, err -} - -// calculateSize returns the total size of all stored data. -func (a *Adapter) calculateSize() (int64, error) { - var total int64 - - // Calculate size across all storage directories - for _, subdir := range []string{"objects", "blocks", "states"} { - dir := filepath.Join(a.basePath, subdir) - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return nil - } - if !info.IsDir() { - total += info.Size() - } - return nil - }) - if err != nil { - return total, err - } - } - - return total, nil -} +// Package local provides a local filesystem storage adapter for Tutus. +// +// This adapter is suitable for: +// - Development and testing +// - Single-node deployments +// - NAS or SAN-backed storage +// - Air-gapped government networks +package local + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "git.marketally.com/tutus-one/tutus-chain/pkg/storage" +) + +const providerName = "local" + +// Adapter implements the storage.Provider interface for local filesystem. +type Adapter struct { + basePath string + maxSize int64 + mu sync.RWMutex +} + +// Config holds local adapter configuration. +type Config struct { + // Path is the base directory for storage + Path string + // MaxSize is the maximum storage size in bytes (0 = unlimited) + MaxSize int64 +} + +// New creates a new local filesystem storage adapter. +func New(cfg Config) (*Adapter, error) { + path := cfg.Path + if path == "" { + path = "./storage" + } + + // Ensure base directory exists + if err := os.MkdirAll(path, 0755); err != nil { + return nil, fmt.Errorf("local: create storage directory: %w", err) + } + + // Create subdirectories + for _, sub := range []string{"objects", "meta", "blocks", "states"} { + if err := os.MkdirAll(filepath.Join(path, sub), 0755); err != nil { + return nil, fmt.Errorf("local: create %s directory: %w", sub, err) + } + } + + return &Adapter{ + basePath: path, + maxSize: cfg.MaxSize, + }, nil +} + +// Name returns the provider identifier. +func (a *Adapter) Name() string { + return providerName +} + +// Put stores data on the local filesystem. +func (a *Adapter) Put(ctx context.Context, data io.Reader, opts storage.PutOptions) (storage.ObjectID, error) { + a.mu.Lock() + defer a.mu.Unlock() + + // Read all data + content, err := io.ReadAll(data) + if err != nil { + return storage.ObjectID{}, fmt.Errorf("local: read data: %w", err) + } + + // Generate content-addressed ID + hash := sha256.Sum256(content) + id := hex.EncodeToString(hash[:]) + + // Check size limits + if a.maxSize > 0 { + currentSize, _ := a.calculateSize() + if currentSize+int64(len(content)) > a.maxSize { + return storage.ObjectID{}, storage.ErrQuotaExceeded + } + } + + // Write object data + objPath := a.objectPath(id) + if err := os.MkdirAll(filepath.Dir(objPath), 0755); err != nil { + return storage.ObjectID{}, fmt.Errorf("local: create object dir: %w", err) + } + + if err := os.WriteFile(objPath, content, 0644); err != nil { + return storage.ObjectID{}, fmt.Errorf("local: write object: %w", err) + } + + // Write metadata + meta := objectMeta{ + Size: int64(len(content)), + ContentType: opts.ContentType, + Created: time.Now(), + Modified: time.Now(), + Checksum: id, + Attributes: opts.Attributes, + } + + metaPath := a.metaPath(id) + if err := os.MkdirAll(filepath.Dir(metaPath), 0755); err != nil { + os.Remove(objPath) // Clean up on error + return storage.ObjectID{}, fmt.Errorf("local: create meta dir: %w", err) + } + metaData, _ := json.Marshal(meta) + if err := os.WriteFile(metaPath, metaData, 0644); err != nil { + os.Remove(objPath) // Clean up on error + return storage.ObjectID{}, fmt.Errorf("local: write metadata: %w", err) + } + + return storage.ObjectID{ + Provider: providerName, + Container: "default", + ID: id, + }, nil +} + +// Get retrieves data from the local filesystem. +func (a *Adapter) Get(ctx context.Context, id storage.ObjectID) (io.ReadCloser, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + objPath := a.objectPath(id.ID) + file, err := os.Open(objPath) + if err != nil { + if os.IsNotExist(err) { + return nil, storage.ErrNotFound + } + return nil, fmt.Errorf("local: open object: %w", err) + } + + return file, nil +} + +// Delete removes an object from the local filesystem. +func (a *Adapter) Delete(ctx context.Context, id storage.ObjectID) error { + a.mu.Lock() + defer a.mu.Unlock() + + objPath := a.objectPath(id.ID) + metaPath := a.metaPath(id.ID) + + if _, err := os.Stat(objPath); os.IsNotExist(err) { + return storage.ErrNotFound + } + + if err := os.Remove(objPath); err != nil { + return fmt.Errorf("local: remove object: %w", err) + } + + os.Remove(metaPath) // Best effort metadata removal + + return nil +} + +// Exists checks if an object exists on the local filesystem. +func (a *Adapter) Exists(ctx context.Context, id storage.ObjectID) (bool, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + objPath := a.objectPath(id.ID) + _, err := os.Stat(objPath) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, fmt.Errorf("local: stat object: %w", err) + } + return true, nil +} + +// List returns objects matching the given prefix. +func (a *Adapter) List(ctx context.Context, prefix string, opts storage.ListOptions) ([]storage.ObjectInfo, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + objectsDir := filepath.Join(a.basePath, "objects") + var results []storage.ObjectInfo + + err := filepath.Walk(objectsDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil // Skip errors + } + if info.IsDir() { + return nil + } + + // Get relative path as ID + rel, _ := filepath.Rel(objectsDir, path) + id := strings.ReplaceAll(rel, string(filepath.Separator), "") + + if prefix != "" && !strings.HasPrefix(id, prefix) { + return nil + } + + objInfo := storage.ObjectInfo{ + ID: storage.ObjectID{ + Provider: providerName, + Container: "default", + ID: id, + }, + Size: info.Size(), + Modified: info.ModTime(), + } + + if opts.IncludeMetadata { + if meta, err := a.loadMeta(id); err == nil { + objInfo.ContentType = meta.ContentType + objInfo.Created = meta.Created + objInfo.Checksum = meta.Checksum + objInfo.Attributes = meta.Attributes + } + } + + results = append(results, objInfo) + + if opts.MaxResults > 0 && len(results) >= opts.MaxResults { + return filepath.SkipAll + } + return nil + }) + + if err != nil { + return nil, fmt.Errorf("local: list objects: %w", err) + } + + return results, nil +} + +// Head retrieves object metadata. +func (a *Adapter) Head(ctx context.Context, id storage.ObjectID) (storage.ObjectInfo, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + objPath := a.objectPath(id.ID) + info, err := os.Stat(objPath) + if err != nil { + if os.IsNotExist(err) { + return storage.ObjectInfo{}, storage.ErrNotFound + } + return storage.ObjectInfo{}, fmt.Errorf("local: stat object: %w", err) + } + + result := storage.ObjectInfo{ + ID: id, + Size: info.Size(), + Modified: info.ModTime(), + } + + if meta, err := a.loadMeta(id.ID); err == nil { + result.ContentType = meta.ContentType + result.Created = meta.Created + result.Checksum = meta.Checksum + result.Attributes = meta.Attributes + } + + return result, nil +} + +// Close is a no-op for local storage. +func (a *Adapter) Close() error { + return nil +} + +// ============================================================================= +// BlockStorage Interface Implementation +// ============================================================================= + +// PutBlock stores a block with its index. +func (a *Adapter) PutBlock(ctx context.Context, index uint32, data []byte) (storage.ObjectID, error) { + a.mu.Lock() + defer a.mu.Unlock() + + // Check size limits + if a.maxSize > 0 { + currentSize, _ := a.calculateSize() + if currentSize+int64(len(data)) > a.maxSize { + return storage.ObjectID{}, storage.ErrQuotaExceeded + } + } + + // Write block file + blockPath := a.blockPath(index) + if err := os.MkdirAll(filepath.Dir(blockPath), 0755); err != nil { + return storage.ObjectID{}, fmt.Errorf("local: create block dir: %w", err) + } + + if err := os.WriteFile(blockPath, data, 0644); err != nil { + return storage.ObjectID{}, fmt.Errorf("local: write block: %w", err) + } + + // Generate content hash for ID + hash := sha256.Sum256(data) + id := hex.EncodeToString(hash[:]) + + return storage.ObjectID{ + Provider: providerName, + Container: "blocks", + ID: id, + }, nil +} + +// GetBlock retrieves a block by its index. +func (a *Adapter) GetBlock(ctx context.Context, index uint32) ([]byte, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + blockPath := a.blockPath(index) + data, err := os.ReadFile(blockPath) + if err != nil { + if os.IsNotExist(err) { + return nil, storage.ErrNotFound + } + return nil, fmt.Errorf("local: read block: %w", err) + } + + return data, nil +} + +// GetBlockRange retrieves multiple blocks efficiently. +func (a *Adapter) GetBlockRange(ctx context.Context, start, end uint32) ([][]byte, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + if start > end { + return nil, fmt.Errorf("local: invalid block range: start %d > end %d", start, end) + } + + blocks := make([][]byte, 0, end-start+1) + for i := start; i <= end; i++ { + blockPath := a.blockPath(i) + data, err := os.ReadFile(blockPath) + if err != nil { + if os.IsNotExist(err) { + return nil, storage.ErrNotFound + } + return nil, fmt.Errorf("local: read block %d: %w", i, err) + } + blocks = append(blocks, data) + } + + return blocks, nil +} + +// GetLatestBlockIndex returns the highest block index in storage. +func (a *Adapter) GetLatestBlockIndex(ctx context.Context) (uint32, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + blocksDir := filepath.Join(a.basePath, "blocks") + var latest uint32 + var found bool + + err := filepath.Walk(blocksDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil // Skip errors + } + if info.IsDir() { + return nil + } + + // Parse block index from filename + name := info.Name() + if !strings.HasSuffix(name, ".block") { + return nil + } + + var index uint32 + if _, err := fmt.Sscanf(name, "%d.block", &index); err != nil { + return nil + } + + if !found || index > latest { + latest = index + found = true + } + return nil + }) + + if err != nil { + return 0, fmt.Errorf("local: scan blocks: %w", err) + } + + if !found { + return 0, storage.ErrNotFound + } + + return latest, nil +} + +// ============================================================================= +// StateStorage Interface Implementation +// ============================================================================= + +// PutState stores a state snapshot at a specific height. +func (a *Adapter) PutState(ctx context.Context, height uint32, data io.Reader) (storage.ObjectID, error) { + a.mu.Lock() + defer a.mu.Unlock() + + // Read all data + content, err := io.ReadAll(data) + if err != nil { + return storage.ObjectID{}, fmt.Errorf("local: read state data: %w", err) + } + + // Check size limits + if a.maxSize > 0 { + currentSize, _ := a.calculateSize() + if currentSize+int64(len(content)) > a.maxSize { + return storage.ObjectID{}, storage.ErrQuotaExceeded + } + } + + // Write state file + statePath := a.statePath(height) + if err := os.MkdirAll(filepath.Dir(statePath), 0755); err != nil { + return storage.ObjectID{}, fmt.Errorf("local: create state dir: %w", err) + } + + if err := os.WriteFile(statePath, content, 0644); err != nil { + return storage.ObjectID{}, fmt.Errorf("local: write state: %w", err) + } + + // Generate content hash for ID + hash := sha256.Sum256(content) + id := hex.EncodeToString(hash[:]) + + return storage.ObjectID{ + Provider: providerName, + Container: "states", + ID: id, + }, nil +} + +// GetState retrieves the state snapshot for a given height. +func (a *Adapter) GetState(ctx context.Context, height uint32) (io.ReadCloser, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + statePath := a.statePath(height) + file, err := os.Open(statePath) + if err != nil { + if os.IsNotExist(err) { + return nil, storage.ErrNotFound + } + return nil, fmt.Errorf("local: open state: %w", err) + } + + return file, nil +} + +// GetLatestState returns the most recent state snapshot. +func (a *Adapter) GetLatestState(ctx context.Context) (uint32, io.ReadCloser, error) { + a.mu.RLock() + defer a.mu.RUnlock() + + statesDir := filepath.Join(a.basePath, "states") + var latest uint32 + var found bool + + err := filepath.Walk(statesDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil // Skip errors + } + if info.IsDir() { + return nil + } + + // Parse height from filename + name := info.Name() + if !strings.HasSuffix(name, ".state") { + return nil + } + + var height uint32 + if _, err := fmt.Sscanf(name, "%d.state", &height); err != nil { + return nil + } + + if !found || height > latest { + latest = height + found = true + } + return nil + }) + + if err != nil { + return 0, nil, fmt.Errorf("local: scan states: %w", err) + } + + if !found { + return 0, nil, storage.ErrNotFound + } + + // Open the latest state file + statePath := a.statePath(latest) + file, err := os.Open(statePath) + if err != nil { + return 0, nil, fmt.Errorf("local: open latest state: %w", err) + } + + return latest, file, nil +} + +// objectPath returns the filesystem path for an object. +func (a *Adapter) objectPath(id string) string { + // Use first 2 chars as subdirectory for better filesystem performance + if len(id) >= 2 { + return filepath.Join(a.basePath, "objects", id[:2], id) + } + return filepath.Join(a.basePath, "objects", id) +} + +// metaPath returns the filesystem path for object metadata. +func (a *Adapter) metaPath(id string) string { + if len(id) >= 2 { + return filepath.Join(a.basePath, "meta", id[:2], id+".json") + } + return filepath.Join(a.basePath, "meta", id+".json") +} + +// blockPath returns the filesystem path for a block by index. +func (a *Adapter) blockPath(index uint32) string { + // Use first 4 digits as subdirectory for better filesystem performance + // e.g., block 12345 -> blocks/0001/12345.block + subdir := fmt.Sprintf("%04d", index/10000) + return filepath.Join(a.basePath, "blocks", subdir, fmt.Sprintf("%d.block", index)) +} + +// statePath returns the filesystem path for a state snapshot by height. +func (a *Adapter) statePath(height uint32) string { + // Use first 4 digits as subdirectory for better filesystem performance + // e.g., state 12345 -> states/0001/12345.state + subdir := fmt.Sprintf("%04d", height/10000) + return filepath.Join(a.basePath, "states", subdir, fmt.Sprintf("%d.state", height)) +} + +// objectMeta holds object metadata. +type objectMeta struct { + Size int64 `json:"size"` + ContentType string `json:"content_type,omitempty"` + Created time.Time `json:"created"` + Modified time.Time `json:"modified"` + Checksum string `json:"checksum"` + Attributes map[string]string `json:"attributes,omitempty"` +} + +// loadMeta loads object metadata from disk. +func (a *Adapter) loadMeta(id string) (objectMeta, error) { + metaPath := a.metaPath(id) + data, err := os.ReadFile(metaPath) + if err != nil { + return objectMeta{}, err + } + var meta objectMeta + err = json.Unmarshal(data, &meta) + return meta, err +} + +// calculateSize returns the total size of all stored data. +func (a *Adapter) calculateSize() (int64, error) { + var total int64 + + // Calculate size across all storage directories + for _, subdir := range []string{"objects", "blocks", "states"} { + dir := filepath.Join(a.basePath, subdir) + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if !info.IsDir() { + total += info.Size() + } + return nil + }) + if err != nil { + return total, err + } + } + + return total, nil +} diff --git a/pkg/storage/local/adapter_test.go b/pkg/storage/local/adapter_test.go index 2ca0437..8f5bc01 100644 --- a/pkg/storage/local/adapter_test.go +++ b/pkg/storage/local/adapter_test.go @@ -1,604 +1,604 @@ -package local - -import ( - "bytes" - "context" - "io" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/tutus-one/tutus-chain/pkg/storage" -) - -func TestNew(t *testing.T) { - tmpDir := t.TempDir() - - adapter, err := New(Config{Path: tmpDir}) - if err != nil { - t.Fatalf("New() error = %v", err) - } - defer adapter.Close() - - // Check subdirectories were created - for _, sub := range []string{"objects", "meta", "blocks", "states"} { - path := filepath.Join(tmpDir, sub) - if _, err := os.Stat(path); os.IsNotExist(err) { - t.Errorf("subdirectory %s not created", sub) - } - } -} - -func TestNew_DefaultPath(t *testing.T) { - // Clean up default path after test - defer os.RemoveAll("./storage") - - adapter, err := New(Config{}) - if err != nil { - t.Fatalf("New() error = %v", err) - } - defer adapter.Close() - - if _, err := os.Stat("./storage"); os.IsNotExist(err) { - t.Error("default storage directory not created") - } -} - -func TestAdapter_Name(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - if got := adapter.Name(); got != "local" { - t.Errorf("Name() = %v, want %v", got, "local") - } -} - -func TestAdapter_PutGet(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - data := []byte("test data") - - // Put - id, err := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{ - ContentType: "text/plain", - Attributes: map[string]string{"key": "value"}, - }) - if err != nil { - t.Fatalf("Put() error = %v", err) - } - - if id.Provider != "local" { - t.Errorf("Put() Provider = %v, want %v", id.Provider, "local") - } - - // Get - reader, err := adapter.Get(ctx, id) - if err != nil { - t.Fatalf("Get() error = %v", err) - } - defer reader.Close() - - got, err := io.ReadAll(reader) - if err != nil { - t.Fatalf("ReadAll() error = %v", err) - } - - if !bytes.Equal(got, data) { - t.Errorf("Get() = %v, want %v", got, data) - } -} - -func TestAdapter_Get_NotFound(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - _, err := adapter.Get(ctx, storage.ObjectID{ID: "nonexistent"}) - - if err != storage.ErrNotFound { - t.Errorf("Get() error = %v, want %v", err, storage.ErrNotFound) - } -} - -func TestAdapter_Exists(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - data := []byte("test data") - - // Put - id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) - - // Exists should return true - exists, err := adapter.Exists(ctx, id) - if err != nil { - t.Fatalf("Exists() error = %v", err) - } - if !exists { - t.Error("Exists() = false, want true") - } - - // Non-existent should return false - exists, err = adapter.Exists(ctx, storage.ObjectID{ID: "nonexistent"}) - if err != nil { - t.Fatalf("Exists() error = %v", err) - } - if exists { - t.Error("Exists() = true, want false") - } -} - -func TestAdapter_Delete(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - data := []byte("test data") - - // Put - id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) - - // Delete - err := adapter.Delete(ctx, id) - if err != nil { - t.Fatalf("Delete() error = %v", err) - } - - // Should not exist anymore - exists, _ := adapter.Exists(ctx, id) - if exists { - t.Error("object still exists after Delete()") - } -} - -func TestAdapter_Delete_NotFound(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - err := adapter.Delete(ctx, storage.ObjectID{ID: "nonexistent"}) - - if err != storage.ErrNotFound { - t.Errorf("Delete() error = %v, want %v", err, storage.ErrNotFound) - } -} - -func TestAdapter_Head(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - data := []byte("test data") - - // Put - id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{ - ContentType: "text/plain", - Attributes: map[string]string{"key": "value"}, - }) - - // Head - info, err := adapter.Head(ctx, id) - if err != nil { - t.Fatalf("Head() error = %v", err) - } - - if info.Size != int64(len(data)) { - t.Errorf("Head() Size = %v, want %v", info.Size, len(data)) - } - if info.ContentType != "text/plain" { - t.Errorf("Head() ContentType = %v, want %v", info.ContentType, "text/plain") - } - if info.Attributes["key"] != "value" { - t.Errorf("Head() Attributes[key] = %v, want %v", info.Attributes["key"], "value") - } -} - -func TestAdapter_Head_NotFound(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - _, err := adapter.Head(ctx, storage.ObjectID{ID: "nonexistent"}) - - if err != storage.ErrNotFound { - t.Errorf("Head() error = %v, want %v", err, storage.ErrNotFound) - } -} - -func TestAdapter_List(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - - // Put some objects - ids := make([]storage.ObjectID, 3) - for i := 0; i < 3; i++ { - data := []byte(strings.Repeat("x", i+1)) // Different content = different ID - id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) - ids[i] = id - } - - // List all - results, err := adapter.List(ctx, "", storage.ListOptions{}) - if err != nil { - t.Fatalf("List() error = %v", err) - } - - if len(results) != 3 { - t.Errorf("List() returned %d items, want 3", len(results)) - } -} - -func TestAdapter_List_MaxResults(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - - // Put some objects - for i := 0; i < 5; i++ { - data := []byte(strings.Repeat("x", i+1)) - adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) - } - - // List with limit - results, err := adapter.List(ctx, "", storage.ListOptions{MaxResults: 2}) - if err != nil { - t.Fatalf("List() error = %v", err) - } - - if len(results) != 2 { - t.Errorf("List() returned %d items, want 2", len(results)) - } -} - -func TestAdapter_QuotaExceeded(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir(), MaxSize: 100}) - defer adapter.Close() - - ctx := context.Background() - - // First put should succeed - data1 := []byte(strings.Repeat("x", 50)) - _, err := adapter.Put(ctx, bytes.NewReader(data1), storage.PutOptions{}) - if err != nil { - t.Fatalf("Put() error = %v", err) - } - - // Second put should exceed quota - data2 := []byte(strings.Repeat("y", 60)) - _, err = adapter.Put(ctx, bytes.NewReader(data2), storage.PutOptions{}) - if err != storage.ErrQuotaExceeded { - t.Errorf("Put() error = %v, want %v", err, storage.ErrQuotaExceeded) - } -} - -// ============================================================================= -// BlockStorage Tests -// ============================================================================= - -func TestAdapter_PutGetBlock(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - blockData := []byte("block 0 data") - - // PutBlock - id, err := adapter.PutBlock(ctx, 0, blockData) - if err != nil { - t.Fatalf("PutBlock() error = %v", err) - } - - if id.Container != "blocks" { - t.Errorf("PutBlock() Container = %v, want %v", id.Container, "blocks") - } - - // GetBlock - got, err := adapter.GetBlock(ctx, 0) - if err != nil { - t.Fatalf("GetBlock() error = %v", err) - } - - if !bytes.Equal(got, blockData) { - t.Errorf("GetBlock() = %v, want %v", got, blockData) - } -} - -func TestAdapter_GetBlock_NotFound(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - _, err := adapter.GetBlock(ctx, 999) - - if err != storage.ErrNotFound { - t.Errorf("GetBlock() error = %v, want %v", err, storage.ErrNotFound) - } -} - -func TestAdapter_GetBlockRange(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - - // Put several blocks - for i := uint32(0); i < 5; i++ { - data := []byte(strings.Repeat(string(rune('a'+i)), 10)) - _, err := adapter.PutBlock(ctx, i, data) - if err != nil { - t.Fatalf("PutBlock(%d) error = %v", i, err) - } - } - - // Get range - blocks, err := adapter.GetBlockRange(ctx, 1, 3) - if err != nil { - t.Fatalf("GetBlockRange() error = %v", err) - } - - if len(blocks) != 3 { - t.Errorf("GetBlockRange() returned %d blocks, want 3", len(blocks)) - } - - // Verify content - for i, block := range blocks { - expected := []byte(strings.Repeat(string(rune('b'+i)), 10)) - if !bytes.Equal(block, expected) { - t.Errorf("GetBlockRange() block %d = %v, want %v", i+1, block, expected) - } - } -} - -func TestAdapter_GetBlockRange_InvalidRange(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - _, err := adapter.GetBlockRange(ctx, 10, 5) - - if err == nil || !strings.Contains(err.Error(), "invalid block range") { - t.Errorf("GetBlockRange() error = %v, want invalid block range error", err) - } -} - -func TestAdapter_GetBlockRange_NotFound(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - - // Put only block 0 - adapter.PutBlock(ctx, 0, []byte("block 0")) - - // Try to get range that includes missing block - _, err := adapter.GetBlockRange(ctx, 0, 2) - - if err != storage.ErrNotFound { - t.Errorf("GetBlockRange() error = %v, want %v", err, storage.ErrNotFound) - } -} - -func TestAdapter_GetLatestBlockIndex(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - - // No blocks yet - _, err := adapter.GetLatestBlockIndex(ctx) - if err != storage.ErrNotFound { - t.Errorf("GetLatestBlockIndex() error = %v, want %v", err, storage.ErrNotFound) - } - - // Put some blocks - for i := uint32(0); i < 10; i++ { - adapter.PutBlock(ctx, i, []byte("block")) - } - - // Get latest - latest, err := adapter.GetLatestBlockIndex(ctx) - if err != nil { - t.Fatalf("GetLatestBlockIndex() error = %v", err) - } - - if latest != 9 { - t.Errorf("GetLatestBlockIndex() = %v, want 9", latest) - } -} - -func TestAdapter_PutBlock_QuotaExceeded(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir(), MaxSize: 100}) - defer adapter.Close() - - ctx := context.Background() - - // First block should succeed - _, err := adapter.PutBlock(ctx, 0, []byte(strings.Repeat("x", 50))) - if err != nil { - t.Fatalf("PutBlock() error = %v", err) - } - - // Second block should exceed quota - _, err = adapter.PutBlock(ctx, 1, []byte(strings.Repeat("y", 60))) - if err != storage.ErrQuotaExceeded { - t.Errorf("PutBlock() error = %v, want %v", err, storage.ErrQuotaExceeded) - } -} - -// ============================================================================= -// StateStorage Tests -// ============================================================================= - -func TestAdapter_PutGetState(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - stateData := []byte("state at height 0") - - // PutState - id, err := adapter.PutState(ctx, 0, bytes.NewReader(stateData)) - if err != nil { - t.Fatalf("PutState() error = %v", err) - } - - if id.Container != "states" { - t.Errorf("PutState() Container = %v, want %v", id.Container, "states") - } - - // GetState - reader, err := adapter.GetState(ctx, 0) - if err != nil { - t.Fatalf("GetState() error = %v", err) - } - defer reader.Close() - - got, err := io.ReadAll(reader) - if err != nil { - t.Fatalf("ReadAll() error = %v", err) - } - - if !bytes.Equal(got, stateData) { - t.Errorf("GetState() = %v, want %v", got, stateData) - } -} - -func TestAdapter_GetState_NotFound(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - _, err := adapter.GetState(ctx, 999) - - if err != storage.ErrNotFound { - t.Errorf("GetState() error = %v, want %v", err, storage.ErrNotFound) - } -} - -func TestAdapter_GetLatestState(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir()}) - defer adapter.Close() - - ctx := context.Background() - - // No states yet - _, _, err := adapter.GetLatestState(ctx) - if err != storage.ErrNotFound { - t.Errorf("GetLatestState() error = %v, want %v", err, storage.ErrNotFound) - } - - // Put some states - for i := uint32(0); i < 5; i++ { - data := []byte(strings.Repeat(string(rune('a'+i)), 10)) - _, err := adapter.PutState(ctx, i, bytes.NewReader(data)) - if err != nil { - t.Fatalf("PutState(%d) error = %v", i, err) - } - } - - // Get latest - height, reader, err := adapter.GetLatestState(ctx) - if err != nil { - t.Fatalf("GetLatestState() error = %v", err) - } - defer reader.Close() - - if height != 4 { - t.Errorf("GetLatestState() height = %v, want 4", height) - } - - got, _ := io.ReadAll(reader) - expected := []byte(strings.Repeat("e", 10)) - if !bytes.Equal(got, expected) { - t.Errorf("GetLatestState() data = %v, want %v", got, expected) - } -} - -func TestAdapter_PutState_QuotaExceeded(t *testing.T) { - adapter, _ := New(Config{Path: t.TempDir(), MaxSize: 100}) - defer adapter.Close() - - ctx := context.Background() - - // First state should succeed - _, err := adapter.PutState(ctx, 0, bytes.NewReader([]byte(strings.Repeat("x", 50)))) - if err != nil { - t.Fatalf("PutState() error = %v", err) - } - - // Second state should exceed quota - _, err = adapter.PutState(ctx, 1, bytes.NewReader([]byte(strings.Repeat("y", 60)))) - if err != storage.ErrQuotaExceeded { - t.Errorf("PutState() error = %v, want %v", err, storage.ErrQuotaExceeded) - } -} - -// ============================================================================= -// Path Helper Tests -// ============================================================================= - -func TestAdapter_blockPath(t *testing.T) { - adapter := &Adapter{basePath: "/storage"} - - tests := []struct { - index uint32 - expected string - }{ - {0, filepath.Join("/storage", "blocks", "0000", "0.block")}, - {9999, filepath.Join("/storage", "blocks", "0000", "9999.block")}, - {10000, filepath.Join("/storage", "blocks", "0001", "10000.block")}, - {12345, filepath.Join("/storage", "blocks", "0001", "12345.block")}, - {99999, filepath.Join("/storage", "blocks", "0009", "99999.block")}, - } - - for _, tt := range tests { - got := adapter.blockPath(tt.index) - if got != tt.expected { - t.Errorf("blockPath(%d) = %v, want %v", tt.index, got, tt.expected) - } - } -} - -func TestAdapter_statePath(t *testing.T) { - adapter := &Adapter{basePath: "/storage"} - - tests := []struct { - height uint32 - expected string - }{ - {0, filepath.Join("/storage", "states", "0000", "0.state")}, - {9999, filepath.Join("/storage", "states", "0000", "9999.state")}, - {10000, filepath.Join("/storage", "states", "0001", "10000.state")}, - {12345, filepath.Join("/storage", "states", "0001", "12345.state")}, - } - - for _, tt := range tests { - got := adapter.statePath(tt.height) - if got != tt.expected { - t.Errorf("statePath(%d) = %v, want %v", tt.height, got, tt.expected) - } - } -} - -// ============================================================================= -// Interface Compliance Tests -// ============================================================================= - -func TestAdapter_ImplementsProvider(t *testing.T) { - var _ storage.Provider = (*Adapter)(nil) -} - -func TestAdapter_ImplementsBlockStorage(t *testing.T) { - var _ storage.BlockStorage = (*Adapter)(nil) -} - -func TestAdapter_ImplementsStateStorage(t *testing.T) { - var _ storage.StateStorage = (*Adapter)(nil) -} +package local + +import ( + "bytes" + "context" + "io" + "os" + "path/filepath" + "strings" + "testing" + + "git.marketally.com/tutus-one/tutus-chain/pkg/storage" +) + +func TestNew(t *testing.T) { + tmpDir := t.TempDir() + + adapter, err := New(Config{Path: tmpDir}) + if err != nil { + t.Fatalf("New() error = %v", err) + } + defer adapter.Close() + + // Check subdirectories were created + for _, sub := range []string{"objects", "meta", "blocks", "states"} { + path := filepath.Join(tmpDir, sub) + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Errorf("subdirectory %s not created", sub) + } + } +} + +func TestNew_DefaultPath(t *testing.T) { + // Clean up default path after test + defer os.RemoveAll("./storage") + + adapter, err := New(Config{}) + if err != nil { + t.Fatalf("New() error = %v", err) + } + defer adapter.Close() + + if _, err := os.Stat("./storage"); os.IsNotExist(err) { + t.Error("default storage directory not created") + } +} + +func TestAdapter_Name(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + if got := adapter.Name(); got != "local" { + t.Errorf("Name() = %v, want %v", got, "local") + } +} + +func TestAdapter_PutGet(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + data := []byte("test data") + + // Put + id, err := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{ + ContentType: "text/plain", + Attributes: map[string]string{"key": "value"}, + }) + if err != nil { + t.Fatalf("Put() error = %v", err) + } + + if id.Provider != "local" { + t.Errorf("Put() Provider = %v, want %v", id.Provider, "local") + } + + // Get + reader, err := adapter.Get(ctx, id) + if err != nil { + t.Fatalf("Get() error = %v", err) + } + defer reader.Close() + + got, err := io.ReadAll(reader) + if err != nil { + t.Fatalf("ReadAll() error = %v", err) + } + + if !bytes.Equal(got, data) { + t.Errorf("Get() = %v, want %v", got, data) + } +} + +func TestAdapter_Get_NotFound(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + _, err := adapter.Get(ctx, storage.ObjectID{ID: "nonexistent"}) + + if err != storage.ErrNotFound { + t.Errorf("Get() error = %v, want %v", err, storage.ErrNotFound) + } +} + +func TestAdapter_Exists(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + data := []byte("test data") + + // Put + id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) + + // Exists should return true + exists, err := adapter.Exists(ctx, id) + if err != nil { + t.Fatalf("Exists() error = %v", err) + } + if !exists { + t.Error("Exists() = false, want true") + } + + // Non-existent should return false + exists, err = adapter.Exists(ctx, storage.ObjectID{ID: "nonexistent"}) + if err != nil { + t.Fatalf("Exists() error = %v", err) + } + if exists { + t.Error("Exists() = true, want false") + } +} + +func TestAdapter_Delete(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + data := []byte("test data") + + // Put + id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) + + // Delete + err := adapter.Delete(ctx, id) + if err != nil { + t.Fatalf("Delete() error = %v", err) + } + + // Should not exist anymore + exists, _ := adapter.Exists(ctx, id) + if exists { + t.Error("object still exists after Delete()") + } +} + +func TestAdapter_Delete_NotFound(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + err := adapter.Delete(ctx, storage.ObjectID{ID: "nonexistent"}) + + if err != storage.ErrNotFound { + t.Errorf("Delete() error = %v, want %v", err, storage.ErrNotFound) + } +} + +func TestAdapter_Head(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + data := []byte("test data") + + // Put + id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{ + ContentType: "text/plain", + Attributes: map[string]string{"key": "value"}, + }) + + // Head + info, err := adapter.Head(ctx, id) + if err != nil { + t.Fatalf("Head() error = %v", err) + } + + if info.Size != int64(len(data)) { + t.Errorf("Head() Size = %v, want %v", info.Size, len(data)) + } + if info.ContentType != "text/plain" { + t.Errorf("Head() ContentType = %v, want %v", info.ContentType, "text/plain") + } + if info.Attributes["key"] != "value" { + t.Errorf("Head() Attributes[key] = %v, want %v", info.Attributes["key"], "value") + } +} + +func TestAdapter_Head_NotFound(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + _, err := adapter.Head(ctx, storage.ObjectID{ID: "nonexistent"}) + + if err != storage.ErrNotFound { + t.Errorf("Head() error = %v, want %v", err, storage.ErrNotFound) + } +} + +func TestAdapter_List(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + + // Put some objects + ids := make([]storage.ObjectID, 3) + for i := 0; i < 3; i++ { + data := []byte(strings.Repeat("x", i+1)) // Different content = different ID + id, _ := adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) + ids[i] = id + } + + // List all + results, err := adapter.List(ctx, "", storage.ListOptions{}) + if err != nil { + t.Fatalf("List() error = %v", err) + } + + if len(results) != 3 { + t.Errorf("List() returned %d items, want 3", len(results)) + } +} + +func TestAdapter_List_MaxResults(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + + // Put some objects + for i := 0; i < 5; i++ { + data := []byte(strings.Repeat("x", i+1)) + adapter.Put(ctx, bytes.NewReader(data), storage.PutOptions{}) + } + + // List with limit + results, err := adapter.List(ctx, "", storage.ListOptions{MaxResults: 2}) + if err != nil { + t.Fatalf("List() error = %v", err) + } + + if len(results) != 2 { + t.Errorf("List() returned %d items, want 2", len(results)) + } +} + +func TestAdapter_QuotaExceeded(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir(), MaxSize: 100}) + defer adapter.Close() + + ctx := context.Background() + + // First put should succeed + data1 := []byte(strings.Repeat("x", 50)) + _, err := adapter.Put(ctx, bytes.NewReader(data1), storage.PutOptions{}) + if err != nil { + t.Fatalf("Put() error = %v", err) + } + + // Second put should exceed quota + data2 := []byte(strings.Repeat("y", 60)) + _, err = adapter.Put(ctx, bytes.NewReader(data2), storage.PutOptions{}) + if err != storage.ErrQuotaExceeded { + t.Errorf("Put() error = %v, want %v", err, storage.ErrQuotaExceeded) + } +} + +// ============================================================================= +// BlockStorage Tests +// ============================================================================= + +func TestAdapter_PutGetBlock(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + blockData := []byte("block 0 data") + + // PutBlock + id, err := adapter.PutBlock(ctx, 0, blockData) + if err != nil { + t.Fatalf("PutBlock() error = %v", err) + } + + if id.Container != "blocks" { + t.Errorf("PutBlock() Container = %v, want %v", id.Container, "blocks") + } + + // GetBlock + got, err := adapter.GetBlock(ctx, 0) + if err != nil { + t.Fatalf("GetBlock() error = %v", err) + } + + if !bytes.Equal(got, blockData) { + t.Errorf("GetBlock() = %v, want %v", got, blockData) + } +} + +func TestAdapter_GetBlock_NotFound(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + _, err := adapter.GetBlock(ctx, 999) + + if err != storage.ErrNotFound { + t.Errorf("GetBlock() error = %v, want %v", err, storage.ErrNotFound) + } +} + +func TestAdapter_GetBlockRange(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + + // Put several blocks + for i := uint32(0); i < 5; i++ { + data := []byte(strings.Repeat(string(rune('a'+i)), 10)) + _, err := adapter.PutBlock(ctx, i, data) + if err != nil { + t.Fatalf("PutBlock(%d) error = %v", i, err) + } + } + + // Get range + blocks, err := adapter.GetBlockRange(ctx, 1, 3) + if err != nil { + t.Fatalf("GetBlockRange() error = %v", err) + } + + if len(blocks) != 3 { + t.Errorf("GetBlockRange() returned %d blocks, want 3", len(blocks)) + } + + // Verify content + for i, block := range blocks { + expected := []byte(strings.Repeat(string(rune('b'+i)), 10)) + if !bytes.Equal(block, expected) { + t.Errorf("GetBlockRange() block %d = %v, want %v", i+1, block, expected) + } + } +} + +func TestAdapter_GetBlockRange_InvalidRange(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + _, err := adapter.GetBlockRange(ctx, 10, 5) + + if err == nil || !strings.Contains(err.Error(), "invalid block range") { + t.Errorf("GetBlockRange() error = %v, want invalid block range error", err) + } +} + +func TestAdapter_GetBlockRange_NotFound(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + + // Put only block 0 + adapter.PutBlock(ctx, 0, []byte("block 0")) + + // Try to get range that includes missing block + _, err := adapter.GetBlockRange(ctx, 0, 2) + + if err != storage.ErrNotFound { + t.Errorf("GetBlockRange() error = %v, want %v", err, storage.ErrNotFound) + } +} + +func TestAdapter_GetLatestBlockIndex(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + + // No blocks yet + _, err := adapter.GetLatestBlockIndex(ctx) + if err != storage.ErrNotFound { + t.Errorf("GetLatestBlockIndex() error = %v, want %v", err, storage.ErrNotFound) + } + + // Put some blocks + for i := uint32(0); i < 10; i++ { + adapter.PutBlock(ctx, i, []byte("block")) + } + + // Get latest + latest, err := adapter.GetLatestBlockIndex(ctx) + if err != nil { + t.Fatalf("GetLatestBlockIndex() error = %v", err) + } + + if latest != 9 { + t.Errorf("GetLatestBlockIndex() = %v, want 9", latest) + } +} + +func TestAdapter_PutBlock_QuotaExceeded(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir(), MaxSize: 100}) + defer adapter.Close() + + ctx := context.Background() + + // First block should succeed + _, err := adapter.PutBlock(ctx, 0, []byte(strings.Repeat("x", 50))) + if err != nil { + t.Fatalf("PutBlock() error = %v", err) + } + + // Second block should exceed quota + _, err = adapter.PutBlock(ctx, 1, []byte(strings.Repeat("y", 60))) + if err != storage.ErrQuotaExceeded { + t.Errorf("PutBlock() error = %v, want %v", err, storage.ErrQuotaExceeded) + } +} + +// ============================================================================= +// StateStorage Tests +// ============================================================================= + +func TestAdapter_PutGetState(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + stateData := []byte("state at height 0") + + // PutState + id, err := adapter.PutState(ctx, 0, bytes.NewReader(stateData)) + if err != nil { + t.Fatalf("PutState() error = %v", err) + } + + if id.Container != "states" { + t.Errorf("PutState() Container = %v, want %v", id.Container, "states") + } + + // GetState + reader, err := adapter.GetState(ctx, 0) + if err != nil { + t.Fatalf("GetState() error = %v", err) + } + defer reader.Close() + + got, err := io.ReadAll(reader) + if err != nil { + t.Fatalf("ReadAll() error = %v", err) + } + + if !bytes.Equal(got, stateData) { + t.Errorf("GetState() = %v, want %v", got, stateData) + } +} + +func TestAdapter_GetState_NotFound(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + _, err := adapter.GetState(ctx, 999) + + if err != storage.ErrNotFound { + t.Errorf("GetState() error = %v, want %v", err, storage.ErrNotFound) + } +} + +func TestAdapter_GetLatestState(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir()}) + defer adapter.Close() + + ctx := context.Background() + + // No states yet + _, _, err := adapter.GetLatestState(ctx) + if err != storage.ErrNotFound { + t.Errorf("GetLatestState() error = %v, want %v", err, storage.ErrNotFound) + } + + // Put some states + for i := uint32(0); i < 5; i++ { + data := []byte(strings.Repeat(string(rune('a'+i)), 10)) + _, err := adapter.PutState(ctx, i, bytes.NewReader(data)) + if err != nil { + t.Fatalf("PutState(%d) error = %v", i, err) + } + } + + // Get latest + height, reader, err := adapter.GetLatestState(ctx) + if err != nil { + t.Fatalf("GetLatestState() error = %v", err) + } + defer reader.Close() + + if height != 4 { + t.Errorf("GetLatestState() height = %v, want 4", height) + } + + got, _ := io.ReadAll(reader) + expected := []byte(strings.Repeat("e", 10)) + if !bytes.Equal(got, expected) { + t.Errorf("GetLatestState() data = %v, want %v", got, expected) + } +} + +func TestAdapter_PutState_QuotaExceeded(t *testing.T) { + adapter, _ := New(Config{Path: t.TempDir(), MaxSize: 100}) + defer adapter.Close() + + ctx := context.Background() + + // First state should succeed + _, err := adapter.PutState(ctx, 0, bytes.NewReader([]byte(strings.Repeat("x", 50)))) + if err != nil { + t.Fatalf("PutState() error = %v", err) + } + + // Second state should exceed quota + _, err = adapter.PutState(ctx, 1, bytes.NewReader([]byte(strings.Repeat("y", 60)))) + if err != storage.ErrQuotaExceeded { + t.Errorf("PutState() error = %v, want %v", err, storage.ErrQuotaExceeded) + } +} + +// ============================================================================= +// Path Helper Tests +// ============================================================================= + +func TestAdapter_blockPath(t *testing.T) { + adapter := &Adapter{basePath: "/storage"} + + tests := []struct { + index uint32 + expected string + }{ + {0, filepath.Join("/storage", "blocks", "0000", "0.block")}, + {9999, filepath.Join("/storage", "blocks", "0000", "9999.block")}, + {10000, filepath.Join("/storage", "blocks", "0001", "10000.block")}, + {12345, filepath.Join("/storage", "blocks", "0001", "12345.block")}, + {99999, filepath.Join("/storage", "blocks", "0009", "99999.block")}, + } + + for _, tt := range tests { + got := adapter.blockPath(tt.index) + if got != tt.expected { + t.Errorf("blockPath(%d) = %v, want %v", tt.index, got, tt.expected) + } + } +} + +func TestAdapter_statePath(t *testing.T) { + adapter := &Adapter{basePath: "/storage"} + + tests := []struct { + height uint32 + expected string + }{ + {0, filepath.Join("/storage", "states", "0000", "0.state")}, + {9999, filepath.Join("/storage", "states", "0000", "9999.state")}, + {10000, filepath.Join("/storage", "states", "0001", "10000.state")}, + {12345, filepath.Join("/storage", "states", "0001", "12345.state")}, + } + + for _, tt := range tests { + got := adapter.statePath(tt.height) + if got != tt.expected { + t.Errorf("statePath(%d) = %v, want %v", tt.height, got, tt.expected) + } + } +} + +// ============================================================================= +// Interface Compliance Tests +// ============================================================================= + +func TestAdapter_ImplementsProvider(t *testing.T) { + var _ storage.Provider = (*Adapter)(nil) +} + +func TestAdapter_ImplementsBlockStorage(t *testing.T) { + var _ storage.BlockStorage = (*Adapter)(nil) +} + +func TestAdapter_ImplementsStateStorage(t *testing.T) { + var _ storage.StateStorage = (*Adapter)(nil) +} diff --git a/pkg/storage/neofs/adapter.go b/pkg/storage/neofs/adapter.go index 4d82940..b08daf2 100644 --- a/pkg/storage/neofs/adapter.go +++ b/pkg/storage/neofs/adapter.go @@ -1,155 +1,155 @@ -// Package neofs provides a NeoFS storage adapter for Tutus. -// -// NeoFS is a decentralized storage network that can be deployed as either -// a public network or a private sovereign network for government use. -// -// For sovereign deployments, governments should run their own NeoFS -// infrastructure within national borders to ensure data residency compliance. -package neofs - -import ( - "bytes" - "context" - "fmt" - "io" - "time" - - "github.com/nspcc-dev/neofs-sdk-go/pool" - - "github.com/tutus-one/tutus-chain/pkg/storage" -) - -const providerName = "neofs" - -// Adapter implements the storage.Provider interface for NeoFS. -type Adapter struct { - pool *pool.Pool - container string - timeout time.Duration -} - -// Config holds NeoFS adapter configuration. -type Config struct { - // Pool is an initialized NeoFS connection pool - Pool *pool.Pool - // Container is the NeoFS container ID string - Container string - // Timeout for NeoFS operations - Timeout time.Duration -} - -// New creates a new NeoFS storage adapter. -func New(cfg Config) *Adapter { - timeout := cfg.Timeout - if timeout == 0 { - timeout = 30 * time.Second - } - return &Adapter{ - pool: cfg.Pool, - container: cfg.Container, - timeout: timeout, - } -} - -// Name returns the provider identifier. -func (a *Adapter) Name() string { - return providerName -} - -// Put stores data in NeoFS and returns the object ID. -func (a *Adapter) Put(ctx context.Context, data io.Reader, opts storage.PutOptions) (storage.ObjectID, error) { - ctx, cancel := context.WithTimeout(ctx, a.timeout) - defer cancel() - - // Read all data - content, err := io.ReadAll(data) - if err != nil { - return storage.ObjectID{}, fmt.Errorf("neofs: read data: %w", err) - } - - // TODO: Implement NeoFS object upload using pool - // This requires proper NeoFS SDK integration with: - // - Container ID parsing - // - Object creation with attributes - // - Signing with user credentials - // - Upload via pool.ObjectPutInit() - - _ = content // Use content when implementing - - return storage.ObjectID{}, fmt.Errorf("neofs: Put not yet implemented - use existing neofs helpers") -} - -// Get retrieves an object from NeoFS. -func (a *Adapter) Get(ctx context.Context, id storage.ObjectID) (io.ReadCloser, error) { - ctx, cancel := context.WithTimeout(ctx, a.timeout) - defer cancel() - - // TODO: Implement NeoFS object download - // This requires: - // - Object ID parsing - // - Download via pool.ObjectGetInit() - // - Payload reading - - _ = ctx // Use ctx when implementing - - return nil, fmt.Errorf("neofs: Get not yet implemented - use existing neofs helpers") -} - -// Delete removes an object from NeoFS. -func (a *Adapter) Delete(ctx context.Context, id storage.ObjectID) error { - ctx, cancel := context.WithTimeout(ctx, a.timeout) - defer cancel() - - // TODO: Implement NeoFS object deletion - _ = ctx - - return fmt.Errorf("neofs: Delete not yet implemented") -} - -// Exists checks if an object exists in NeoFS. -func (a *Adapter) Exists(ctx context.Context, id storage.ObjectID) (bool, error) { - ctx, cancel := context.WithTimeout(ctx, a.timeout) - defer cancel() - - // TODO: Implement via ObjectHead - _ = ctx - - return false, fmt.Errorf("neofs: Exists not yet implemented") -} - -// List returns objects matching the given prefix. -func (a *Adapter) List(ctx context.Context, prefix string, opts storage.ListOptions) ([]storage.ObjectInfo, error) { - ctx, cancel := context.WithTimeout(ctx, a.timeout) - defer cancel() - - // TODO: Implement via ObjectSearch - _ = ctx - _ = prefix - - return nil, fmt.Errorf("neofs: List not yet implemented") -} - -// Head retrieves object metadata. -func (a *Adapter) Head(ctx context.Context, id storage.ObjectID) (storage.ObjectInfo, error) { - ctx, cancel := context.WithTimeout(ctx, a.timeout) - defer cancel() - - // TODO: Implement via ObjectHead - _ = ctx - - return storage.ObjectInfo{}, fmt.Errorf("neofs: Head not yet implemented") -} - -// Close releases NeoFS pool resources. -func (a *Adapter) Close() error { - if a.pool != nil { - a.pool.Close() - } - return nil -} - -// WrapExistingReader wraps data from the existing NeoFS helpers into the storage interface. -// This is a bridge to use existing pkg/services/helpers/neofs code with the new interface. -func WrapExistingReader(data []byte) io.ReadCloser { - return io.NopCloser(bytes.NewReader(data)) -} +// Package neofs provides a NeoFS storage adapter for Tutus. +// +// NeoFS is a decentralized storage network that can be deployed as either +// a public network or a private sovereign network for government use. +// +// For sovereign deployments, governments should run their own NeoFS +// infrastructure within national borders to ensure data residency compliance. +package neofs + +import ( + "bytes" + "context" + "fmt" + "io" + "time" + + "github.com/nspcc-dev/neofs-sdk-go/pool" + + "git.marketally.com/tutus-one/tutus-chain/pkg/storage" +) + +const providerName = "neofs" + +// Adapter implements the storage.Provider interface for NeoFS. +type Adapter struct { + pool *pool.Pool + container string + timeout time.Duration +} + +// Config holds NeoFS adapter configuration. +type Config struct { + // Pool is an initialized NeoFS connection pool + Pool *pool.Pool + // Container is the NeoFS container ID string + Container string + // Timeout for NeoFS operations + Timeout time.Duration +} + +// New creates a new NeoFS storage adapter. +func New(cfg Config) *Adapter { + timeout := cfg.Timeout + if timeout == 0 { + timeout = 30 * time.Second + } + return &Adapter{ + pool: cfg.Pool, + container: cfg.Container, + timeout: timeout, + } +} + +// Name returns the provider identifier. +func (a *Adapter) Name() string { + return providerName +} + +// Put stores data in NeoFS and returns the object ID. +func (a *Adapter) Put(ctx context.Context, data io.Reader, opts storage.PutOptions) (storage.ObjectID, error) { + ctx, cancel := context.WithTimeout(ctx, a.timeout) + defer cancel() + + // Read all data + content, err := io.ReadAll(data) + if err != nil { + return storage.ObjectID{}, fmt.Errorf("neofs: read data: %w", err) + } + + // TODO: Implement NeoFS object upload using pool + // This requires proper NeoFS SDK integration with: + // - Container ID parsing + // - Object creation with attributes + // - Signing with user credentials + // - Upload via pool.ObjectPutInit() + + _ = content // Use content when implementing + + return storage.ObjectID{}, fmt.Errorf("neofs: Put not yet implemented - use existing neofs helpers") +} + +// Get retrieves an object from NeoFS. +func (a *Adapter) Get(ctx context.Context, id storage.ObjectID) (io.ReadCloser, error) { + ctx, cancel := context.WithTimeout(ctx, a.timeout) + defer cancel() + + // TODO: Implement NeoFS object download + // This requires: + // - Object ID parsing + // - Download via pool.ObjectGetInit() + // - Payload reading + + _ = ctx // Use ctx when implementing + + return nil, fmt.Errorf("neofs: Get not yet implemented - use existing neofs helpers") +} + +// Delete removes an object from NeoFS. +func (a *Adapter) Delete(ctx context.Context, id storage.ObjectID) error { + ctx, cancel := context.WithTimeout(ctx, a.timeout) + defer cancel() + + // TODO: Implement NeoFS object deletion + _ = ctx + + return fmt.Errorf("neofs: Delete not yet implemented") +} + +// Exists checks if an object exists in NeoFS. +func (a *Adapter) Exists(ctx context.Context, id storage.ObjectID) (bool, error) { + ctx, cancel := context.WithTimeout(ctx, a.timeout) + defer cancel() + + // TODO: Implement via ObjectHead + _ = ctx + + return false, fmt.Errorf("neofs: Exists not yet implemented") +} + +// List returns objects matching the given prefix. +func (a *Adapter) List(ctx context.Context, prefix string, opts storage.ListOptions) ([]storage.ObjectInfo, error) { + ctx, cancel := context.WithTimeout(ctx, a.timeout) + defer cancel() + + // TODO: Implement via ObjectSearch + _ = ctx + _ = prefix + + return nil, fmt.Errorf("neofs: List not yet implemented") +} + +// Head retrieves object metadata. +func (a *Adapter) Head(ctx context.Context, id storage.ObjectID) (storage.ObjectInfo, error) { + ctx, cancel := context.WithTimeout(ctx, a.timeout) + defer cancel() + + // TODO: Implement via ObjectHead + _ = ctx + + return storage.ObjectInfo{}, fmt.Errorf("neofs: Head not yet implemented") +} + +// Close releases NeoFS pool resources. +func (a *Adapter) Close() error { + if a.pool != nil { + a.pool.Close() + } + return nil +} + +// WrapExistingReader wraps data from the existing NeoFS helpers into the storage interface. +// This is a bridge to use existing pkg/services/helpers/neofs code with the new interface. +func WrapExistingReader(data []byte) io.ReadCloser { + return io.NopCloser(bytes.NewReader(data)) +} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 0b4812e..9667ce7 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -1,168 +1,168 @@ -package storage - -import ( - "context" - "io" - "time" -) - -// Provider defines the interface for sovereign storage backends. -// Implementations must ensure data sovereignty compliance for government deployments. -// -// Tutus supports multiple storage backends to accommodate national data -// residency requirements: -// - NeoFS: Decentralized storage (can be deployed as private network) -// - Local: Filesystem storage for single-node or NAS deployments -// - S3-compatible: AWS, Azure, GCP, or sovereign cloud providers -// - Custom: Nation-specific storage infrastructure -type Provider interface { - // Name returns the provider identifier (e.g., "neofs", "local", "s3") - Name() string - - // Put stores data and returns a unique identifier for retrieval. - // The identifier format is provider-specific. - Put(ctx context.Context, data io.Reader, opts PutOptions) (ObjectID, error) - - // Get retrieves data by its identifier. - // Returns ErrNotFound if the object doesn't exist. - Get(ctx context.Context, id ObjectID) (io.ReadCloser, error) - - // Delete removes an object from storage. - // Returns ErrNotFound if the object doesn't exist. - Delete(ctx context.Context, id ObjectID) error - - // Exists checks if an object exists in storage. - Exists(ctx context.Context, id ObjectID) (bool, error) - - // List returns object IDs matching the given prefix. - // Use an empty prefix to list all objects in the container. - List(ctx context.Context, prefix string, opts ListOptions) ([]ObjectInfo, error) - - // Head retrieves object metadata without downloading content. - Head(ctx context.Context, id ObjectID) (ObjectInfo, error) - - // Close releases any resources held by the provider. - Close() error -} - -// BlockStorage extends Provider with blockchain-specific operations. -type BlockStorage interface { - Provider - - // PutBlock stores a block with its index for efficient retrieval. - PutBlock(ctx context.Context, index uint32, data []byte) (ObjectID, error) - - // GetBlock retrieves a block by its index. - GetBlock(ctx context.Context, index uint32) ([]byte, error) - - // GetBlockRange retrieves multiple blocks efficiently. - GetBlockRange(ctx context.Context, start, end uint32) ([][]byte, error) - - // GetLatestBlockIndex returns the highest block index in storage. - GetLatestBlockIndex(ctx context.Context) (uint32, error) -} - -// StateStorage extends Provider with state snapshot operations. -type StateStorage interface { - Provider - - // PutState stores a state snapshot at a specific height. - PutState(ctx context.Context, height uint32, data io.Reader) (ObjectID, error) - - // GetState retrieves the state snapshot for a given height. - GetState(ctx context.Context, height uint32) (io.ReadCloser, error) - - // GetLatestState returns the most recent state snapshot. - GetLatestState(ctx context.Context) (height uint32, data io.ReadCloser, err error) -} - -// ObjectID uniquely identifies an object within a storage provider. -type ObjectID struct { - // Provider is the storage backend identifier - Provider string - // Container is the bucket/container/namespace - Container string - // ID is the unique object identifier within the container - ID string -} - -// String returns a URI-style representation of the object ID. -func (o ObjectID) String() string { - return o.Provider + "://" + o.Container + "/" + o.ID -} - -// ObjectInfo contains metadata about a stored object. -type ObjectInfo struct { - ID ObjectID - Size int64 - ContentType string - Created time.Time - Modified time.Time - Checksum string - Attributes map[string]string -} - -// PutOptions configures object storage behavior. -type PutOptions struct { - // ContentType specifies the MIME type of the content - ContentType string - // Attributes are custom key-value metadata - Attributes map[string]string - // Expiration sets automatic deletion time (zero means never) - Expiration time.Time - // Replicas specifies the minimum number of copies (provider-dependent) - Replicas int -} - -// ListOptions configures listing behavior. -type ListOptions struct { - // MaxResults limits the number of results (0 = no limit) - MaxResults int - // Cursor for pagination (provider-specific) - Cursor string - // IncludeMetadata fetches full ObjectInfo vs just IDs - IncludeMetadata bool -} - -// Registry manages available storage providers. -type Registry struct { - providers map[string]Provider -} - -// NewRegistry creates a new provider registry. -func NewRegistry() *Registry { - return &Registry{ - providers: make(map[string]Provider), - } -} - -// Register adds a storage provider to the registry. -func (r *Registry) Register(p Provider) { - r.providers[p.Name()] = p -} - -// Get returns a provider by name. -func (r *Registry) Get(name string) (Provider, bool) { - p, ok := r.providers[name] - return p, ok -} - -// List returns all registered provider names. -func (r *Registry) List() []string { - names := make([]string, 0, len(r.providers)) - for name := range r.providers { - names = append(names, name) - } - return names -} - -// Close closes all registered providers. -func (r *Registry) Close() error { - var lastErr error - for _, p := range r.providers { - if err := p.Close(); err != nil { - lastErr = err - } - } - return lastErr -} +package storage + +import ( + "context" + "io" + "time" +) + +// Provider defines the interface for sovereign storage backends. +// Implementations must ensure data sovereignty compliance for government deployments. +// +// Tutus supports multiple storage backends to accommodate national data +// residency requirements: +// - NeoFS: Decentralized storage (can be deployed as private network) +// - Local: Filesystem storage for single-node or NAS deployments +// - S3-compatible: AWS, Azure, GCP, or sovereign cloud providers +// - Custom: Nation-specific storage infrastructure +type Provider interface { + // Name returns the provider identifier (e.g., "neofs", "local", "s3") + Name() string + + // Put stores data and returns a unique identifier for retrieval. + // The identifier format is provider-specific. + Put(ctx context.Context, data io.Reader, opts PutOptions) (ObjectID, error) + + // Get retrieves data by its identifier. + // Returns ErrNotFound if the object doesn't exist. + Get(ctx context.Context, id ObjectID) (io.ReadCloser, error) + + // Delete removes an object from storage. + // Returns ErrNotFound if the object doesn't exist. + Delete(ctx context.Context, id ObjectID) error + + // Exists checks if an object exists in storage. + Exists(ctx context.Context, id ObjectID) (bool, error) + + // List returns object IDs matching the given prefix. + // Use an empty prefix to list all objects in the container. + List(ctx context.Context, prefix string, opts ListOptions) ([]ObjectInfo, error) + + // Head retrieves object metadata without downloading content. + Head(ctx context.Context, id ObjectID) (ObjectInfo, error) + + // Close releases any resources held by the provider. + Close() error +} + +// BlockStorage extends Provider with blockchain-specific operations. +type BlockStorage interface { + Provider + + // PutBlock stores a block with its index for efficient retrieval. + PutBlock(ctx context.Context, index uint32, data []byte) (ObjectID, error) + + // GetBlock retrieves a block by its index. + GetBlock(ctx context.Context, index uint32) ([]byte, error) + + // GetBlockRange retrieves multiple blocks efficiently. + GetBlockRange(ctx context.Context, start, end uint32) ([][]byte, error) + + // GetLatestBlockIndex returns the highest block index in storage. + GetLatestBlockIndex(ctx context.Context) (uint32, error) +} + +// StateStorage extends Provider with state snapshot operations. +type StateStorage interface { + Provider + + // PutState stores a state snapshot at a specific height. + PutState(ctx context.Context, height uint32, data io.Reader) (ObjectID, error) + + // GetState retrieves the state snapshot for a given height. + GetState(ctx context.Context, height uint32) (io.ReadCloser, error) + + // GetLatestState returns the most recent state snapshot. + GetLatestState(ctx context.Context) (height uint32, data io.ReadCloser, err error) +} + +// ObjectID uniquely identifies an object within a storage provider. +type ObjectID struct { + // Provider is the storage backend identifier + Provider string + // Container is the bucket/container/namespace + Container string + // ID is the unique object identifier within the container + ID string +} + +// String returns a URI-style representation of the object ID. +func (o ObjectID) String() string { + return o.Provider + "://" + o.Container + "/" + o.ID +} + +// ObjectInfo contains metadata about a stored object. +type ObjectInfo struct { + ID ObjectID + Size int64 + ContentType string + Created time.Time + Modified time.Time + Checksum string + Attributes map[string]string +} + +// PutOptions configures object storage behavior. +type PutOptions struct { + // ContentType specifies the MIME type of the content + ContentType string + // Attributes are custom key-value metadata + Attributes map[string]string + // Expiration sets automatic deletion time (zero means never) + Expiration time.Time + // Replicas specifies the minimum number of copies (provider-dependent) + Replicas int +} + +// ListOptions configures listing behavior. +type ListOptions struct { + // MaxResults limits the number of results (0 = no limit) + MaxResults int + // Cursor for pagination (provider-specific) + Cursor string + // IncludeMetadata fetches full ObjectInfo vs just IDs + IncludeMetadata bool +} + +// Registry manages available storage providers. +type Registry struct { + providers map[string]Provider +} + +// NewRegistry creates a new provider registry. +func NewRegistry() *Registry { + return &Registry{ + providers: make(map[string]Provider), + } +} + +// Register adds a storage provider to the registry. +func (r *Registry) Register(p Provider) { + r.providers[p.Name()] = p +} + +// Get returns a provider by name. +func (r *Registry) Get(name string) (Provider, bool) { + p, ok := r.providers[name] + return p, ok +} + +// List returns all registered provider names. +func (r *Registry) List() []string { + names := make([]string, 0, len(r.providers)) + for name := range r.providers { + names = append(names, name) + } + return names +} + +// Close closes all registered providers. +func (r *Registry) Close() error { + var lastErr error + for _, p := range r.providers { + if err := p.Close(); err != nil { + lastErr = err + } + } + return lastErr +} diff --git a/pkg/tutusrpc/errors.go b/pkg/tutusrpc/errors.go index ffea7d9..e8417fa 100644 --- a/pkg/tutusrpc/errors.go +++ b/pkg/tutusrpc/errors.go @@ -60,7 +60,7 @@ const ( ) // Codes for calls that use a wallet (-300...-304) can be returned by the C# RPC server only, -// see the https://github.com/tutus-one/tutus-chain/blob/master/docs/rpc.md#unsupported-methods. +// see the https://git.marketally.com/tutus-one/tutus-chain/blob/master/docs/rpc.md#unsupported-methods. const ( // ErrInsufficientFundsWalletCode is returned if transaction that sends some assets can't be created // because it fails. Can be returned only by the C# RPC server. diff --git a/pkg/tutusrpc/filters.go b/pkg/tutusrpc/filters.go index 2fe6f02..c7a01ae 100644 --- a/pkg/tutusrpc/filters.go +++ b/pkg/tutusrpc/filters.go @@ -5,12 +5,12 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/core/interop/runtime" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/runtime" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) // MaxNotificationFilterParametersCount is a reasonable filter's parameter limit diff --git a/pkg/tutusrpc/filters_test.go b/pkg/tutusrpc/filters_test.go index 438707c..cc7e8cb 100644 --- a/pkg/tutusrpc/filters_test.go +++ b/pkg/tutusrpc/filters_test.go @@ -3,9 +3,9 @@ package tutusrpc import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutusrpc/result/application_log.go b/pkg/tutusrpc/result/application_log.go index d01f676..c7b1335 100644 --- a/pkg/tutusrpc/result/application_log.go +++ b/pkg/tutusrpc/result/application_log.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // ApplicationLog represent the results of the script executions for a block or a transaction. diff --git a/pkg/tutusrpc/result/block.go b/pkg/tutusrpc/result/block.go index 1ad18fe..3b46122 100644 --- a/pkg/tutusrpc/result/block.go +++ b/pkg/tutusrpc/result/block.go @@ -4,8 +4,8 @@ import ( "encoding/json" "errors" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) type ( diff --git a/pkg/tutusrpc/result/block_header.go b/pkg/tutusrpc/result/block_header.go index 98a4cda..e36e82d 100644 --- a/pkg/tutusrpc/result/block_header.go +++ b/pkg/tutusrpc/result/block_header.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" - "github.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" ) type ( diff --git a/pkg/tutusrpc/result/block_notifications.go b/pkg/tutusrpc/result/block_notifications.go index d7441bc..f83977f 100644 --- a/pkg/tutusrpc/result/block_notifications.go +++ b/pkg/tutusrpc/result/block_notifications.go @@ -1,7 +1,7 @@ package result import ( - "github.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" ) // BlockNotifications represents notifications from a block organized by diff --git a/pkg/tutusrpc/result/invoke.go b/pkg/tutusrpc/result/invoke.go index 3b5d8d7..a670a68 100644 --- a/pkg/tutusrpc/result/invoke.go +++ b/pkg/tutusrpc/result/invoke.go @@ -5,11 +5,11 @@ import ( "fmt" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/storage/dboper" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/vm/invocations" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage/dboper" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/invocations" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Invoke represents a code invocation result and is used by several RPC calls diff --git a/pkg/tutusrpc/result/invoke_test.go b/pkg/tutusrpc/result/invoke_test.go index c4765ae..bddc485 100644 --- a/pkg/tutusrpc/result/invoke_test.go +++ b/pkg/tutusrpc/result/invoke_test.go @@ -8,12 +8,12 @@ import ( "testing" "github.com/google/uuid" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutusrpc/result/mempool_event.go b/pkg/tutusrpc/result/mempool_event.go index 7e6f866..6beb58b 100644 --- a/pkg/tutusrpc/result/mempool_event.go +++ b/pkg/tutusrpc/result/mempool_event.go @@ -1,8 +1,8 @@ package result import ( - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" ) // MempoolEvent represents a transaction event either added to diff --git a/pkg/tutusrpc/result/mpt.go b/pkg/tutusrpc/result/mpt.go index b172a82..e285edc 100644 --- a/pkg/tutusrpc/result/mpt.go +++ b/pkg/tutusrpc/result/mpt.go @@ -5,7 +5,7 @@ import ( "encoding/base64" "encoding/json" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" ) // StateHeight is a result of getstateheight RPC. diff --git a/pkg/tutusrpc/result/mpt_test.go b/pkg/tutusrpc/result/mpt_test.go index 338c86f..aecbc57 100644 --- a/pkg/tutusrpc/result/mpt_test.go +++ b/pkg/tutusrpc/result/mpt_test.go @@ -4,10 +4,10 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/mpt" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mpt" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutusrpc/result/notary_request_event.go b/pkg/tutusrpc/result/notary_request_event.go index f95c4ef..497efb3 100644 --- a/pkg/tutusrpc/result/notary_request_event.go +++ b/pkg/tutusrpc/result/notary_request_event.go @@ -1,8 +1,8 @@ package result import ( - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" ) // NotaryRequestEvent represents a P2PNotaryRequest event either added or removed diff --git a/pkg/tutusrpc/result/peers.go b/pkg/tutusrpc/result/peers.go index 77b014d..6f23d19 100644 --- a/pkg/tutusrpc/result/peers.go +++ b/pkg/tutusrpc/result/peers.go @@ -4,7 +4,7 @@ import ( "net" "strconv" - "github.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" ) type ( diff --git a/pkg/tutusrpc/result/peers_test.go b/pkg/tutusrpc/result/peers_test.go index 1f02dd7..8ebf1e8 100644 --- a/pkg/tutusrpc/result/peers_test.go +++ b/pkg/tutusrpc/result/peers_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/pkg/network" + "git.marketally.com/tutus-one/tutus-chain/pkg/network" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutusrpc/result/raw_mempool.go b/pkg/tutusrpc/result/raw_mempool.go index 890f0d8..eb82ae8 100644 --- a/pkg/tutusrpc/result/raw_mempool.go +++ b/pkg/tutusrpc/result/raw_mempool.go @@ -1,6 +1,6 @@ package result -import "github.com/tutus-one/tutus-chain/pkg/util" +import "git.marketally.com/tutus-one/tutus-chain/pkg/util" // RawMempool represents a result of getrawmempool RPC call. type RawMempool struct { diff --git a/pkg/tutusrpc/result/raw_notary_pool.go b/pkg/tutusrpc/result/raw_notary_pool.go index 3a86d3b..f8ca901 100644 --- a/pkg/tutusrpc/result/raw_notary_pool.go +++ b/pkg/tutusrpc/result/raw_notary_pool.go @@ -4,7 +4,7 @@ import ( "encoding/json" "strings" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // RawNotaryPool represents a result of `getrawnotarypool` RPC call. diff --git a/pkg/tutusrpc/result/relay_result.go b/pkg/tutusrpc/result/relay_result.go index b9d25d4..342f199 100644 --- a/pkg/tutusrpc/result/relay_result.go +++ b/pkg/tutusrpc/result/relay_result.go @@ -1,6 +1,6 @@ package result -import "github.com/tutus-one/tutus-chain/pkg/util" +import "git.marketally.com/tutus-one/tutus-chain/pkg/util" // RelayResult ia a result of `sendrawtransaction` or `submitblock` RPC calls. type RelayResult struct { diff --git a/pkg/tutusrpc/result/tokens.go b/pkg/tutusrpc/result/tokens.go index b920f72..a880f2e 100644 --- a/pkg/tutusrpc/result/tokens.go +++ b/pkg/tutusrpc/result/tokens.go @@ -1,7 +1,7 @@ package result import ( - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // NEP11Balances is a result for the getnep11balances RPC call. diff --git a/pkg/tutusrpc/result/tx_raw_output.go b/pkg/tutusrpc/result/tx_raw_output.go index 5586e29..2393327 100644 --- a/pkg/tutusrpc/result/tx_raw_output.go +++ b/pkg/tutusrpc/result/tx_raw_output.go @@ -4,8 +4,8 @@ import ( "encoding/json" "errors" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // TransactionOutputRaw is used as a wrapper to represents diff --git a/pkg/tutusrpc/result/unclaimed_gas.go b/pkg/tutusrpc/result/unclaimed_gas.go index 9e8bce7..768304c 100644 --- a/pkg/tutusrpc/result/unclaimed_gas.go +++ b/pkg/tutusrpc/result/unclaimed_gas.go @@ -5,8 +5,8 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // UnclaimedGas response wrapper. diff --git a/pkg/tutusrpc/result/validator.go b/pkg/tutusrpc/result/validator.go index bb87f95..0fa9d50 100644 --- a/pkg/tutusrpc/result/validator.go +++ b/pkg/tutusrpc/result/validator.go @@ -3,7 +3,7 @@ package result import ( "encoding/json" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" ) // Validator is used for the representation of consensus node data in the JSON-RPC diff --git a/pkg/tutusrpc/result/version.go b/pkg/tutusrpc/result/version.go index 8c18b14..a5bbbe6 100644 --- a/pkg/tutusrpc/result/version.go +++ b/pkg/tutusrpc/result/version.go @@ -5,10 +5,10 @@ import ( "fmt" "strings" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" ) type ( diff --git a/pkg/tutusrpc/result/version_test.go b/pkg/tutusrpc/result/version_test.go index 9a3bc72..01eee35 100644 --- a/pkg/tutusrpc/result/version_test.go +++ b/pkg/tutusrpc/result/version_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/fixedn" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/fixedn" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutusrpc/rpcevent/filter.go b/pkg/tutusrpc/rpcevent/filter.go index 5d9e0f4..43daa54 100644 --- a/pkg/tutusrpc/rpcevent/filter.go +++ b/pkg/tutusrpc/rpcevent/filter.go @@ -1,12 +1,12 @@ package rpcevent import ( - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) type ( diff --git a/pkg/tutusrpc/rpcevent/filter_test.go b/pkg/tutusrpc/rpcevent/filter_test.go index ff95d83..3c39c03 100644 --- a/pkg/tutusrpc/rpcevent/filter_test.go +++ b/pkg/tutusrpc/rpcevent/filter_test.go @@ -3,17 +3,17 @@ package rpcevent import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/mempoolevent" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc" - "github.com/tutus-one/tutus-chain/pkg/tutusrpc/result" - "github.com/tutus-one/tutus-chain/pkg/network/payload" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/mempoolevent" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutusrpc/result" + "git.marketally.com/tutus-one/tutus-chain/pkg/network/payload" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutusrpc/types.go b/pkg/tutusrpc/types.go index 687b33a..1bbee62 100644 --- a/pkg/tutusrpc/types.go +++ b/pkg/tutusrpc/types.go @@ -10,10 +10,10 @@ import ( "fmt" "strings" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/tutusrpc/types_test.go b/pkg/tutusrpc/types_test.go index 3b05562..8bf3a73 100644 --- a/pkg/tutusrpc/types_test.go +++ b/pkg/tutusrpc/types_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutustest/basic.go b/pkg/tutustest/basic.go index 2a1b449..ad8b981 100644 --- a/pkg/tutustest/basic.go +++ b/pkg/tutustest/basic.go @@ -7,22 +7,22 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/fee" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/fee" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutustest/chain/chain.go b/pkg/tutustest/chain/chain.go index 669ec3a..abd4947 100644 --- a/pkg/tutustest/chain/chain.go +++ b/pkg/tutustest/chain/chain.go @@ -6,15 +6,15 @@ import ( "testing" "time" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/interop" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/tutustest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zaptest" diff --git a/pkg/tutustest/chain/chain_test.go b/pkg/tutustest/chain/chain_test.go index 1a49af5..602c52c 100644 --- a/pkg/tutustest/chain/chain_test.go +++ b/pkg/tutustest/chain/chain_test.go @@ -3,7 +3,7 @@ package chain import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/tutustest" + "git.marketally.com/tutus-one/tutus-chain/pkg/tutustest" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutustest/client.go b/pkg/tutustest/client.go index 8343aed..a3dc5f3 100644 --- a/pkg/tutustest/client.go +++ b/pkg/tutustest/client.go @@ -3,13 +3,13 @@ package tutustest import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutustest/compile.go b/pkg/tutustest/compile.go index 206f46b..a31a968 100644 --- a/pkg/tutustest/compile.go +++ b/pkg/tutustest/compile.go @@ -6,13 +6,13 @@ import ( "os" "testing" - "github.com/tutus-one/tutus-chain/cli/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/cli/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutustest/compile_test.go b/pkg/tutustest/compile_test.go index 5b37ec2..60a0927 100644 --- a/pkg/tutustest/compile_test.go +++ b/pkg/tutustest/compile_test.go @@ -5,7 +5,7 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutustest/coverage.go b/pkg/tutustest/coverage.go index 71f2018..b942393 100644 --- a/pkg/tutustest/coverage.go +++ b/pkg/tutustest/coverage.go @@ -11,10 +11,10 @@ import ( "sync" "testing" - "github.com/tutus-one/tutus-chain/pkg/compiler" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/compiler" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) const ( diff --git a/pkg/tutustest/crosscontract.go b/pkg/tutustest/crosscontract.go index 1113831..21571b8 100644 --- a/pkg/tutustest/crosscontract.go +++ b/pkg/tutustest/crosscontract.go @@ -1,254 +1,254 @@ -package tutustest - -import ( - "testing" - - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/stretchr/testify/require" -) - -// CrossContractHelper provides utilities for testing cross-contract calls. -// Many Tutus contracts use GetCallingScriptHash() for authorization, which -// requires deploying a helper contract to properly test these paths. -type CrossContractHelper struct { - *Executor - t testing.TB -} - -// NewCrossContractHelper creates a helper for cross-contract testing. -func NewCrossContractHelper(t testing.TB, e *Executor) *CrossContractHelper { - return &CrossContractHelper{ - Executor: e, - t: t, - } -} - -// CallFromContract builds a script that calls a target contract method from -// another contract's context. This is useful for testing GetCallingScriptHash(). -// The script will: -// 1. Call the target contract with the specified method and args -// 2. Return the result -func (c *CrossContractHelper) CallFromContract( - caller util.Uint160, - target util.Uint160, - method string, - args ...any, -) []byte { - w := io.NewBufBinWriter() - emit.AppCall(w.BinWriter, target, method, callflag.All, args...) - require.NoError(c.t, w.Err) - return w.Bytes() -} - -// BuildProxyScript creates a script that acts as a proxy contract. -// It calls the target method and returns the result. -// Useful for testing authorization checks that require specific callers. -func (c *CrossContractHelper) BuildProxyScript( - target util.Uint160, - method string, - args ...any, -) []byte { - w := io.NewBufBinWriter() - emit.AppCall(w.BinWriter, target, method, callflag.All, args...) - require.NoError(c.t, w.Err) - return w.Bytes() -} - -// BuildMultiCallScript creates a script that calls multiple contracts in sequence. -// Each call's result is collected and returned as an array. -func (c *CrossContractHelper) BuildMultiCallScript(calls []ContractCall) []byte { - w := io.NewBufBinWriter() - - // Call each contract and collect results - for _, call := range calls { - emit.AppCall(w.BinWriter, call.Hash, call.Method, callflag.All, call.Args...) - } - - // Pack results into array - emit.Int(w.BinWriter, int64(len(calls))) - emit.Opcodes(w.BinWriter, opcode.PACK) - - require.NoError(c.t, w.Err) - return w.Bytes() -} - -// ContractCall represents a single contract invocation. -type ContractCall struct { - Hash util.Uint160 - Method string - Args []any -} - -// BuildConditionalScript creates a script that calls one method if condition -// is true, otherwise calls another method. -func (c *CrossContractHelper) BuildConditionalScript( - condition bool, - target util.Uint160, - trueMethod string, - trueArgs []any, - falseMethod string, - falseArgs []any, -) []byte { - w := io.NewBufBinWriter() - - if condition { - emit.AppCall(w.BinWriter, target, trueMethod, callflag.All, trueArgs...) - } else { - emit.AppCall(w.BinWriter, target, falseMethod, callflag.All, falseArgs...) - } - - require.NoError(c.t, w.Err) - return w.Bytes() -} - -// PrepareProxyCall creates a transaction that calls a contract method -// through a custom script, allowing the test to control the calling context. -func (c *CrossContractHelper) PrepareProxyCall( - signers []Signer, - script []byte, -) *transaction.Transaction { - tx := transaction.New(script, 0) - tx.Nonce = Nonce() - tx.ValidUntilBlock = c.Chain.BlockHeight() + 1 - - // Add signers - tx.Signers = make([]transaction.Signer, len(signers)) - for i, s := range signers { - tx.Signers[i] = transaction.Signer{ - Account: s.ScriptHash(), - Scopes: transaction.Global, - } - } - - // Calculate fees and sign - AddNetworkFee(c.t, c.Chain, tx, signers...) - require.NoError(c.t, c.Chain.PoolTx(tx)) - - return tx -} - -// InvokeViaProxy invokes a contract method through a proxy script -// and returns the result stack. -func (c *CrossContractHelper) InvokeViaProxy( - signers []Signer, - target util.Uint160, - method string, - args ...any, -) []stackitem.Item { - script := c.BuildProxyScript(target, method, args...) - tx := c.PrepareProxyCall(signers, script) - - c.AddNewBlock(c.t, tx) - aer, err := c.Chain.GetAppExecResults(tx.Hash(), 0) - require.NoError(c.t, err) - require.Equal(c.t, 1, len(aer)) - - return aer[0].Stack -} - -// TestContractBuilder helps build simple test contracts for cross-contract testing. -type TestContractBuilder struct { - t testing.TB - script []byte -} - -// NewTestContractBuilder creates a builder for test contracts. -func NewTestContractBuilder(t testing.TB) *TestContractBuilder { - return &TestContractBuilder{t: t} -} - -// WithCall adds a contract call to the test contract. -func (b *TestContractBuilder) WithCall(target util.Uint160, method string, args ...any) *TestContractBuilder { - w := io.NewBufBinWriter() - if len(b.script) > 0 { - w.BinWriter.WriteBytes(b.script) - } - emit.AppCall(w.BinWriter, target, method, callflag.All, args...) - require.NoError(b.t, w.Err) - b.script = w.Bytes() - return b -} - -// WithAssertion adds an assertion that the top of the stack is true. -func (b *TestContractBuilder) WithAssertion() *TestContractBuilder { - w := io.NewBufBinWriter() - if len(b.script) > 0 { - w.BinWriter.WriteBytes(b.script) - } - emit.Opcodes(w.BinWriter, opcode.ASSERT) - require.NoError(b.t, w.Err) - b.script = w.Bytes() - return b -} - -// WithDrop drops the top stack item. -func (b *TestContractBuilder) WithDrop() *TestContractBuilder { - w := io.NewBufBinWriter() - if len(b.script) > 0 { - w.BinWriter.WriteBytes(b.script) - } - emit.Opcodes(w.BinWriter, opcode.DROP) - require.NoError(b.t, w.Err) - b.script = w.Bytes() - return b -} - -// Build returns the final script. -func (b *TestContractBuilder) Build() []byte { - return b.script -} - -// AuthorizationTestHelper provides utilities for testing authorization patterns. -type AuthorizationTestHelper struct { - *CrossContractHelper -} - -// NewAuthorizationTestHelper creates a helper for testing authorization. -func NewAuthorizationTestHelper(t testing.TB, e *Executor) *AuthorizationTestHelper { - return &AuthorizationTestHelper{ - CrossContractHelper: NewCrossContractHelper(t, e), - } -} - -// TestCallerAuthorization tests that a method properly checks GetCallingScriptHash(). -// It calls the method from different contexts and verifies the expected behavior. -func (a *AuthorizationTestHelper) TestCallerAuthorization( - target util.Uint160, - method string, - args []any, - authorizedCallers []util.Uint160, - unauthorizedCallers []util.Uint160, - signers []Signer, -) { - // Test authorized callers succeed - for _, caller := range authorizedCallers { - script := a.CallFromContract(caller, target, method, args...) - tx := a.PrepareProxyCall(signers, script) - a.AddNewBlock(a.t, tx) - - aer, err := a.Chain.GetAppExecResults(tx.Hash(), 0) - require.NoError(a.t, err) - require.Equal(a.t, 1, len(aer)) - require.Equal(a.t, "HALT", aer[0].VMState.String(), - "authorized caller %s should succeed", caller.StringLE()) - } - - // Test unauthorized callers fail - for _, caller := range unauthorizedCallers { - script := a.CallFromContract(caller, target, method, args...) - tx := a.PrepareProxyCall(signers, script) - a.AddNewBlock(a.t, tx) - - aer, err := a.Chain.GetAppExecResults(tx.Hash(), 0) - require.NoError(a.t, err) - require.Equal(a.t, 1, len(aer)) - require.Equal(a.t, "FAULT", aer[0].VMState.String(), - "unauthorized caller %s should fail", caller.StringLE()) - } -} +package tutustest + +import ( + "testing" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +// CrossContractHelper provides utilities for testing cross-contract calls. +// Many Tutus contracts use GetCallingScriptHash() for authorization, which +// requires deploying a helper contract to properly test these paths. +type CrossContractHelper struct { + *Executor + t testing.TB +} + +// NewCrossContractHelper creates a helper for cross-contract testing. +func NewCrossContractHelper(t testing.TB, e *Executor) *CrossContractHelper { + return &CrossContractHelper{ + Executor: e, + t: t, + } +} + +// CallFromContract builds a script that calls a target contract method from +// another contract's context. This is useful for testing GetCallingScriptHash(). +// The script will: +// 1. Call the target contract with the specified method and args +// 2. Return the result +func (c *CrossContractHelper) CallFromContract( + caller util.Uint160, + target util.Uint160, + method string, + args ...any, +) []byte { + w := io.NewBufBinWriter() + emit.AppCall(w.BinWriter, target, method, callflag.All, args...) + require.NoError(c.t, w.Err) + return w.Bytes() +} + +// BuildProxyScript creates a script that acts as a proxy contract. +// It calls the target method and returns the result. +// Useful for testing authorization checks that require specific callers. +func (c *CrossContractHelper) BuildProxyScript( + target util.Uint160, + method string, + args ...any, +) []byte { + w := io.NewBufBinWriter() + emit.AppCall(w.BinWriter, target, method, callflag.All, args...) + require.NoError(c.t, w.Err) + return w.Bytes() +} + +// BuildMultiCallScript creates a script that calls multiple contracts in sequence. +// Each call's result is collected and returned as an array. +func (c *CrossContractHelper) BuildMultiCallScript(calls []ContractCall) []byte { + w := io.NewBufBinWriter() + + // Call each contract and collect results + for _, call := range calls { + emit.AppCall(w.BinWriter, call.Hash, call.Method, callflag.All, call.Args...) + } + + // Pack results into array + emit.Int(w.BinWriter, int64(len(calls))) + emit.Opcodes(w.BinWriter, opcode.PACK) + + require.NoError(c.t, w.Err) + return w.Bytes() +} + +// ContractCall represents a single contract invocation. +type ContractCall struct { + Hash util.Uint160 + Method string + Args []any +} + +// BuildConditionalScript creates a script that calls one method if condition +// is true, otherwise calls another method. +func (c *CrossContractHelper) BuildConditionalScript( + condition bool, + target util.Uint160, + trueMethod string, + trueArgs []any, + falseMethod string, + falseArgs []any, +) []byte { + w := io.NewBufBinWriter() + + if condition { + emit.AppCall(w.BinWriter, target, trueMethod, callflag.All, trueArgs...) + } else { + emit.AppCall(w.BinWriter, target, falseMethod, callflag.All, falseArgs...) + } + + require.NoError(c.t, w.Err) + return w.Bytes() +} + +// PrepareProxyCall creates a transaction that calls a contract method +// through a custom script, allowing the test to control the calling context. +func (c *CrossContractHelper) PrepareProxyCall( + signers []Signer, + script []byte, +) *transaction.Transaction { + tx := transaction.New(script, 0) + tx.Nonce = Nonce() + tx.ValidUntilBlock = c.Chain.BlockHeight() + 1 + + // Add signers + tx.Signers = make([]transaction.Signer, len(signers)) + for i, s := range signers { + tx.Signers[i] = transaction.Signer{ + Account: s.ScriptHash(), + Scopes: transaction.Global, + } + } + + // Calculate fees and sign + AddNetworkFee(c.t, c.Chain, tx, signers...) + require.NoError(c.t, c.Chain.PoolTx(tx)) + + return tx +} + +// InvokeViaProxy invokes a contract method through a proxy script +// and returns the result stack. +func (c *CrossContractHelper) InvokeViaProxy( + signers []Signer, + target util.Uint160, + method string, + args ...any, +) []stackitem.Item { + script := c.BuildProxyScript(target, method, args...) + tx := c.PrepareProxyCall(signers, script) + + c.AddNewBlock(c.t, tx) + aer, err := c.Chain.GetAppExecResults(tx.Hash(), 0) + require.NoError(c.t, err) + require.Equal(c.t, 1, len(aer)) + + return aer[0].Stack +} + +// TestContractBuilder helps build simple test contracts for cross-contract testing. +type TestContractBuilder struct { + t testing.TB + script []byte +} + +// NewTestContractBuilder creates a builder for test contracts. +func NewTestContractBuilder(t testing.TB) *TestContractBuilder { + return &TestContractBuilder{t: t} +} + +// WithCall adds a contract call to the test contract. +func (b *TestContractBuilder) WithCall(target util.Uint160, method string, args ...any) *TestContractBuilder { + w := io.NewBufBinWriter() + if len(b.script) > 0 { + w.BinWriter.WriteBytes(b.script) + } + emit.AppCall(w.BinWriter, target, method, callflag.All, args...) + require.NoError(b.t, w.Err) + b.script = w.Bytes() + return b +} + +// WithAssertion adds an assertion that the top of the stack is true. +func (b *TestContractBuilder) WithAssertion() *TestContractBuilder { + w := io.NewBufBinWriter() + if len(b.script) > 0 { + w.BinWriter.WriteBytes(b.script) + } + emit.Opcodes(w.BinWriter, opcode.ASSERT) + require.NoError(b.t, w.Err) + b.script = w.Bytes() + return b +} + +// WithDrop drops the top stack item. +func (b *TestContractBuilder) WithDrop() *TestContractBuilder { + w := io.NewBufBinWriter() + if len(b.script) > 0 { + w.BinWriter.WriteBytes(b.script) + } + emit.Opcodes(w.BinWriter, opcode.DROP) + require.NoError(b.t, w.Err) + b.script = w.Bytes() + return b +} + +// Build returns the final script. +func (b *TestContractBuilder) Build() []byte { + return b.script +} + +// AuthorizationTestHelper provides utilities for testing authorization patterns. +type AuthorizationTestHelper struct { + *CrossContractHelper +} + +// NewAuthorizationTestHelper creates a helper for testing authorization. +func NewAuthorizationTestHelper(t testing.TB, e *Executor) *AuthorizationTestHelper { + return &AuthorizationTestHelper{ + CrossContractHelper: NewCrossContractHelper(t, e), + } +} + +// TestCallerAuthorization tests that a method properly checks GetCallingScriptHash(). +// It calls the method from different contexts and verifies the expected behavior. +func (a *AuthorizationTestHelper) TestCallerAuthorization( + target util.Uint160, + method string, + args []any, + authorizedCallers []util.Uint160, + unauthorizedCallers []util.Uint160, + signers []Signer, +) { + // Test authorized callers succeed + for _, caller := range authorizedCallers { + script := a.CallFromContract(caller, target, method, args...) + tx := a.PrepareProxyCall(signers, script) + a.AddNewBlock(a.t, tx) + + aer, err := a.Chain.GetAppExecResults(tx.Hash(), 0) + require.NoError(a.t, err) + require.Equal(a.t, 1, len(aer)) + require.Equal(a.t, "HALT", aer[0].VMState.String(), + "authorized caller %s should succeed", caller.StringLE()) + } + + // Test unauthorized callers fail + for _, caller := range unauthorizedCallers { + script := a.CallFromContract(caller, target, method, args...) + tx := a.PrepareProxyCall(signers, script) + a.AddNewBlock(a.t, tx) + + aer, err := a.Chain.GetAppExecResults(tx.Hash(), 0) + require.NoError(a.t, err) + require.Equal(a.t, 1, len(aer)) + require.Equal(a.t, "FAULT", aer[0].VMState.String(), + "unauthorized caller %s should fail", caller.StringLE()) + } +} diff --git a/pkg/tutustest/doc.go b/pkg/tutustest/doc.go index 9f4ff0c..4e32eaf 100644 --- a/pkg/tutustest/doc.go +++ b/pkg/tutustest/doc.go @@ -20,7 +20,7 @@ It's recommended to have a separate folder/package for tests, because having them in the same package with the smart contract itself can lead to unexpected results if smart contract has any init() functions. If that's the case they will be compiled into the testing binary even when using package_test and their -execution can affect tests. See https://github.com/tutus-one/tutus-chain/issues/3120 for details. +execution can affect tests. See https://git.marketally.com/tutus-one/tutus-chain/issues/3120 for details. Test coverage for contracts is automatically enabled when `go test` is running with coverage enabled. When not desired, it can be disabled for any Executor by using diff --git a/pkg/tutustest/events.go b/pkg/tutustest/events.go index a60a204..c247798 100644 --- a/pkg/tutustest/events.go +++ b/pkg/tutustest/events.go @@ -1,295 +1,295 @@ -package tutustest - -import ( - "testing" - - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/stretchr/testify/require" -) - -// EventMatcher provides fluent event validation for contract tests. -type EventMatcher struct { - t testing.TB - events []state.NotificationEvent - contract util.Uint160 -} - -// NewEventMatcher creates a matcher for the given events. -func NewEventMatcher(t testing.TB, events []state.NotificationEvent) *EventMatcher { - return &EventMatcher{ - t: t, - events: events, - } -} - -// FromContract filters events to only those from the specified contract. -func (m *EventMatcher) FromContract(hash util.Uint160) *EventMatcher { - m.contract = hash - return m -} - -// HasEvent checks that at least one event with the given name exists. -func (m *EventMatcher) HasEvent(name string) *EventMatcher { - found := false - for _, e := range m.events { - if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { - continue - } - if e.Name == name { - found = true - break - } - } - require.True(m.t, found, "expected event %q not found", name) - return m -} - -// HasNoEvent checks that no event with the given name exists. -func (m *EventMatcher) HasNoEvent(name string) *EventMatcher { - for _, e := range m.events { - if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { - continue - } - require.NotEqual(m.t, name, e.Name, "unexpected event %q found", name) - } - return m -} - -// CountEvents returns the number of events with the given name. -func (m *EventMatcher) CountEvents(name string) int { - count := 0 - for _, e := range m.events { - if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { - continue - } - if e.Name == name { - count++ - } - } - return count -} - -// RequireEventCount asserts exactly N events with the given name. -func (m *EventMatcher) RequireEventCount(name string, count int) *EventMatcher { - actual := m.CountEvents(name) - require.Equal(m.t, count, actual, "expected %d %q events, got %d", count, name, actual) - return m -} - -// GetEvent returns the first event with the given name. -func (m *EventMatcher) GetEvent(name string) *state.NotificationEvent { - for _, e := range m.events { - if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { - continue - } - if e.Name == name { - return &e - } - } - return nil -} - -// GetEvents returns all events with the given name. -func (m *EventMatcher) GetEvents(name string) []state.NotificationEvent { - var result []state.NotificationEvent - for _, e := range m.events { - if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { - continue - } - if e.Name == name { - result = append(result, e) - } - } - return result -} - -// EventValidator provides detailed validation of a single event. -type EventValidator struct { - t testing.TB - event *state.NotificationEvent -} - -// ValidateEvent creates a validator for a specific event. -func (m *EventMatcher) ValidateEvent(name string) *EventValidator { - event := m.GetEvent(name) - require.NotNil(m.t, event, "event %q not found", name) - return &EventValidator{ - t: m.t, - event: event, - } -} - -// HasArgs checks that the event has the expected number of arguments. -func (v *EventValidator) HasArgs(count int) *EventValidator { - arr, ok := v.event.Item.Value().([]stackitem.Item) - require.True(v.t, ok, "event item is not an array") - require.Equal(v.t, count, len(arr), "expected %d args, got %d", count, len(arr)) - return v -} - -// ArgEquals checks that argument at index equals the expected value. -func (v *EventValidator) ArgEquals(index int, expected any) *EventValidator { - arr, ok := v.event.Item.Value().([]stackitem.Item) - require.True(v.t, ok, "event item is not an array") - require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) - - actual := arr[index] - exp := stackitem.Make(expected) - require.True(v.t, actual.Equals(exp), "arg[%d]: expected %v, got %v", index, expected, actual) - return v -} - -// ArgIsHash160 checks that argument at index is a valid Hash160. -func (v *EventValidator) ArgIsHash160(index int) *EventValidator { - arr, ok := v.event.Item.Value().([]stackitem.Item) - require.True(v.t, ok, "event item is not an array") - require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) - - bs, err := arr[index].TryBytes() - require.NoError(v.t, err, "arg[%d] is not bytes", index) - require.Equal(v.t, 20, len(bs), "arg[%d] is not Hash160 (len=%d)", index, len(bs)) - return v -} - -// ArgIsPositive checks that argument at index is a positive integer. -func (v *EventValidator) ArgIsPositive(index int) *EventValidator { - arr, ok := v.event.Item.Value().([]stackitem.Item) - require.True(v.t, ok, "event item is not an array") - require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) - - n, err := arr[index].TryInteger() - require.NoError(v.t, err, "arg[%d] is not integer", index) - require.Greater(v.t, n.Int64(), int64(0), "arg[%d] is not positive", index) - return v -} - -// ArgIsNonNegative checks that argument at index is a non-negative integer. -func (v *EventValidator) ArgIsNonNegative(index int) *EventValidator { - arr, ok := v.event.Item.Value().([]stackitem.Item) - require.True(v.t, ok, "event item is not an array") - require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) - - n, err := arr[index].TryInteger() - require.NoError(v.t, err, "arg[%d] is not integer", index) - require.GreaterOrEqual(v.t, n.Int64(), int64(0), "arg[%d] is negative", index) - return v -} - -// GetArg returns the argument at the given index. -func (v *EventValidator) GetArg(index int) stackitem.Item { - arr, ok := v.event.Item.Value().([]stackitem.Item) - require.True(v.t, ok, "event item is not an array") - require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) - return arr[index] -} - -// GetArgBytes returns the argument at the given index as bytes. -func (v *EventValidator) GetArgBytes(index int) []byte { - item := v.GetArg(index) - bs, err := item.TryBytes() - require.NoError(v.t, err) - return bs -} - -// GetArgInt returns the argument at the given index as int64. -func (v *EventValidator) GetArgInt(index int) int64 { - item := v.GetArg(index) - n, err := item.TryInteger() - require.NoError(v.t, err) - return n.Int64() -} - -// GetArgHash160 returns the argument at the given index as Hash160. -func (v *EventValidator) GetArgHash160(index int) util.Uint160 { - bs := v.GetArgBytes(index) - require.Equal(v.t, 20, len(bs)) - return util.Uint160(bs) -} - -// Common Tutus event names for convenience -const ( - // Vita events - EventVitaRegistered = "VitaRegistered" - EventVitaSuspended = "VitaSuspended" - EventVitaRevoked = "VitaRevoked" - - // VTS events - EventTransfer = "Transfer" - EventMint = "Mint" - EventBurn = "Burn" - - // Eligere events - EventProposalCreated = "ProposalCreated" - EventVoteCast = "VoteCast" - EventProposalPassed = "ProposalPassed" - EventProposalFailed = "ProposalFailed" - - // Lex events - EventLawEnacted = "LawEnacted" - EventLawRepealed = "LawRepealed" - EventRightRestricted = "RightRestricted" - EventRightRestored = "RightRestored" - - // RoleRegistry events - EventRoleGranted = "RoleGranted" - EventRoleRevoked = "RoleRevoked" - - // Scire events - EventEnrollment = "Enrollment" - EventCertification = "Certification" - - // Salus events - EventMedicalRecord = "MedicalRecord" - EventEmergencyAccess = "EmergencyAccess" - - // Federation events - EventVisitorRegistered = "VisitorRegistered" - EventAsylumGranted = "AsylumGranted" - EventCitizenNaturalized = "CitizenNaturalized" -) - -// TransferEventValidator is a specialized validator for Transfer events. -type TransferEventValidator struct { - *EventValidator -} - -// ValidateTransfer creates a validator specifically for Transfer events. -func (m *EventMatcher) ValidateTransfer() *TransferEventValidator { - return &TransferEventValidator{ - EventValidator: m.ValidateEvent(EventTransfer), - } -} - -// From checks the sender of the transfer. -func (v *TransferEventValidator) From(expected util.Uint160) *TransferEventValidator { - v.ArgEquals(0, expected.BytesBE()) - return v -} - -// To checks the recipient of the transfer. -func (v *TransferEventValidator) To(expected util.Uint160) *TransferEventValidator { - v.ArgEquals(1, expected.BytesBE()) - return v -} - -// Amount checks the transfer amount. -func (v *TransferEventValidator) Amount(expected int64) *TransferEventValidator { - v.ArgEquals(2, expected) - return v -} - -// IsMint checks that this is a mint (from is null). -func (v *TransferEventValidator) IsMint() *TransferEventValidator { - arr := v.event.Item.Value().([]stackitem.Item) - require.Equal(v.t, stackitem.AnyT, arr[0].Type(), "expected null sender for mint") - return v -} - -// IsBurn checks that this is a burn (to is null). -func (v *TransferEventValidator) IsBurn() *TransferEventValidator { - arr := v.event.Item.Value().([]stackitem.Item) - require.Equal(v.t, stackitem.AnyT, arr[1].Type(), "expected null recipient for burn") - return v -} +package tutustest + +import ( + "testing" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +// EventMatcher provides fluent event validation for contract tests. +type EventMatcher struct { + t testing.TB + events []state.NotificationEvent + contract util.Uint160 +} + +// NewEventMatcher creates a matcher for the given events. +func NewEventMatcher(t testing.TB, events []state.NotificationEvent) *EventMatcher { + return &EventMatcher{ + t: t, + events: events, + } +} + +// FromContract filters events to only those from the specified contract. +func (m *EventMatcher) FromContract(hash util.Uint160) *EventMatcher { + m.contract = hash + return m +} + +// HasEvent checks that at least one event with the given name exists. +func (m *EventMatcher) HasEvent(name string) *EventMatcher { + found := false + for _, e := range m.events { + if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { + continue + } + if e.Name == name { + found = true + break + } + } + require.True(m.t, found, "expected event %q not found", name) + return m +} + +// HasNoEvent checks that no event with the given name exists. +func (m *EventMatcher) HasNoEvent(name string) *EventMatcher { + for _, e := range m.events { + if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { + continue + } + require.NotEqual(m.t, name, e.Name, "unexpected event %q found", name) + } + return m +} + +// CountEvents returns the number of events with the given name. +func (m *EventMatcher) CountEvents(name string) int { + count := 0 + for _, e := range m.events { + if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { + continue + } + if e.Name == name { + count++ + } + } + return count +} + +// RequireEventCount asserts exactly N events with the given name. +func (m *EventMatcher) RequireEventCount(name string, count int) *EventMatcher { + actual := m.CountEvents(name) + require.Equal(m.t, count, actual, "expected %d %q events, got %d", count, name, actual) + return m +} + +// GetEvent returns the first event with the given name. +func (m *EventMatcher) GetEvent(name string) *state.NotificationEvent { + for _, e := range m.events { + if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { + continue + } + if e.Name == name { + return &e + } + } + return nil +} + +// GetEvents returns all events with the given name. +func (m *EventMatcher) GetEvents(name string) []state.NotificationEvent { + var result []state.NotificationEvent + for _, e := range m.events { + if m.contract != (util.Uint160{}) && e.ScriptHash != m.contract { + continue + } + if e.Name == name { + result = append(result, e) + } + } + return result +} + +// EventValidator provides detailed validation of a single event. +type EventValidator struct { + t testing.TB + event *state.NotificationEvent +} + +// ValidateEvent creates a validator for a specific event. +func (m *EventMatcher) ValidateEvent(name string) *EventValidator { + event := m.GetEvent(name) + require.NotNil(m.t, event, "event %q not found", name) + return &EventValidator{ + t: m.t, + event: event, + } +} + +// HasArgs checks that the event has the expected number of arguments. +func (v *EventValidator) HasArgs(count int) *EventValidator { + arr, ok := v.event.Item.Value().([]stackitem.Item) + require.True(v.t, ok, "event item is not an array") + require.Equal(v.t, count, len(arr), "expected %d args, got %d", count, len(arr)) + return v +} + +// ArgEquals checks that argument at index equals the expected value. +func (v *EventValidator) ArgEquals(index int, expected any) *EventValidator { + arr, ok := v.event.Item.Value().([]stackitem.Item) + require.True(v.t, ok, "event item is not an array") + require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) + + actual := arr[index] + exp := stackitem.Make(expected) + require.True(v.t, actual.Equals(exp), "arg[%d]: expected %v, got %v", index, expected, actual) + return v +} + +// ArgIsHash160 checks that argument at index is a valid Hash160. +func (v *EventValidator) ArgIsHash160(index int) *EventValidator { + arr, ok := v.event.Item.Value().([]stackitem.Item) + require.True(v.t, ok, "event item is not an array") + require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) + + bs, err := arr[index].TryBytes() + require.NoError(v.t, err, "arg[%d] is not bytes", index) + require.Equal(v.t, 20, len(bs), "arg[%d] is not Hash160 (len=%d)", index, len(bs)) + return v +} + +// ArgIsPositive checks that argument at index is a positive integer. +func (v *EventValidator) ArgIsPositive(index int) *EventValidator { + arr, ok := v.event.Item.Value().([]stackitem.Item) + require.True(v.t, ok, "event item is not an array") + require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) + + n, err := arr[index].TryInteger() + require.NoError(v.t, err, "arg[%d] is not integer", index) + require.Greater(v.t, n.Int64(), int64(0), "arg[%d] is not positive", index) + return v +} + +// ArgIsNonNegative checks that argument at index is a non-negative integer. +func (v *EventValidator) ArgIsNonNegative(index int) *EventValidator { + arr, ok := v.event.Item.Value().([]stackitem.Item) + require.True(v.t, ok, "event item is not an array") + require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) + + n, err := arr[index].TryInteger() + require.NoError(v.t, err, "arg[%d] is not integer", index) + require.GreaterOrEqual(v.t, n.Int64(), int64(0), "arg[%d] is negative", index) + return v +} + +// GetArg returns the argument at the given index. +func (v *EventValidator) GetArg(index int) stackitem.Item { + arr, ok := v.event.Item.Value().([]stackitem.Item) + require.True(v.t, ok, "event item is not an array") + require.Greater(v.t, len(arr), index, "arg index %d out of bounds", index) + return arr[index] +} + +// GetArgBytes returns the argument at the given index as bytes. +func (v *EventValidator) GetArgBytes(index int) []byte { + item := v.GetArg(index) + bs, err := item.TryBytes() + require.NoError(v.t, err) + return bs +} + +// GetArgInt returns the argument at the given index as int64. +func (v *EventValidator) GetArgInt(index int) int64 { + item := v.GetArg(index) + n, err := item.TryInteger() + require.NoError(v.t, err) + return n.Int64() +} + +// GetArgHash160 returns the argument at the given index as Hash160. +func (v *EventValidator) GetArgHash160(index int) util.Uint160 { + bs := v.GetArgBytes(index) + require.Equal(v.t, 20, len(bs)) + return util.Uint160(bs) +} + +// Common Tutus event names for convenience +const ( + // Vita events + EventVitaRegistered = "VitaRegistered" + EventVitaSuspended = "VitaSuspended" + EventVitaRevoked = "VitaRevoked" + + // VTS events + EventTransfer = "Transfer" + EventMint = "Mint" + EventBurn = "Burn" + + // Eligere events + EventProposalCreated = "ProposalCreated" + EventVoteCast = "VoteCast" + EventProposalPassed = "ProposalPassed" + EventProposalFailed = "ProposalFailed" + + // Lex events + EventLawEnacted = "LawEnacted" + EventLawRepealed = "LawRepealed" + EventRightRestricted = "RightRestricted" + EventRightRestored = "RightRestored" + + // RoleRegistry events + EventRoleGranted = "RoleGranted" + EventRoleRevoked = "RoleRevoked" + + // Scire events + EventEnrollment = "Enrollment" + EventCertification = "Certification" + + // Salus events + EventMedicalRecord = "MedicalRecord" + EventEmergencyAccess = "EmergencyAccess" + + // Federation events + EventVisitorRegistered = "VisitorRegistered" + EventAsylumGranted = "AsylumGranted" + EventCitizenNaturalized = "CitizenNaturalized" +) + +// TransferEventValidator is a specialized validator for Transfer events. +type TransferEventValidator struct { + *EventValidator +} + +// ValidateTransfer creates a validator specifically for Transfer events. +func (m *EventMatcher) ValidateTransfer() *TransferEventValidator { + return &TransferEventValidator{ + EventValidator: m.ValidateEvent(EventTransfer), + } +} + +// From checks the sender of the transfer. +func (v *TransferEventValidator) From(expected util.Uint160) *TransferEventValidator { + v.ArgEquals(0, expected.BytesBE()) + return v +} + +// To checks the recipient of the transfer. +func (v *TransferEventValidator) To(expected util.Uint160) *TransferEventValidator { + v.ArgEquals(1, expected.BytesBE()) + return v +} + +// Amount checks the transfer amount. +func (v *TransferEventValidator) Amount(expected int64) *TransferEventValidator { + v.ArgEquals(2, expected) + return v +} + +// IsMint checks that this is a mint (from is null). +func (v *TransferEventValidator) IsMint() *TransferEventValidator { + arr := v.event.Item.Value().([]stackitem.Item) + require.Equal(v.t, stackitem.AnyT, arr[0].Type(), "expected null sender for mint") + return v +} + +// IsBurn checks that this is a burn (to is null). +func (v *TransferEventValidator) IsBurn() *TransferEventValidator { + arr := v.event.Item.Value().([]stackitem.Item) + require.Equal(v.t, stackitem.AnyT, arr[1].Type(), "expected null recipient for burn") + return v +} diff --git a/pkg/tutustest/government.go b/pkg/tutustest/government.go index 3d53454..f0e320b 100644 --- a/pkg/tutustest/government.go +++ b/pkg/tutustest/government.go @@ -1,237 +1,237 @@ -package tutustest - -import ( - "math/big" - "testing" - "time" - - "github.com/stretchr/testify/require" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" -) - -// GovernmentHelper provides utilities for testing Tutus government contracts. -// It wraps common operations like registering citizens, granting roles, and -// setting up cross-contract test scenarios. -type GovernmentHelper struct { - *Executor - t testing.TB - - // Contract invokers for common government contracts - Vita *ContractInvoker - Lex *ContractInvoker - Eligere *ContractInvoker - Scire *ContractInvoker - Salus *ContractInvoker - VTS *ContractInvoker - Annos *ContractInvoker - Tribute *ContractInvoker - RoleReg *ContractInvoker - Treasury *ContractInvoker -} - -// NewGovernmentHelper creates a helper for testing government contracts. -// It initializes invokers for all major government contracts. -func NewGovernmentHelper(t testing.TB, e *Executor) *GovernmentHelper { - g := &GovernmentHelper{ - Executor: e, - t: t, - } - - // Initialize contract invokers with committee authority - g.Vita = e.CommitteeInvoker(e.NativeHash(t, nativenames.Vita)) - g.Lex = e.CommitteeInvoker(e.NativeHash(t, nativenames.Lex)) - g.Eligere = e.CommitteeInvoker(e.NativeHash(t, nativenames.Eligere)) - g.Scire = e.CommitteeInvoker(e.NativeHash(t, nativenames.Scire)) - g.Salus = e.CommitteeInvoker(e.NativeHash(t, nativenames.Salus)) - g.VTS = e.CommitteeInvoker(e.NativeHash(t, nativenames.VTS)) - g.Annos = e.CommitteeInvoker(e.NativeHash(t, nativenames.Annos)) - g.Tribute = e.CommitteeInvoker(e.NativeHash(t, nativenames.Tribute)) - g.RoleReg = e.CommitteeInvoker(e.NativeHash(t, nativenames.RoleRegistry)) - g.Treasury = e.CommitteeInvoker(e.NativeHash(t, nativenames.Treasury)) - - return g -} - -// Citizen represents a registered Vita holder for testing. -type Citizen struct { - Account SingleSigner - VitaID uint64 - BirthTime uint64 - Registered bool -} - -// RegisterCitizen registers a new Vita token for the given account. -// birthTimestamp is the citizen's birth date (Unix timestamp in milliseconds). -// Returns the Citizen with VitaID populated. -func (g *GovernmentHelper) RegisterCitizen(account SingleSigner, birthTimestamp uint64) *Citizen { - citizen := &Citizen{ - Account: account, - BirthTime: birthTimestamp, - } - - // Get current token count to predict the new VitaID - g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { - count, err := stack[0].TryInteger() - require.NoError(t, err) - citizen.VitaID = count.Uint64() - }, "totalSupply") - - // Register the Vita token - g.Vita.Invoke(g.t, true, "register", account.ScriptHash(), birthTimestamp) - citizen.Registered = true - - return citizen -} - -// RegisterAdultCitizen registers a citizen who is 25 years old (adult). -// Useful for tests requiring voting age or adult status. -func (g *GovernmentHelper) RegisterAdultCitizen(account SingleSigner) *Citizen { - // 25 years ago in milliseconds - birthTime := uint64(time.Now().AddDate(-25, 0, 0).UnixMilli()) - return g.RegisterCitizen(account, birthTime) -} - -// RegisterChildCitizen registers a citizen who is 10 years old (child). -// Useful for tests verifying age restrictions. -func (g *GovernmentHelper) RegisterChildCitizen(account SingleSigner) *Citizen { - // 10 years ago in milliseconds - birthTime := uint64(time.Now().AddDate(-10, 0, 0).UnixMilli()) - return g.RegisterCitizen(account, birthTime) -} - -// RegisterElderCitizen registers a citizen who is 70 years old (elder). -// Useful for tests involving retirement age. -func (g *GovernmentHelper) RegisterElderCitizen(account SingleSigner) *Citizen { - // 70 years ago in milliseconds - birthTime := uint64(time.Now().AddDate(-70, 0, 0).UnixMilli()) - return g.RegisterCitizen(account, birthTime) -} - -// NewCitizenAccount creates a new account and registers it as a citizen. -// Returns both the citizen and their account for signing transactions. -func (g *GovernmentHelper) NewCitizenAccount() *Citizen { - acc := g.Vita.NewAccount(g.t).(SingleSigner) - return g.RegisterAdultCitizen(acc) -} - -// VerifyVitaOwnership checks that the given account owns a Vita token. -func (g *GovernmentHelper) VerifyVitaOwnership(account util.Uint160) bool { - var hasVita bool - g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { - balance, err := stack[0].TryInteger() - require.NoError(t, err) - hasVita = balance.Cmp(big.NewInt(0)) > 0 - }, "balanceOf", account) - return hasVita -} - -// GetVitaID returns the Vita token ID for the given owner, or -1 if not found. -func (g *GovernmentHelper) GetVitaID(owner util.Uint160) int64 { - var vitaID int64 = -1 - g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { - if stack[0].Type() != stackitem.AnyT { - tokens := stack[0].Value().([]stackitem.Item) - if len(tokens) > 0 { - id, err := tokens[0].TryInteger() - require.NoError(t, err) - vitaID = id.Int64() - } - } - }, "tokensOf", owner) - return vitaID -} - -// SuspendVita suspends a citizen's Vita token (requires committee + Lex restriction). -func (g *GovernmentHelper) SuspendVita(vitaID uint64, reason string) { - // First, create a Lex liberty restriction (required for due process) - // This would normally require a judicial order - g.Vita.Invoke(g.t, true, "suspend", vitaID, reason) -} - -// TransferVTS transfers VTS tokens between accounts. -func (g *GovernmentHelper) TransferVTS(from, to SingleSigner, amount int64) util.Uint256 { - vtsInvoker := g.VTS.WithSigners(from) - return vtsInvoker.Invoke(g.t, true, "transfer", from.ScriptHash(), to.ScriptHash(), amount, nil) -} - -// GetVTSBalance returns the VTS balance for an account. -func (g *GovernmentHelper) GetVTSBalance(account util.Uint160) *big.Int { - var balance *big.Int - g.VTS.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { - var err error - balance, err = stack[0].TryInteger() - require.NoError(t, err) - }, "balanceOf", account) - return balance -} - -// MintVTS mints VTS tokens to an account (committee only). -func (g *GovernmentHelper) MintVTS(to util.Uint160, amount int64) { - g.VTS.Invoke(g.t, true, "mint", to, amount) -} - -// CreateProposal creates a new Eligere proposal. -// Returns the proposal ID. -func (g *GovernmentHelper) CreateProposal(proposer *Citizen, title, description string, category int64) uint64 { - eligereInvoker := g.Eligere.WithSigners(proposer.Account) - - var proposalID uint64 - eligereInvoker.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { - id, err := stack[0].TryInteger() - require.NoError(t, err) - proposalID = id.Uint64() - }, "createProposal", title, description, category) - - return proposalID -} - -// Vote casts a vote on a proposal. -func (g *GovernmentHelper) Vote(voter *Citizen, proposalID uint64, vote int64) { - eligereInvoker := g.Eligere.WithSigners(voter.Account) - eligereInvoker.Invoke(g.t, true, "vote", proposalID, vote) -} - -// PrepareVitaRegistration prepares a Vita registration transaction without executing. -// Useful for batching or testing transaction ordering. -func (g *GovernmentHelper) PrepareVitaRegistration(account SingleSigner, birthTimestamp uint64) *transaction.Transaction { - return g.Vita.PrepareInvoke(g.t, "register", account.ScriptHash(), birthTimestamp) -} - -// BatchRegisterCitizens registers multiple citizens in a single block. -func (g *GovernmentHelper) BatchRegisterCitizens(accounts []SingleSigner) []*Citizen { - citizens := make([]*Citizen, len(accounts)) - txs := make([]*transaction.Transaction, len(accounts)) - birthTime := uint64(time.Now().AddDate(-25, 0, 0).UnixMilli()) - - // Get starting VitaID - var startID uint64 - g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { - count, err := stack[0].TryInteger() - require.NoError(t, err) - startID = count.Uint64() - }, "totalSupply") - - // Prepare all transactions - for i, acc := range accounts { - citizens[i] = &Citizen{ - Account: acc, - BirthTime: birthTime, - VitaID: startID + uint64(i), - } - txs[i] = g.Vita.PrepareInvoke(g.t, "register", acc.ScriptHash(), birthTime) - } - - // Execute all in one block - g.Vita.AddNewBlock(g.t, txs...) - - // Verify all succeeded - for i, tx := range txs { - g.Vita.CheckHalt(g.t, tx.Hash(), stackitem.Make(true)) - citizens[i].Registered = true - } - - return citizens -} +package tutustest + +import ( + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/require" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" +) + +// GovernmentHelper provides utilities for testing Tutus government contracts. +// It wraps common operations like registering citizens, granting roles, and +// setting up cross-contract test scenarios. +type GovernmentHelper struct { + *Executor + t testing.TB + + // Contract invokers for common government contracts + Vita *ContractInvoker + Lex *ContractInvoker + Eligere *ContractInvoker + Scire *ContractInvoker + Salus *ContractInvoker + VTS *ContractInvoker + Annos *ContractInvoker + Tribute *ContractInvoker + RoleReg *ContractInvoker + Treasury *ContractInvoker +} + +// NewGovernmentHelper creates a helper for testing government contracts. +// It initializes invokers for all major government contracts. +func NewGovernmentHelper(t testing.TB, e *Executor) *GovernmentHelper { + g := &GovernmentHelper{ + Executor: e, + t: t, + } + + // Initialize contract invokers with committee authority + g.Vita = e.CommitteeInvoker(e.NativeHash(t, nativenames.Vita)) + g.Lex = e.CommitteeInvoker(e.NativeHash(t, nativenames.Lex)) + g.Eligere = e.CommitteeInvoker(e.NativeHash(t, nativenames.Eligere)) + g.Scire = e.CommitteeInvoker(e.NativeHash(t, nativenames.Scire)) + g.Salus = e.CommitteeInvoker(e.NativeHash(t, nativenames.Salus)) + g.VTS = e.CommitteeInvoker(e.NativeHash(t, nativenames.VTS)) + g.Annos = e.CommitteeInvoker(e.NativeHash(t, nativenames.Annos)) + g.Tribute = e.CommitteeInvoker(e.NativeHash(t, nativenames.Tribute)) + g.RoleReg = e.CommitteeInvoker(e.NativeHash(t, nativenames.RoleRegistry)) + g.Treasury = e.CommitteeInvoker(e.NativeHash(t, nativenames.Treasury)) + + return g +} + +// Citizen represents a registered Vita holder for testing. +type Citizen struct { + Account SingleSigner + VitaID uint64 + BirthTime uint64 + Registered bool +} + +// RegisterCitizen registers a new Vita token for the given account. +// birthTimestamp is the citizen's birth date (Unix timestamp in milliseconds). +// Returns the Citizen with VitaID populated. +func (g *GovernmentHelper) RegisterCitizen(account SingleSigner, birthTimestamp uint64) *Citizen { + citizen := &Citizen{ + Account: account, + BirthTime: birthTimestamp, + } + + // Get current token count to predict the new VitaID + g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { + count, err := stack[0].TryInteger() + require.NoError(t, err) + citizen.VitaID = count.Uint64() + }, "totalSupply") + + // Register the Vita token + g.Vita.Invoke(g.t, true, "register", account.ScriptHash(), birthTimestamp) + citizen.Registered = true + + return citizen +} + +// RegisterAdultCitizen registers a citizen who is 25 years old (adult). +// Useful for tests requiring voting age or adult status. +func (g *GovernmentHelper) RegisterAdultCitizen(account SingleSigner) *Citizen { + // 25 years ago in milliseconds + birthTime := uint64(time.Now().AddDate(-25, 0, 0).UnixMilli()) + return g.RegisterCitizen(account, birthTime) +} + +// RegisterChildCitizen registers a citizen who is 10 years old (child). +// Useful for tests verifying age restrictions. +func (g *GovernmentHelper) RegisterChildCitizen(account SingleSigner) *Citizen { + // 10 years ago in milliseconds + birthTime := uint64(time.Now().AddDate(-10, 0, 0).UnixMilli()) + return g.RegisterCitizen(account, birthTime) +} + +// RegisterElderCitizen registers a citizen who is 70 years old (elder). +// Useful for tests involving retirement age. +func (g *GovernmentHelper) RegisterElderCitizen(account SingleSigner) *Citizen { + // 70 years ago in milliseconds + birthTime := uint64(time.Now().AddDate(-70, 0, 0).UnixMilli()) + return g.RegisterCitizen(account, birthTime) +} + +// NewCitizenAccount creates a new account and registers it as a citizen. +// Returns both the citizen and their account for signing transactions. +func (g *GovernmentHelper) NewCitizenAccount() *Citizen { + acc := g.Vita.NewAccount(g.t).(SingleSigner) + return g.RegisterAdultCitizen(acc) +} + +// VerifyVitaOwnership checks that the given account owns a Vita token. +func (g *GovernmentHelper) VerifyVitaOwnership(account util.Uint160) bool { + var hasVita bool + g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { + balance, err := stack[0].TryInteger() + require.NoError(t, err) + hasVita = balance.Cmp(big.NewInt(0)) > 0 + }, "balanceOf", account) + return hasVita +} + +// GetVitaID returns the Vita token ID for the given owner, or -1 if not found. +func (g *GovernmentHelper) GetVitaID(owner util.Uint160) int64 { + var vitaID int64 = -1 + g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { + if stack[0].Type() != stackitem.AnyT { + tokens := stack[0].Value().([]stackitem.Item) + if len(tokens) > 0 { + id, err := tokens[0].TryInteger() + require.NoError(t, err) + vitaID = id.Int64() + } + } + }, "tokensOf", owner) + return vitaID +} + +// SuspendVita suspends a citizen's Vita token (requires committee + Lex restriction). +func (g *GovernmentHelper) SuspendVita(vitaID uint64, reason string) { + // First, create a Lex liberty restriction (required for due process) + // This would normally require a judicial order + g.Vita.Invoke(g.t, true, "suspend", vitaID, reason) +} + +// TransferVTS transfers VTS tokens between accounts. +func (g *GovernmentHelper) TransferVTS(from, to SingleSigner, amount int64) util.Uint256 { + vtsInvoker := g.VTS.WithSigners(from) + return vtsInvoker.Invoke(g.t, true, "transfer", from.ScriptHash(), to.ScriptHash(), amount, nil) +} + +// GetVTSBalance returns the VTS balance for an account. +func (g *GovernmentHelper) GetVTSBalance(account util.Uint160) *big.Int { + var balance *big.Int + g.VTS.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { + var err error + balance, err = stack[0].TryInteger() + require.NoError(t, err) + }, "balanceOf", account) + return balance +} + +// MintVTS mints VTS tokens to an account (committee only). +func (g *GovernmentHelper) MintVTS(to util.Uint160, amount int64) { + g.VTS.Invoke(g.t, true, "mint", to, amount) +} + +// CreateProposal creates a new Eligere proposal. +// Returns the proposal ID. +func (g *GovernmentHelper) CreateProposal(proposer *Citizen, title, description string, category int64) uint64 { + eligereInvoker := g.Eligere.WithSigners(proposer.Account) + + var proposalID uint64 + eligereInvoker.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { + id, err := stack[0].TryInteger() + require.NoError(t, err) + proposalID = id.Uint64() + }, "createProposal", title, description, category) + + return proposalID +} + +// Vote casts a vote on a proposal. +func (g *GovernmentHelper) Vote(voter *Citizen, proposalID uint64, vote int64) { + eligereInvoker := g.Eligere.WithSigners(voter.Account) + eligereInvoker.Invoke(g.t, true, "vote", proposalID, vote) +} + +// PrepareVitaRegistration prepares a Vita registration transaction without executing. +// Useful for batching or testing transaction ordering. +func (g *GovernmentHelper) PrepareVitaRegistration(account SingleSigner, birthTimestamp uint64) *transaction.Transaction { + return g.Vita.PrepareInvoke(g.t, "register", account.ScriptHash(), birthTimestamp) +} + +// BatchRegisterCitizens registers multiple citizens in a single block. +func (g *GovernmentHelper) BatchRegisterCitizens(accounts []SingleSigner) []*Citizen { + citizens := make([]*Citizen, len(accounts)) + txs := make([]*transaction.Transaction, len(accounts)) + birthTime := uint64(time.Now().AddDate(-25, 0, 0).UnixMilli()) + + // Get starting VitaID + var startID uint64 + g.Vita.InvokeAndCheck(g.t, func(t testing.TB, stack []stackitem.Item) { + count, err := stack[0].TryInteger() + require.NoError(t, err) + startID = count.Uint64() + }, "totalSupply") + + // Prepare all transactions + for i, acc := range accounts { + citizens[i] = &Citizen{ + Account: acc, + BirthTime: birthTime, + VitaID: startID + uint64(i), + } + txs[i] = g.Vita.PrepareInvoke(g.t, "register", acc.ScriptHash(), birthTime) + } + + // Execute all in one block + g.Vita.AddNewBlock(g.t, txs...) + + // Verify all succeeded + for i, tx := range txs { + g.Vita.CheckHalt(g.t, tx.Hash(), stackitem.Make(true)) + citizens[i].Registered = true + } + + return citizens +} diff --git a/pkg/tutustest/roles.go b/pkg/tutustest/roles.go index aec5ab8..8e7df17 100644 --- a/pkg/tutustest/roles.go +++ b/pkg/tutustest/roles.go @@ -1,226 +1,226 @@ -package tutustest - -import ( - "testing" - - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/stretchr/testify/require" -) - -// Standard role IDs matching role_registry.go and role_registry_domain.go -const ( - // Basic roles - RoleAdmin uint64 = 1 - RoleValidator uint64 = 2 - RoleNotary uint64 = 3 - RoleOracle uint64 = 4 - RoleStateRoot uint64 = 5 - RoleNeoFSInner uint64 = 6 - RoleNeoFSOuter uint64 = 7 - RoleP2PNotary uint64 = 8 - - // Government service roles - RoleEducator uint64 = 20 - RoleHealthProvider uint64 = 21 - RoleLifeCoach uint64 = 22 - RoleTributeAdmin uint64 = 23 - RoleOpusSupervisor uint64 = 24 - RolePalamAuditor uint64 = 25 - RolePalamJudge uint64 = 26 - RoleBridgeOperator uint64 = 27 - RoleInvestmentMgr uint64 = 28 - RoleJudge uint64 = 29 - - // Domain committee roles (CRIT-002) - RoleCommitteeLegal uint64 = 100 - RoleCommitteeHealth uint64 = 101 - RoleCommitteeEducation uint64 = 102 - RoleCommitteeEconomy uint64 = 103 - RoleCommitteeIdentity uint64 = 104 - RoleCommitteeGovernance uint64 = 105 -) - -// RoleHelper provides utilities for testing RoleRegistry operations. -type RoleHelper struct { - *Executor - t testing.TB - RoleReg *ContractInvoker - Designate *ContractInvoker -} - -// NewRoleHelper creates a helper for testing role-based operations. -func NewRoleHelper(t testing.TB, e *Executor) *RoleHelper { - return &RoleHelper{ - Executor: e, - t: t, - RoleReg: e.CommitteeInvoker(e.NativeHash(t, nativenames.RoleRegistry)), - Designate: e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation)), - } -} - -// GrantRole grants a role to an account (requires committee authority). -func (r *RoleHelper) GrantRole(account util.Uint160, roleID uint64) { - r.RoleReg.Invoke(r.t, true, "grantRole", account, roleID) -} - -// RevokeRole revokes a role from an account (requires committee authority). -func (r *RoleHelper) RevokeRole(account util.Uint160, roleID uint64) { - r.RoleReg.Invoke(r.t, true, "revokeRole", account, roleID) -} - -// HasRole checks if an account has a specific role. -func (r *RoleHelper) HasRole(account util.Uint160, roleID uint64) bool { - var hasRole bool - r.RoleReg.InvokeAndCheck(r.t, func(t testing.TB, stack []stackitem.Item) { - hasRole = stack[0].Value().(bool) - }, "hasRole", account, roleID) - return hasRole -} - -// RequireRole asserts that an account has a specific role. -func (r *RoleHelper) RequireRole(account util.Uint160, roleID uint64) { - require.True(r.t, r.HasRole(account, roleID), "account should have role %d", roleID) -} - -// RequireNoRole asserts that an account does not have a specific role. -func (r *RoleHelper) RequireNoRole(account util.Uint160, roleID uint64) { - require.False(r.t, r.HasRole(account, roleID), "account should not have role %d", roleID) -} - -// GetRoleMembers returns all members with a specific role. -func (r *RoleHelper) GetRoleMembers(roleID uint64) []util.Uint160 { - var members []util.Uint160 - r.RoleReg.InvokeAndCheck(r.t, func(t testing.TB, stack []stackitem.Item) { - arr, ok := stack[0].Value().([]stackitem.Item) - if !ok { - return - } - for _, item := range arr { - bs, err := item.TryBytes() - require.NoError(t, err) - members = append(members, util.Uint160(bs)) - } - }, "getRoleMembers", roleID) - return members -} - -// SetupEducator grants the Educator role to an account. -func (r *RoleHelper) SetupEducator(account util.Uint160) { - r.GrantRole(account, RoleEducator) -} - -// SetupHealthProvider grants the HealthProvider role to an account. -func (r *RoleHelper) SetupHealthProvider(account util.Uint160) { - r.GrantRole(account, RoleHealthProvider) -} - -// SetupJudge grants the Judge role to an account. -func (r *RoleHelper) SetupJudge(account util.Uint160) { - r.GrantRole(account, RoleJudge) -} - -// SetupLifeCoach grants the LifeCoach role to an account. -func (r *RoleHelper) SetupLifeCoach(account util.Uint160) { - r.GrantRole(account, RoleLifeCoach) -} - -// SetupTributeAdmin grants the TributeAdmin role to an account. -func (r *RoleHelper) SetupTributeAdmin(account util.Uint160) { - r.GrantRole(account, RoleTributeAdmin) -} - -// SetupOpusSupervisor grants the OpusSupervisor role to an account. -func (r *RoleHelper) SetupOpusSupervisor(account util.Uint160) { - r.GrantRole(account, RoleOpusSupervisor) -} - -// SetupPalamAuditor grants the PalamAuditor role to an account. -func (r *RoleHelper) SetupPalamAuditor(account util.Uint160) { - r.GrantRole(account, RolePalamAuditor) -} - -// SetupPalamJudge grants the PalamJudge role to an account. -func (r *RoleHelper) SetupPalamJudge(account util.Uint160) { - r.GrantRole(account, RolePalamJudge) -} - -// SetupBridgeOperator grants the BridgeOperator role to an account. -func (r *RoleHelper) SetupBridgeOperator(account util.Uint160) { - r.GrantRole(account, RoleBridgeOperator) -} - -// SetupInvestmentManager grants the InvestmentManager role to an account. -func (r *RoleHelper) SetupInvestmentManager(account util.Uint160) { - r.GrantRole(account, RoleInvestmentMgr) -} - -// SetupDomainCommittee grants a domain committee role to an account. -func (r *RoleHelper) SetupDomainCommittee(account util.Uint160, domain string) { - var roleID uint64 - switch domain { - case "legal": - roleID = RoleCommitteeLegal - case "health": - roleID = RoleCommitteeHealth - case "education": - roleID = RoleCommitteeEducation - case "economy": - roleID = RoleCommitteeEconomy - case "identity": - roleID = RoleCommitteeIdentity - case "governance": - roleID = RoleCommitteeGovernance - default: - require.Fail(r.t, "unknown domain: %s", domain) - } - r.GrantRole(account, roleID) -} - -// RoleScenario represents a test scenario with role assignments. -type RoleScenario struct { - helper *RoleHelper - accounts map[string]SingleSigner - roles map[string][]uint64 -} - -// NewRoleScenario creates a new role scenario builder. -func (r *RoleHelper) NewScenario() *RoleScenario { - return &RoleScenario{ - helper: r, - accounts: make(map[string]SingleSigner), - roles: make(map[string][]uint64), - } -} - -// WithAccount adds a named account to the scenario. -func (s *RoleScenario) WithAccount(name string, acc SingleSigner) *RoleScenario { - s.accounts[name] = acc - return s -} - -// WithRole assigns a role to a named account. -func (s *RoleScenario) WithRole(accountName string, roleID uint64) *RoleScenario { - s.roles[accountName] = append(s.roles[accountName], roleID) - return s -} - -// Setup executes all role assignments in the scenario. -func (s *RoleScenario) Setup() map[string]SingleSigner { - for name, roles := range s.roles { - acc, ok := s.accounts[name] - require.True(s.helper.t, ok, "account %s not found", name) - for _, roleID := range roles { - s.helper.GrantRole(acc.ScriptHash(), roleID) - } - } - return s.accounts -} - -// Get returns a named account from the scenario. -func (s *RoleScenario) Get(name string) SingleSigner { - acc, ok := s.accounts[name] - require.True(s.helper.t, ok, "account %s not found", name) - return acc -} +package tutustest + +import ( + "testing" + + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +// Standard role IDs matching role_registry.go and role_registry_domain.go +const ( + // Basic roles + RoleAdmin uint64 = 1 + RoleValidator uint64 = 2 + RoleNotary uint64 = 3 + RoleOracle uint64 = 4 + RoleStateRoot uint64 = 5 + RoleNeoFSInner uint64 = 6 + RoleNeoFSOuter uint64 = 7 + RoleP2PNotary uint64 = 8 + + // Government service roles + RoleEducator uint64 = 20 + RoleHealthProvider uint64 = 21 + RoleLifeCoach uint64 = 22 + RoleTributeAdmin uint64 = 23 + RoleOpusSupervisor uint64 = 24 + RolePalamAuditor uint64 = 25 + RolePalamJudge uint64 = 26 + RoleBridgeOperator uint64 = 27 + RoleInvestmentMgr uint64 = 28 + RoleJudge uint64 = 29 + + // Domain committee roles (CRIT-002) + RoleCommitteeLegal uint64 = 100 + RoleCommitteeHealth uint64 = 101 + RoleCommitteeEducation uint64 = 102 + RoleCommitteeEconomy uint64 = 103 + RoleCommitteeIdentity uint64 = 104 + RoleCommitteeGovernance uint64 = 105 +) + +// RoleHelper provides utilities for testing RoleRegistry operations. +type RoleHelper struct { + *Executor + t testing.TB + RoleReg *ContractInvoker + Designate *ContractInvoker +} + +// NewRoleHelper creates a helper for testing role-based operations. +func NewRoleHelper(t testing.TB, e *Executor) *RoleHelper { + return &RoleHelper{ + Executor: e, + t: t, + RoleReg: e.CommitteeInvoker(e.NativeHash(t, nativenames.RoleRegistry)), + Designate: e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation)), + } +} + +// GrantRole grants a role to an account (requires committee authority). +func (r *RoleHelper) GrantRole(account util.Uint160, roleID uint64) { + r.RoleReg.Invoke(r.t, true, "grantRole", account, roleID) +} + +// RevokeRole revokes a role from an account (requires committee authority). +func (r *RoleHelper) RevokeRole(account util.Uint160, roleID uint64) { + r.RoleReg.Invoke(r.t, true, "revokeRole", account, roleID) +} + +// HasRole checks if an account has a specific role. +func (r *RoleHelper) HasRole(account util.Uint160, roleID uint64) bool { + var hasRole bool + r.RoleReg.InvokeAndCheck(r.t, func(t testing.TB, stack []stackitem.Item) { + hasRole = stack[0].Value().(bool) + }, "hasRole", account, roleID) + return hasRole +} + +// RequireRole asserts that an account has a specific role. +func (r *RoleHelper) RequireRole(account util.Uint160, roleID uint64) { + require.True(r.t, r.HasRole(account, roleID), "account should have role %d", roleID) +} + +// RequireNoRole asserts that an account does not have a specific role. +func (r *RoleHelper) RequireNoRole(account util.Uint160, roleID uint64) { + require.False(r.t, r.HasRole(account, roleID), "account should not have role %d", roleID) +} + +// GetRoleMembers returns all members with a specific role. +func (r *RoleHelper) GetRoleMembers(roleID uint64) []util.Uint160 { + var members []util.Uint160 + r.RoleReg.InvokeAndCheck(r.t, func(t testing.TB, stack []stackitem.Item) { + arr, ok := stack[0].Value().([]stackitem.Item) + if !ok { + return + } + for _, item := range arr { + bs, err := item.TryBytes() + require.NoError(t, err) + members = append(members, util.Uint160(bs)) + } + }, "getRoleMembers", roleID) + return members +} + +// SetupEducator grants the Educator role to an account. +func (r *RoleHelper) SetupEducator(account util.Uint160) { + r.GrantRole(account, RoleEducator) +} + +// SetupHealthProvider grants the HealthProvider role to an account. +func (r *RoleHelper) SetupHealthProvider(account util.Uint160) { + r.GrantRole(account, RoleHealthProvider) +} + +// SetupJudge grants the Judge role to an account. +func (r *RoleHelper) SetupJudge(account util.Uint160) { + r.GrantRole(account, RoleJudge) +} + +// SetupLifeCoach grants the LifeCoach role to an account. +func (r *RoleHelper) SetupLifeCoach(account util.Uint160) { + r.GrantRole(account, RoleLifeCoach) +} + +// SetupTributeAdmin grants the TributeAdmin role to an account. +func (r *RoleHelper) SetupTributeAdmin(account util.Uint160) { + r.GrantRole(account, RoleTributeAdmin) +} + +// SetupOpusSupervisor grants the OpusSupervisor role to an account. +func (r *RoleHelper) SetupOpusSupervisor(account util.Uint160) { + r.GrantRole(account, RoleOpusSupervisor) +} + +// SetupPalamAuditor grants the PalamAuditor role to an account. +func (r *RoleHelper) SetupPalamAuditor(account util.Uint160) { + r.GrantRole(account, RolePalamAuditor) +} + +// SetupPalamJudge grants the PalamJudge role to an account. +func (r *RoleHelper) SetupPalamJudge(account util.Uint160) { + r.GrantRole(account, RolePalamJudge) +} + +// SetupBridgeOperator grants the BridgeOperator role to an account. +func (r *RoleHelper) SetupBridgeOperator(account util.Uint160) { + r.GrantRole(account, RoleBridgeOperator) +} + +// SetupInvestmentManager grants the InvestmentManager role to an account. +func (r *RoleHelper) SetupInvestmentManager(account util.Uint160) { + r.GrantRole(account, RoleInvestmentMgr) +} + +// SetupDomainCommittee grants a domain committee role to an account. +func (r *RoleHelper) SetupDomainCommittee(account util.Uint160, domain string) { + var roleID uint64 + switch domain { + case "legal": + roleID = RoleCommitteeLegal + case "health": + roleID = RoleCommitteeHealth + case "education": + roleID = RoleCommitteeEducation + case "economy": + roleID = RoleCommitteeEconomy + case "identity": + roleID = RoleCommitteeIdentity + case "governance": + roleID = RoleCommitteeGovernance + default: + require.Fail(r.t, "unknown domain: %s", domain) + } + r.GrantRole(account, roleID) +} + +// RoleScenario represents a test scenario with role assignments. +type RoleScenario struct { + helper *RoleHelper + accounts map[string]SingleSigner + roles map[string][]uint64 +} + +// NewRoleScenario creates a new role scenario builder. +func (r *RoleHelper) NewScenario() *RoleScenario { + return &RoleScenario{ + helper: r, + accounts: make(map[string]SingleSigner), + roles: make(map[string][]uint64), + } +} + +// WithAccount adds a named account to the scenario. +func (s *RoleScenario) WithAccount(name string, acc SingleSigner) *RoleScenario { + s.accounts[name] = acc + return s +} + +// WithRole assigns a role to a named account. +func (s *RoleScenario) WithRole(accountName string, roleID uint64) *RoleScenario { + s.roles[accountName] = append(s.roles[accountName], roleID) + return s +} + +// Setup executes all role assignments in the scenario. +func (s *RoleScenario) Setup() map[string]SingleSigner { + for name, roles := range s.roles { + acc, ok := s.accounts[name] + require.True(s.helper.t, ok, "account %s not found", name) + for _, roleID := range roles { + s.helper.GrantRole(acc.ScriptHash(), roleID) + } + } + return s.accounts +} + +// Get returns a named account from the scenario. +func (s *RoleScenario) Get(name string) SingleSigner { + acc, ok := s.accounts[name] + require.True(s.helper.t, ok, "account %s not found", name) + return acc +} diff --git a/pkg/tutustest/signer.go b/pkg/tutustest/signer.go index e2c8095..369fba0 100644 --- a/pkg/tutustest/signer.go +++ b/pkg/tutustest/signer.go @@ -6,17 +6,17 @@ import ( "slices" "testing" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/tutustest/signer_test.go b/pkg/tutustest/signer_test.go index 15ea9e6..0df3113 100644 --- a/pkg/tutustest/signer_test.go +++ b/pkg/tutustest/signer_test.go @@ -4,9 +4,9 @@ import ( "slices" "testing" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go index 23f1849..6665ae5 100644 --- a/pkg/util/uint160.go +++ b/pkg/util/uint160.go @@ -8,7 +8,7 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "gopkg.in/yaml.v3" ) diff --git a/pkg/util/uint160_test.go b/pkg/util/uint160_test.go index c4d9fb5..110f390 100644 --- a/pkg/util/uint160_test.go +++ b/pkg/util/uint160_test.go @@ -4,8 +4,8 @@ import ( "encoding/hex" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" diff --git a/pkg/util/uint256.go b/pkg/util/uint256.go index 6c0d850..dd92cb0 100644 --- a/pkg/util/uint256.go +++ b/pkg/util/uint256.go @@ -8,7 +8,7 @@ import ( "slices" "strings" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "gopkg.in/yaml.v3" ) diff --git a/pkg/util/uint256_test.go b/pkg/util/uint256_test.go index d6583e2..a07bf26 100644 --- a/pkg/util/uint256_test.go +++ b/pkg/util/uint256_test.go @@ -4,8 +4,8 @@ import ( "encoding/hex" "testing" - "github.com/tutus-one/tutus-chain/internal/testserdes" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/internal/testserdes" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" diff --git a/pkg/vm/bench_test.go b/pkg/vm/bench_test.go index fed2b63..5374521 100644 --- a/pkg/vm/bench_test.go +++ b/pkg/vm/bench_test.go @@ -5,7 +5,7 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/context.go b/pkg/vm/context.go index c5955be..e4633fd 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -5,14 +5,14 @@ import ( "errors" "slices" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/invocations" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/invocations" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // scriptContext is a part of the Context that is shared between multiple Contexts, diff --git a/pkg/vm/debug_test.go b/pkg/vm/debug_test.go index 52c6b7a..1e1a7ae 100644 --- a/pkg/vm/debug_test.go +++ b/pkg/vm/debug_test.go @@ -4,9 +4,9 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/emit/emit.go b/pkg/vm/emit/emit.go index e827452..1725b79 100644 --- a/pkg/vm/emit/emit.go +++ b/pkg/vm/emit/emit.go @@ -15,13 +15,13 @@ import ( "math/big" "math/bits" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Instruction emits a VM Instruction with data to the given buffer. diff --git a/pkg/vm/emit/emit_test.go b/pkg/vm/emit/emit_test.go index 1c87868..b6f16ee 100644 --- a/pkg/vm/emit/emit_test.go +++ b/pkg/vm/emit/emit_test.go @@ -8,12 +8,12 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/exception.go b/pkg/vm/exception.go index c6684c7..1596374 100644 --- a/pkg/vm/exception.go +++ b/pkg/vm/exception.go @@ -4,7 +4,7 @@ import ( "errors" "math/big" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // exceptionHandlingState represents state of the exception handling process. diff --git a/pkg/vm/fuzz_test.go b/pkg/vm/fuzz_test.go index f6a2ba0..9db8ef8 100644 --- a/pkg/vm/fuzz_test.go +++ b/pkg/vm/fuzz_test.go @@ -3,8 +3,8 @@ package vm import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/invocation_tree_test.go b/pkg/vm/invocation_tree_test.go index a39a831..8638025 100644 --- a/pkg/vm/invocation_tree_test.go +++ b/pkg/vm/invocation_tree_test.go @@ -3,9 +3,9 @@ package vm import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/invocations" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/invocations" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/invocations/invocation_tree.go b/pkg/vm/invocations/invocation_tree.go index 096f249..16f3daa 100644 --- a/pkg/vm/invocations/invocation_tree.go +++ b/pkg/vm/invocations/invocation_tree.go @@ -1,7 +1,7 @@ package invocations import ( - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Tree represents a tree with script hashes; when traversing it, diff --git a/pkg/vm/iterator_test.go b/pkg/vm/iterator_test.go index 3371b18..ddceb94 100644 --- a/pkg/vm/iterator_test.go +++ b/pkg/vm/iterator_test.go @@ -5,11 +5,11 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 5379a6e..25d877e 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -14,11 +14,11 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/opcodebench_test.go b/pkg/vm/opcodebench_test.go index ee61e86..863edc8 100644 --- a/pkg/vm/opcodebench_test.go +++ b/pkg/vm/opcodebench_test.go @@ -5,8 +5,8 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/ref_counter.go b/pkg/vm/ref_counter.go index b13985e..9f41b1a 100644 --- a/pkg/vm/ref_counter.go +++ b/pkg/vm/ref_counter.go @@ -1,7 +1,7 @@ package vm import ( - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // refCounter represents a reference counter for the VM. diff --git a/pkg/vm/ref_counter_test.go b/pkg/vm/ref_counter_test.go index 62556df..e56ce9d 100644 --- a/pkg/vm/ref_counter_test.go +++ b/pkg/vm/ref_counter_test.go @@ -3,8 +3,8 @@ package vm import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/slot.go b/pkg/vm/slot.go index 7f8cbb6..3726bd5 100644 --- a/pkg/vm/slot.go +++ b/pkg/vm/slot.go @@ -3,7 +3,7 @@ package vm import ( "encoding/json" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Slot is a fixed-size slice of stack items. diff --git a/pkg/vm/slot_test.go b/pkg/vm/slot_test.go index 000ac8d..5883d01 100644 --- a/pkg/vm/slot_test.go +++ b/pkg/vm/slot_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index e9ba874..dea0649 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -7,7 +7,7 @@ import ( "math/big" "slices" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" ) // Stack implementation for the neo-go virtual machine. The stack with its LIFO diff --git a/pkg/vm/stack_test.go b/pkg/vm/stack_test.go index e0cb77a..7ec9f1b 100644 --- a/pkg/vm/stack_test.go +++ b/pkg/vm/stack_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index c341af4..dbc88e3 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -12,9 +12,9 @@ import ( "slices" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/vm/stackitem/item_test.go b/pkg/vm/stackitem/item_test.go index 2495bc6..a24262e 100644 --- a/pkg/vm/stackitem/item_test.go +++ b/pkg/vm/stackitem/item_test.go @@ -4,8 +4,8 @@ import ( "math/big" "testing" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index 42e035c..e32ad2b 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -5,9 +5,9 @@ import ( "fmt" "math/big" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // MaxDeserialized is the maximum number one deserialized item can contain diff --git a/pkg/vm/stackitem/serialization_test.go b/pkg/vm/stackitem/serialization_test.go index ceb1d2a..1bd86e2 100644 --- a/pkg/vm/stackitem/serialization_test.go +++ b/pkg/vm/stackitem/serialization_test.go @@ -4,7 +4,7 @@ import ( "strconv" "testing" - "github.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" "github.com/stretchr/testify/require" ) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 0ec853a..43962f9 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -14,20 +14,20 @@ import ( "text/tabwriter" "unicode/utf8" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/invocations" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" - "github.com/tutus-one/tutus-chain/pkg/vm/vmstate" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/invocations" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/vmstate" ) type errorAtInstruct struct { @@ -1119,7 +1119,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } res.Exp(base, exponent, modulus) - // https://github.com/tutus-one/tutus-chain/issues/3612 + // https://git.marketally.com/tutus-one/tutus-chain/issues/3612 if base.Sign() < 0 && exponent.Bit(0) == 1 && res.Sign() != 0 { absModulus := new(big.Int).Abs(modulus) res.Sub(res, absModulus) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index e0cf3c7..b68e493 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -13,17 +13,17 @@ import ( "strings" "testing" - "github.com/tutus-one/tutus-chain/internal/random" - "github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" - "github.com/tutus-one/tutus-chain/pkg/encoding/bigint" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" - "github.com/tutus-one/tutus-chain/pkg/vm/stackitem" + "git.marketally.com/tutus-one/tutus-chain/internal/random" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/interop/interopnames" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/bigint" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/callflag" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/wallet/account.go b/pkg/wallet/account.go index cb11892..d7dd31c 100644 --- a/pkg/wallet/account.go +++ b/pkg/wallet/account.go @@ -5,16 +5,16 @@ import ( "fmt" "slices" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/util" - "github.com/tutus-one/tutus-chain/pkg/vm/emit" - "github.com/tutus-one/tutus-chain/pkg/vm/opcode" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/emit" + "git.marketally.com/tutus-one/tutus-chain/pkg/vm/opcode" ) // Account represents a NEO account. It holds the private and the public key diff --git a/pkg/wallet/account_test.go b/pkg/wallet/account_test.go index 7e34c8e..776e0f7 100644 --- a/pkg/wallet/account_test.go +++ b/pkg/wallet/account_test.go @@ -4,12 +4,12 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/internal/keytestcases" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/crypto/hash" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/internal/keytestcases" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/hash" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/wallet/regenerate_test.go b/pkg/wallet/regenerate_test.go index 9575aa2..a5dbc30 100644 --- a/pkg/wallet/regenerate_test.go +++ b/pkg/wallet/regenerate_test.go @@ -7,11 +7,11 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/pkg/core/state" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/nef" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/state" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/nef" "github.com/stretchr/testify/require" ) diff --git a/pkg/wallet/token.go b/pkg/wallet/token.go index 201a7aa..fafd607 100644 --- a/pkg/wallet/token.go +++ b/pkg/wallet/token.go @@ -1,8 +1,8 @@ package wallet import ( - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) // Token represents an imported token contract. diff --git a/pkg/wallet/token_test.go b/pkg/wallet/token_test.go index 0e98258..47c9d58 100644 --- a/pkg/wallet/token_test.go +++ b/pkg/wallet/token_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index 211ffed..0c28dec 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -8,10 +8,10 @@ import ( "io" "os" - "github.com/tutus-one/tutus-chain/pkg/crypto/keys" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/crypto/keys" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/scparser" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" ) const ( diff --git a/pkg/wallet/wallet_test.go b/pkg/wallet/wallet_test.go index f5631ad..64484fd 100644 --- a/pkg/wallet/wallet_test.go +++ b/pkg/wallet/wallet_test.go @@ -6,9 +6,9 @@ import ( "path/filepath" "testing" - "github.com/tutus-one/tutus-chain/pkg/encoding/address" - "github.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/encoding/address" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/manifest" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/scripts/compare-dumps/compare-dumps_test.go b/scripts/compare-dumps/compare-dumps_test.go index 2070771..6f8641d 100644 --- a/scripts/compare-dumps/compare-dumps_test.go +++ b/scripts/compare-dumps/compare-dumps_test.go @@ -3,9 +3,9 @@ package main import ( "testing" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/native/nativenames" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native/nativenames" "github.com/stretchr/testify/require" ) diff --git a/scripts/compare-states/compare-states.go b/scripts/compare-states/compare-states.go index 10a7932..f1921c4 100644 --- a/scripts/compare-states/compare-states.go +++ b/scripts/compare-states/compare-states.go @@ -7,8 +7,8 @@ import ( "os" "github.com/davecgh/go-spew/spew" - "github.com/tutus-one/tutus-chain/pkg/rpcclient" - "github.com/tutus-one/tutus-chain/pkg/util" + "git.marketally.com/tutus-one/tutus-chain/pkg/rpcclient" + "git.marketally.com/tutus-one/tutus-chain/pkg/util" "github.com/pmezard/go-difflib/difflib" "github.com/urfave/cli/v2" ) diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go index 3111792..a6334f7 100644 --- a/scripts/gendump/main.go +++ b/scripts/gendump/main.go @@ -9,18 +9,18 @@ import ( "strings" "time" - "github.com/tutus-one/tutus-chain/internal/testchain" - "github.com/tutus-one/tutus-chain/pkg/config" - "github.com/tutus-one/tutus-chain/pkg/config/netmode" - "github.com/tutus-one/tutus-chain/pkg/core" - "github.com/tutus-one/tutus-chain/pkg/core/block" - "github.com/tutus-one/tutus-chain/pkg/core/chaindump" - "github.com/tutus-one/tutus-chain/pkg/core/native" - "github.com/tutus-one/tutus-chain/pkg/core/storage" - "github.com/tutus-one/tutus-chain/pkg/core/transaction" - "github.com/tutus-one/tutus-chain/pkg/io" - "github.com/tutus-one/tutus-chain/pkg/smartcontract" - "github.com/tutus-one/tutus-chain/pkg/wallet" + "git.marketally.com/tutus-one/tutus-chain/internal/testchain" + "git.marketally.com/tutus-one/tutus-chain/pkg/config" + "git.marketally.com/tutus-one/tutus-chain/pkg/config/netmode" + "git.marketally.com/tutus-one/tutus-chain/pkg/core" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/block" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/chaindump" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/native" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/storage" + "git.marketally.com/tutus-one/tutus-chain/pkg/core/transaction" + "git.marketally.com/tutus-one/tutus-chain/pkg/io" + "git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract" + "git.marketally.com/tutus-one/tutus-chain/pkg/wallet" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -49,7 +49,7 @@ func main() { const contract = ` package contract - import "github.com/tutus-one/tutus-chain/pkg/interop/storage" + import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/storage" var ctx = storage.GetContext() func Put(key, value []byte) { storage.Put(ctx, key, value) diff --git a/scripts/go.mod b/scripts/go.mod index bcc8911..4ef529c 100644 --- a/scripts/go.mod +++ b/scripts/go.mod @@ -1,12 +1,12 @@ -module github.com/tutus-one/tutus-chain/scripts +module git.marketally.com/tutus-one/tutus-chain/scripts go 1.24.0 -replace github.com/tutus-one/tutus-chain => ../ +replace git.marketally.com/tutus-one/tutus-chain => ../ require ( github.com/davecgh/go-spew v1.1.1 - github.com/tutus-one/tutus-chain v0.113.0 + git.marketally.com/tutus-one/tutus-chain v0.113.0 github.com/pmezard/go-difflib v1.0.0 github.com/stretchr/testify v1.11.1 github.com/urfave/cli/v2 v2.27.7 diff --git a/scripts/go.sum b/scripts/go.sum index eeab1a4..84553ee 100644 --- a/scripts/go.sum +++ b/scripts/go.sum @@ -128,8 +128,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20250911084817-6fb4472993d1 h1:U3wvY github.com/nspcc-dev/go-ordered-json v0.0.0-20250911084817-6fb4472993d1/go.mod h1:CHwf1nwquA6ecSfxmNF0YuemOPHAnRGoLuZUv/WPjeY= github.com/nspcc-dev/hrw/v2 v2.0.4 h1:o3Zh/2aF+IgGpvt414f46Ya20WG9u9vWxVd16ErFI8w= github.com/nspcc-dev/hrw/v2 v2.0.4/go.mod h1:dUjOx27zTTvoPmT5EG25vSSWL2tKS7ndAa2TPTiZwFo= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251112080609-3c8e29c66609 h1:9jH0IXFw8rjBgBVWSJbWeEHf7XDjANBnmEas49rdAH8= -github.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251112080609-3c8e29c66609/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251112080609-3c8e29c66609 h1:9jH0IXFw8rjBgBVWSJbWeEHf7XDjANBnmEas49rdAH8= +git.marketally.com/tutus-one/tutus-chain/pkg/interop v0.0.0-20251112080609-3c8e29c66609/go.mod h1:X2spkE8hK/l08CYulOF19fpK5n3p2xO0L1GnJFIywQg= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16 h1:0zOMTcstqKn/j/frqOHign9rtaoIPdxhJF069LRlCJM= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16/go.mod h1:u3XqGGbGQiPBAonFBJ6Dm9KbbDTvcGjEiRhndppzgHw= github.com/nspcc-dev/rfc6979 v0.2.4 h1:NBgsdCjhLpEPJZqmC9rciMZDcSY297po2smeaRjw57k= diff --git a/scripts/register-test-vita.sh b/scripts/register-test-vita.sh new file mode 100644 index 0000000..5df146f --- /dev/null +++ b/scripts/register-test-vita.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# Register a test Vita on the local blockchain +# Usage: ./register-test-vita.sh
+# +# Example: ./register-test-vita.sh Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +CHAIN_DIR="$(dirname "$SCRIPT_DIR")" +TUTUS_CLI="$CHAIN_DIR/bin/tutus.exe" +WALLET="$CHAIN_DIR/.docker/wallets/wallet1.json" +WALLET_CONFIG="/tmp/wallet-config.yaml" +RPC_ENDPOINT="${RPC_ENDPOINT:-http://localhost:10332}" + +# Vita contract hash +VITA_HASH="0xde437f043fdfb8f9c8241acb82d0791dd4b3217d" + +if [ -z "$1" ]; then + echo "Usage: $0
" + echo "Example: $0 Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn" + exit 1 +fi + +ADDRESS="$1" + +# Convert address to script hash +echo "Converting address to script hash..." +SCRIPT_HASH=$("$TUTUS_CLI" util convert "$ADDRESS" 2>/dev/null | grep "Address to LE ScriptHash" | awk '{print $NF}') + +if [ -z "$SCRIPT_HASH" ]; then + echo "Error: Could not convert address to script hash" + exit 1 +fi + +echo "Address: $ADDRESS" +echo "Script Hash: $SCRIPT_HASH" + +# Generate test data +PERSON_HASH=$(echo -n "test-person-$ADDRESS-$(date +%Y%m%d)" | sha256sum | cut -d' ' -f1) +RECOVERY_HASH="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +BIRTH_TS="631152000000" # Jan 1, 1990 in ms + +# Create wallet config +cat > "$WALLET_CONFIG" << EOF +Path: $WALLET +Password: one +EOF + +echo "" +echo "Registering Vita on blockchain..." + +# Invoke the contract +TX_HASH=$("$TUTUS_CLI" contract invokefunction \ + -r "$RPC_ENDPOINT" \ + --wallet-config "$WALLET_CONFIG" \ + -a Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn \ + --force \ + --await \ + "$VITA_HASH" \ + register \ + "Hash160:$SCRIPT_HASH" \ + "ByteArray:$PERSON_HASH" \ + "Boolean:false" \ + "ByteArray:$RECOVERY_HASH" \ + "Integer:$BIRTH_TS" \ + -- "$SCRIPT_HASH:CalledByEntry" 2>&1 | head -1) + +echo "" +echo "=== VITA REGISTERED ===" +echo "Transaction: $TX_HASH" +echo "Address: $ADDRESS" +echo "" +echo "Verify with:" +echo " curl -s -X POST $RPC_ENDPOINT -H 'Content-Type: application/json' \\" +echo " -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"invokefunction\",\"params\":[\"$VITA_HASH\",\"exists\",[{\"type\":\"Hash160\",\"value\":\"$SCRIPT_HASH\"}]]}'" + +# Cleanup +rm -f "$WALLET_CONFIG"