Compare commits

...

103 commits

Author SHA1 Message Date
Roman Khimov
24a6d842b5
Merge pull request #3837 from nspcc-dev/fix-native-init 2025-03-12 13:35:37 +03:00
Anna Shaleva
ee56f73606 core: fix bug in (bc *Blockchain).isHardforkEnabled
This code was never invoked since we had no native contract enabled
starting from some hardfork, Notary is the first one. And luckily, we
have plenty of tests that fail due to this bug.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-12 13:29:10 +03:00
Anna Shaleva
210059fff8 native: fix native deploy process
It doesn't work for contracts enabled starting from non-nil hardfork:
```
--- FAIL: TestStateroot_GetLatestStateHeight (0.00s)
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	initial gas supply is not set or wrong, setting default value	{"InitialGASSupply": "52000000"}
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	mempool size is not set or wrong, setting default value	{"MemPoolSize": 50000}
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	P2PNotaryRequestPayloadPool size is not set or wrong, setting default value	{"P2PNotaryRequestPayloadPoolSize": 1000}
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	MaxBlockSize is not set or wrong, setting default value	{"MaxBlockSize": 262144}
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	MaxBlockSystemFee is not set or wrong, setting default value	{"MaxBlockSystemFee": 900000000000}
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	MaxTransactionsPerBlock is not set or wrong, using default value	{"MaxTransactionsPerBlock": 512}
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	MaxValidUntilBlockIncrement is not set or wrong, using default value	{"MaxValidUntilBlockIncrement": 86400}
    logger.go:146: 2024-06-04T17:08:35.263+0300	INFO	Hardforks are not set, using default value
    logger.go:146: 2024-06-04T17:08:35.266+0300	INFO	no storage version found! creating genesis block
    chain.go:227:
        	Error Trace:	/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/neotest/chain/chain.go:227
        	            				/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/neotest/chain/chain.go:217
        	            				/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/services/stateroot/service_test.go:319
        	Error:      	Received unexpected error:
        	            	onPersist failed: VM has failed: at instruction 0 (SYSCALL): native contract descriptor cache is not initialized: contract c1e14f19c3e60d0b9244d06dd7ba9b113135ec3b, hardfork Default
        	Test:       	TestStateroot_GetLatestStateHeight
FAIL
coverage: 28.6% of statements

```

It happens because ActiveIn hardfork wasn't taken into account during
`latestHF` computation. This commit also removes the reusage of
`activeIn` variable in deploy procedure, it's misleading and not
necessary startign from #3444.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-12 13:29:10 +03:00
Anna Shaleva
4a510637be interop: improve error message on native cache initialization error
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-12 13:29:09 +03:00
Anna Shaleva
2fd51cf6b1 native: fix error message on native cache initialization
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-12 13:27:22 +03:00
Anna Shaleva
7d72b6538a
Merge pull request #3828 from fyfyrchik/update-feeds
rpcsrv: allow to configure subscription limit
2025-03-07 16:42:35 +03:00
Evgenii Stratonikov
70b4d005e3
rpcsrv: allow to configure subscription limit
Close #3823

Signed-off-by: Evgenii Stratonikov <fyfyrchik@runbox.com>
2025-03-07 14:34:40 +03:00
Anna Shaleva
2881961421
Merge pull request #3808 from nspcc-dev/upload-state
cli: add `upload-state ` command
2025-03-07 14:05:23 +03:00
Ekaterina Pavlova
5f80a142b0 cli: add upload-state command
Close #3782

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-03-07 18:13:54 +08:00
Roman Khimov
d411337648
Merge pull request #3640 from nspcc-dev/fix-ntf
core: restrict the number of allowed SC notifications
2025-03-07 12:40:13 +03:00
Anna Shaleva
2c8bd056fa core: restrict the number of allowed SC notifications
Close #3490.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-07 12:28:25 +03:00
Ekaterina Pavlova
9c0274850a cli: refactor NeoFS related flags and functions
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-03-07 06:43:43 +08:00
Roman Khimov
94660f6333
Merge pull request #3832 from nspcc-dev/extend-billet-doc 2025-03-06 17:07:45 +03:00
Anna Shaleva
c3916ef2c3 mpt: introduce DummySTTempStoragePrefix
Use this constant as a dummy contract storage item prefix for those
situations when Billet is used as an MPT search wrapper. Otherwise it's
confusing to see `0` passed as a storage prefix to the billet
constructor and then panic inside the Billet's code if this prefix is
`0`.

Ref. https://github.com/nspcc-dev/neo-go/pull/2201.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-06 16:01:14 +03:00
Anna Shaleva
22a5bbcef3 mpt: extend Billet constructor documentation
Billet's mode must match the DB's MPT mode, otherwise Billet-level nodes
decoding won't work properly startign from 86cb4ed8.

Ref.
https://github.com/nspcc-dev/neo-go/pull/3808#issuecomment-2703668032
and 49945e9ae9.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-06 15:56:14 +03:00
Anna Shaleva
e63cbe7c82
Merge pull request #3831 from nspcc-dev/pretty-log
Improve errors reporting
2025-03-06 15:53:13 +03:00
Anna Shaleva
13b75c9d1a notary: warn if no space left in completed transaction queue
No functional changes, just add a warning to the node's logs.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-06 12:18:37 +03:00
Anna Shaleva
3ec14d2e8f *: extend NotaryRequest verification errors
This change is compatible with the old protocol.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-05 19:14:28 +03:00
Anna Shaleva
e9f496a19f cli: use context-bound writer to log message
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-03-05 19:05:26 +03:00
Anna Shaleva
68d7e8e01c
Merge pull request #3820 from nspcc-dev/getblocknotifications
rpcclient: fix getblocknotifications filter handling
2025-02-27 14:46:56 +03:00
Ekaterina Pavlova
a227572d01 rpcclient: fix getblocknotifications no filter case handling
`error meta/blocks.go:96 block handling failed {"service": "meta data",
"error": "fetching
781475e13a407826b9147328f30a6a3635cf19238dc45b8c23fa9cd989672aba block:
Invalid params (-32602) - invalid filter: json: cannot unmarshal array
into Go value of type neorpc.NotificationFilter"}`

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-27 19:24:48 +08:00
Anna Shaleva
64e3b6aa48
Merge pull request #3819 from nspcc-dev/genesis-header-size
*: fix GetExpectedHeaderSize for genesis block case
2025-02-26 20:49:34 +08:00
Ekaterina Pavlova
a7c66dcb0b blockfetcher: add genesis header size to the headerSizeMap
Close #3807

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-26 11:46:41 +08:00
Ekaterina Pavlova
ff8ea5d4e8 block: fix GetExpectedHeaderSize for genesis block case
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-26 11:45:46 +08:00
Roman Khimov
0d8c751e50
Merge pull request #3815 from nspcc-dev/max-cond-nest 2025-02-17 15:45:17 +03:00
Roman Khimov
d0e47c739a transaction: fix MaxConditionNesting
Rework 75d12081bf to follow https://github.com/neo-project/neo/pull/3761,
let's have a real depth value in this constant.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-17 15:25:42 +03:00
Roman Khimov
3b54213c08
Merge pull request #3811 from nspcc-dev/rel-0.108.1
CHANGELOG: release 0.108.1
2025-02-13 17:49:17 +03:00
Roman Khimov
d7232357d4 CHANGELOG: release 0.108.1
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-13 17:47:26 +03:00
Roman Khimov
e05863d13b
Merge pull request #3810 from nspcc-dev/nested-levels
core: fix `too many nesting levels` bug
2025-02-13 17:41:54 +03:00
Ekaterina Pavlova
75d12081bf core: fix too many nesting levels bug
The C# implementation only decrements the nesting depth once in the
parent composite condition (And, Or, or Not) and then passes that
already‐decremented value onto its subconditions without further
decrementing inside the loop.

Close #3809

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-13 16:48:03 +03:00
Roman Khimov
62972ebc14
Merge pull request #3806 from nspcc-dev/rel-0.108.0
Release 0.108.0
2025-02-11 18:17:35 +03:00
Roman Khimov
406e8cd840 CHANGELOG: release 0.108.0
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-11 17:09:00 +03:00
Roman Khimov
30b3318503 vm: drop Dump*Slot methods from Context
As scheduled for the 0.108.0 release.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-11 16:38:21 +03:00
Roman Khimov
a1db45d668
Merge pull request #3805 from nspcc-dev/block-notifications 2025-02-11 16:28:23 +03:00
Roman Khimov
25e2d80363 neorpc: rename BlockNotifications fields to follow triggers exactly
Be more consistent.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-11 16:06:15 +03:00
Roman Khimov
93be186685 rpcsrv: check args first, get data from DB then
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-11 15:55:24 +03:00
Roman Khimov
d1926e4fb0 rpcsrv: handle block aer errors in getblocknotifications
Ignoring them is not correct. Notice that block-level aers are always present
and if filtering returns nothing the value of the resulting slice still remains
nil, so the behavior is the same.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-11 15:54:46 +03:00
Roman Khimov
cb4aba497e rpcsrv: filter out invalid getblocknotifications requests
Unknown fields are not allowed.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-11 15:52:34 +03:00
Ricardo Prado
bd170fb849 rpc: implement getblocknotifications API
Add new RPC method to retrieve block notifications organized by trigger type.

Signed-off-by: Ricardo Prado <ricardo.prado@simpli.com.br>
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-11 15:40:15 +03:00
Roman Khimov
8d728b4ec1
Merge pull request #3789 from nspcc-dev/headers
Headers fetching via NeoFS BlockFetcher service
2025-02-06 17:05:20 +03:00
Ekaterina Pavlova
1ad62ef5f5 blockfetcher: add headerSizeMap to GetRange headers accordingly
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-06 16:13:01 +03:00
Ekaterina Pavlova
91b60ea7d3 network: integrate state sync module with blockfetcher
Close #3574

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-06 16:12:55 +03:00
Roman Khimov
3fa02d062b
Merge pull request #3804 from nspcc-dev/toscparameter
rpcbinding: expand struct generator with ToSCParameter
2025-02-04 17:52:41 +03:00
Ekaterina Pavlova
de50d0be49 bqueue: make queue generic
The queue can operate with any item that implements proper interface.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-04 16:48:03 +03:00
Roman Khimov
e70b1063a6 rpcbinding: expand struct generator with ToSCParameter
Follow-up to 00e22b9751, while
stackitem.Convertible is good for vm/emit we still need
smartcontract.Convertible for invokers.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-02-04 12:10:26 +03:00
Roman Khimov
72e0cff82b
Merge pull request #3802 from nspcc-dev/range-int
zkpbinding: remove range usage from smartcontract
2025-02-03 23:06:46 +03:00
Ekaterina Pavlova
267d7dca78 zkpbinding: update interop
This place was not updated with update_deps.sh.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-03 22:00:37 +03:00
Ekaterina Pavlova
83a02fc0c0 zkpbinding: remove range usage from smartcontract
Reverting a part of 1b83dc2, because ranging over integers is not
supported by smart contract compiler, ref. #3525.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-03 21:56:54 +03:00
Roman Khimov
847479e6a1
Merge pull request #3801 from nspcc-dev/vm-tests
*: neo-vm submodule update dependency
2025-02-03 14:40:52 +03:00
Ekaterina Pavlova
d02e2b629d *: neo-vm submodule update dependency
Close #3204

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2025-02-03 12:09:02 +03:00
Anna Shaleva
6969982f77
Merge pull request #3800 from nspcc-dev/dependabot/go_modules/examples/zkp/cubic_circuit/github.com/consensys/gnark-0.12.0
build(deps): bump github.com/consensys/gnark from 0.11.0 to 0.12.0 in /examples/zkp/cubic_circuit
2025-01-30 23:48:57 +03:00
dependabot[bot]
f09a04d705
build(deps): bump github.com/consensys/gnark
Bumps [github.com/consensys/gnark](https://github.com/consensys/gnark) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/consensys/gnark/releases)
- [Changelog](https://github.com/Consensys/gnark/blob/master/CHANGELOG.md)
- [Commits](https://github.com/consensys/gnark/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: github.com/consensys/gnark
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-30 12:54:06 +00:00
Roman Khimov
5d0cd53d67
Merge pull request #3799 from nspcc-dev/dbft-0.3.2
go.mod: fetch dBFT v0.3.2
2025-01-30 15:51:59 +03:00
Anna Shaleva
e704731f2f go.mod: fetch dBFT v0.3.2
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-01-30 15:35:20 +03:00
Anna Shaleva
50cb21333e
Merge pull request #3796 from nspcc-dev/si-fix
rpcbindings: fix missing return item
2025-01-27 15:54:26 +03:00
Roman Khimov
8ba2bbf87c rpcbindings: fix missing return item
... not enough return values
        have (error)
        want (stackitem.Item, error)

An omission of 00e22b9751.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-26 23:47:36 +03:00
Anna Shaleva
7ba8048530
Merge pull request #3569 from ixje/applog-invocations
*: add invocations to applicationlog
2025-01-24 18:42:53 +03:00
Roman Khimov
22c5cf3537
Merge pull request #3794 from nspcc-dev/tostackitem 2025-01-24 18:06:44 +03:00
ixje
4b2ee9a424
state, rpc: add invocations to applicationlog
Add setting to include smart contract invocations data to the applicationlog.
Original issue at https://github.com/neo-project/neo/issues/3386

Signed-off-by: ixje <erik@coz.io>
2025-01-24 15:44:10 +01:00
Roman Khimov
00e22b9751 rpcbindings: produce ToStackItem companion method
We need it for structs to be usable as function parameters, otherwise
they're not implementing stackitam.Convertible and they fail at transaction
script creation phase:

    2025/01/22 20:31:26 bootstrap error: could not invoke method (addNode): test invocation failed: unsupported operation: *netmap.NetmapNode2 type

Related to https://github.com/nspcc-dev/neofs-node/pull/3088.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-24 17:07:20 +03:00
Roman Khimov
4da753f822 stackitem: split TryMake and Make
Sometimes we may need something that doesn't panic.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-24 17:07:20 +03:00
Roman Khimov
673b26fdb1
Merge pull request #3792 from nspcc-dev/payables
native: sync supported standards with reference
2025-01-21 21:58:22 +03:00
Anna Shaleva
3b28749fd1 examples: add NEP-27 to the list of supported standards
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-01-21 19:41:45 +03:00
Anna Shaleva
257fa7c6c9 native: add NEP-27 to Notary's supported standards
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-01-21 19:41:45 +03:00
Anna Shaleva
b2322a804a native: add NEP-27 to Neo's supported standards
Port https://github.com/neo-project/neo/pull/3643.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-01-21 19:41:43 +03:00
Anna Shaleva
fcd83e9fd7 smartcontract: support NEP-26 and NEP-27
A replacement of NEP11Payable and NEP17Payable. The essence is the same,
but these standards have official name since
https://github.com/neo-project/proposals/pull/169.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2025-01-21 18:14:55 +03:00
Anna Shaleva
16a6a59c43
Merge pull request #3787 from nspcc-dev/gc-improvements
GC and persistence improvements
2025-01-16 14:37:17 +03:00
Roman Khimov
c047bad446 docs: a bit more DB recommendations
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-15 22:08:08 +03:00
Roman Khimov
9513780c45 core: adjust in-memory processed set dynamically
Instead of tick-tocking with sync/async and having an unpredictable data
set we can just try to check for the real amount of keys that be processed
by the underlying DB. Can't be perfect, but still this adds some hard
limit to the amount of in-memory data. It's also adaptive, slower machines
will keep less and faster machines will keep more.

This gives almost perfect 4s cycles for mainnet BoltDB with no tail cutting,
it makes zero sense to process more blocks since we're clearly DB-bound:

2025-01-15T11:35:00.567+0300    INFO    persisted to disk       {"blocks": 1469, "keys": 40579, "headerHeight": 5438141, "blockHeight": 5438140, "velocity": 9912, "took": "4.378939648s"}
2025-01-15T11:35:04.699+0300    INFO    persisted to disk       {"blocks": 1060, "keys": 39976, "headerHeight": 5439201, "blockHeight": 5439200, "velocity": 9888, "took": "4.131985438s"}
2025-01-15T11:35:08.752+0300    INFO    persisted to disk       {"blocks": 1508, "keys": 39658, "headerHeight": 5440709, "blockHeight": 5440708, "velocity": 9877, "took": "4.052347569s"}
2025-01-15T11:35:12.807+0300    INFO    persisted to disk       {"blocks": 1645, "keys": 39565, "headerHeight": 5442354, "blockHeight": 5442353, "velocity": 9864, "took": "4.05547743s"}
2025-01-15T11:35:17.011+0300    INFO    persisted to disk       {"blocks": 1472, "keys": 39519, "headerHeight": 5443826, "blockHeight": 5443825, "velocity": 9817, "took": "4.203258142s"}
2025-01-15T11:35:21.089+0300    INFO    persisted to disk       {"blocks": 1345, "keys": 39529, "headerHeight": 5445171, "blockHeight": 5445170, "velocity": 9804, "took": "4.078297579s"}
2025-01-15T11:35:25.090+0300    INFO    persisted to disk       {"blocks": 1054, "keys": 39326, "headerHeight": 5446225, "blockHeight": 5446224, "velocity": 9806, "took": "4.000524899s"}
2025-01-15T11:35:30.372+0300    INFO    persisted to disk       {"blocks": 1239, "keys": 39349, "headerHeight": 5447464, "blockHeight": 5447463, "velocity": 9744, "took": "4.281444939s"}

2× can be considered, but this calculation isn't perfect for low number of
keys, so somewhat bigger tolerance is preferable for now. Overall it's not
degrading performance, my mainnet/bolt run was even 8% better with this.

Fixes #3249, we don't need any option this way.

Fixes #3783 as well, it no longer OOMs in that scenario. It however can OOM in
case of big GarbageCollectionPeriod (like 400K), but this can't be solved easily.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-15 22:08:08 +03:00
Roman Khimov
c07c74df41 docs: add more recommendataions to GarbageCollectionPeriod
We're trying to delete less than 1% of data in the default configuration for
mainnet, so it looks like this:

    2025-01-14T15:51:39.449+0300    INFO    finished MPT garbage collection {"removed": 221115, "kept": 71236766, "time": "5m40.323822085s"}

Spending this much time for this low gain every 10K blocks is far from being
optimal.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-14 17:20:31 +03:00
Roman Khimov
c14492e8c8 core: raise severity of persistence failure message
It is very critical in fact.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-14 12:46:01 +03:00
Roman Khimov
70aaeb06ad core: fix old transfer data deletion in RUB-only configuration
RemoveUntraceableBlocks without RemoveUntraceableHeaders is still a valid
configuration and removeOldTransfers() only checks for gcBlockTimes now
for timestamps, so they should always be added. This fixes

    2025-01-13T23:28:57.340+0300    ERROR   failed to get block timestamp transfer GC       {"time": "1.162µs", "index": 20000}

for RemoveUntraceableBlocks-only configurations.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-14 12:43:15 +03:00
Roman Khimov
ba0ca6a4ab core: perform synchronized persist if GC took some time
The intention here is to reduce the amount of in-flight changes and prevent
OOM. It doesn't matter what we're doing, persisting or collecting garbage,
what matters is that we're behind the schedule of regular persist cycle.

Refs. #3783.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-13 23:39:48 +03:00
Roman Khimov
1f83f472c7 core: swap transfer and MPT GC
It doesn't change anything logically, but transfer GC needs to have current
block timestamp for its logic and if we're delaying it with MPT GC it can
more often fail to obtain it:

    2025-01-13T16:15:18.311+0300    ERROR   failed to get block timestamp transfer GC       {"time": "1.022µs", "index": 20000}

It's not critical, this garbage can still be collected on the next run, but
we better avoid this anyway.

Refs. #3783.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2025-01-13 23:37:21 +03:00
Anna Shaleva
4d2b88dd9d
Merge pull request #3778 from nspcc-dev/archival-node
Archival node
2024-12-28 12:07:28 +03:00
Roman Khimov
82b400700f capability: add ArchivalNode, fix #3776
This will be used to differentiate full history nodes from state only. Currently
it's just a definition, we can not use it safely on current networks because
nodes will refuse this Version, so actual code using the attribute will be
added in newer versions.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-28 10:51:40 +03:00
Roman Khimov
4720727bf5 capability: allow to decode unknown capabilities
It's more symmetric than the C# counterpart, but the essence is the same: we
can accept capabilities we can't interpret. Refs. https://github.com/neo-project/neo/pull/3639

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-28 10:51:40 +03:00
Roman Khimov
47b679341b capability: specify reserved type range
Follow https://github.com/neo-project/neo/pull/3639.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-28 10:51:40 +03:00
Roman Khimov
e0338b7ed0 capability: improve FullNode documentation
That's the way it's defined and used in C#, see https://github.com/neo-project/neo/pull/3639 also.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-27 12:14:48 +03:00
Anna Shaleva
64c4de4e38
Merge pull request #3700 from nspcc-dev/nep-27-candidate-registration
NEO candidate registration via NEP-27 callback
2024-12-25 10:50:03 +03:00
Roman Khimov
38b9b13098 blockfetcher: make UT work with AIO started
Connection succeeds when AIO is running, some more obscure port is less likely
to cause this problem.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-25 10:36:38 +03:00
Roman Khimov
4d45be8434 native: add candidate registration via onNEP17Payment
Solves two problems:
 * inability to estimate GAS needed for registerCandidate in a regular way
   because of its very high fee (more than what normal RPC servers allow)
 * inability to have MaxBlockSystemFee lower than the registration price
   which is very high on its own (more than practically possible to execute)

See https://github.com/neo-project/neo/issues/3552.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-25 10:36:38 +03:00
Roman Khimov
ab5128fb48 config: enable Echidna for tests
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-24 18:01:12 +03:00
Anna Shaleva
dc747ce3b7
Merge pull request #3761 from nspcc-dev/designate
native: add Echidna changes
2024-12-24 10:36:36 +03:00
Ekaterina Pavlova
663146aa1f examples: update neo-go deps
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-23 17:01:20 +01:00
Ekaterina Pavlova
86b2493edd *: update interop deps
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-23 15:57:05 +01:00
Ekaterina Pavlova
80e18222bc native: add callflag.AllowNotify to some methods to HFEchidna
Close #3702

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-23 15:54:56 +01:00
Ekaterina Pavlova
b096e68428 native: add Base64URL support to the StdLib starting from HFEchidna
Close #3550

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-23 15:54:56 +01:00
Ekaterina Pavlova
dc68e39811 native: add Designation event extension starting from HFEchidna
Close #3549

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-23 15:54:54 +01:00
Anna Shaleva
992890721a
Merge pull request #3771 from Chubru/feat/add_LoadContract
Add 'neotest.ReadNEF' method
2024-12-20 20:53:13 +03:00
Anna Shaleva
cffafc4e79
Merge pull request #3759 from nspcc-dev/container-update
config: update NeoFSBlockFetcher's ContainerID
2024-12-20 20:10:59 +03:00
Chubru
0b21d4caf6
neotest: Add 'ReadNEF' method
Signed-off-by: Chubru <chubru.alexander@gmail.com>
2024-12-20 16:03:03 +03:00
Anna Shaleva
6d20772714
Merge pull request #3763 from nspcc-dev/index-duplicates
cli: add warning for duplicated index files in `upload-bin`
2024-12-17 16:27:50 +03:00
Anna Shaleva
b86c6201d3
Merge pull request #3765 from nspcc-dev/gnark-up
zkp: bump golang.org/x/crypto from 0.26.0 to 0.31.0
2024-12-17 16:24:09 +03:00
Ekaterina Pavlova
c669ae343b cli: add warning for duplicated index files in upload-bin
As we are not afraid of duplicates this is not a critical error anymore.
BlockFetcher will take the first returned by search.

Close #3762

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-17 16:19:07 +03:00
Anna Shaleva
c27101a97c
Merge pull request #3760 from nspcc-dev/default-attr
cli: take care of `upload-bin` defaults
2024-12-17 16:13:21 +03:00
Anna Shaleva
4b7b2ef701 docs: take care of upload-bin documentation
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-17 10:18:34 +03:00
Anna Shaleva
0bb6c6e6bd cli: take care of upload-bin defaults
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-16 15:54:15 +03:00
Ekaterina Pavlova
6d509896fb config: update NeoFSBlockFetcher's ContainerID
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-16 14:26:40 +03:00
Anna Shaleva
24f81a7f3e
Merge pull request #3758 from nspcc-dev/sdk-fix
services: fix error check for NeoFS client Dial
2024-12-16 12:36:03 +03:00
Ekaterina Pavlova
f5ea79d649 services: refactor NeoFS client naming
There is just NeoFS client/conn.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-16 11:12:46 +03:00
Ekaterina Pavlova
e741053f0c services: drop check of Unimplemented gRPC status in neofs helper
NeoFS oracle can miss problem NeoFS server on dial. So `Unimplemented`
gRPC status should not be ignored.

Close #3757

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-16 11:12:43 +03:00
Anna Shaleva
b285cf8c6e zkp: bump golang.org/x/crypto from 0.26.0 to 0.31.0
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-13 19:38:07 +03:00
140 changed files with 6632 additions and 710 deletions

2
.gitmodules vendored
View file

@ -1,4 +1,4 @@
[submodule "pkg/vm/testdata/neo-vm"]
path = pkg/vm/testdata/neo-vm
url = https://github.com/neo-project/neo-vm.git
url = https://github.com/neo-project/neo.git
branch = master

View file

@ -2,6 +2,66 @@
This document outlines major changes between releases.
## 0.108.1 "Revalidation" (13 Feb 2025)
An urgent fix for a very old behavior difference with C# node in Rules witness
condition parsing. It suddenly affected testnet compatibility at block 5450030
and made the chain unprocessable by NeoGo. Please upgrade to fix it, DB is
compatible, no resynchronization required.
Bugs fixed:
* incorrect rule depth limit for Rules witness conditions (#3810)
## 0.108.0 "Participation" (11 Feb 2025)
This version is compatible with the C# node 3.7.6, but also contains some
Echidna changes preview. Additional RPC extensions are introduced with this
release as well as some important fixes and improvements.
We recommend to check your configurations wrt NeoFS synchronization options,
a new set of containers was introduced recently, old ones will eventually be
deleted (which won't break syncrhonization, but can delay it a bit). No DB
resync is required unless you want to use the new "SaveInvocations" option.
New features:
* "Designation" event in RoleManagement native contract starting from Echidna
HF (#3761)
* base64Url encoding and decoding support in StdLib native contract starting
from Echidna HF (#3761)
* NEO candidate registration via NEP-27 payment starting from Echidna HF (#3700)
* ArchivalNode P2P capability (#3778)
* NEP-26 and NEP-27 support everywhere (#3792)
* "SaveInvocations" node parameter that allows to store more detailed
contract invocation data and retrieve it via RPC (#3569)
* "getblocknotifications" RPC API allowing to fetch filtered notifications
from all block execution contexts (#3805)
Behavior changes:
* updated "upload-bin" command defaults (#3760)
* updated NeoFS containers for all networks (#3759)
* additional AllowNotify call flag for some NEO methods starting from Echidna
HF (#3761)
* Dump*Slot methods removed from vm.Context (#3806)
Improvements:
* golang.org/x/crypto update from 0.26.0 to 0.31.0 (#3765)
* neotest can load contracts from NEF/manifest files now (#3771)
* more accurate memory management in persisting/processing cycles preventing
OOM conditions in most cases (#3787)
* RPC bindings now have ToStackItem and ToSCParameter methods for structures
(#3794, #3796, #3804)
* dBFT 0.3.2 with improved timers (#3799)
* github.com/consensys/gnark update from 0.11.0 to 0.12.0 (#3800)
* ability to fetch headers from NeoFS (#3789)
Bugs fixed:
* potentially incorrect handling of misconfigured NeoFS endpoints (#3758)
* duplicate index objects are no longer an error for "upload-bin" (#3763)
* old transfer data removal problem in RemoveUntraceableBlocks configuration,
0.107.2 regression (#3787)
* zkpbinding module producing code that can't be compiled, 0.107.0 regression
(#3802)
## 0.107.2 "Obliteration" (13 Dec 2024)
One more compatible patch-release that introduces `RemoveUntraceableHeaders`

View file

@ -7,10 +7,10 @@ functionality.
## Versions 0.7X.Y (as needed)
* Neo 2.0 support (bug fixes, minor functionality additions)
## Version 0.108.0 (~Jan-Feb 2025)
## Version 0.109.0 (~April 2025)
* protocol updates
* bug fixes
* node resynchronisation from local DB
* NeoFS-based synchronization
## Version 1.0 (2024, TBD)
* stable version
@ -24,12 +24,3 @@ APIs/commands/configurations will be removed and here is a list of scheduled
breaking changes. Consider changing your code/scripts/configurations if you're
using anything mentioned here.
## Dump*Slot() methods of `vm.Context`
The following new methods have been exposed to give access to VM context slot contents
with greater flexibility:
- `ArgumentsSlot`
- `LocalsSlot`
- `StaticsSlot`.
Removal of the `Dump*Slot()` methods are scheduled for the 0.108.0 release.

View file

@ -25,8 +25,11 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/nspcc-dev/neofs-sdk-go/pool"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -43,9 +46,13 @@ const (
DefaultAwaitableTimeout = 3 * 15 * time.Second
)
// RPCEndpointFlag is a long flag name for an RPC endpoint. It can be used to
// check for flag presence in the context.
const RPCEndpointFlag = "rpc-endpoint"
const (
// RPCEndpointFlag is a long flag name for an RPC endpoint. It can be used to
// check for flag presence in the context.
RPCEndpointFlag = "rpc-endpoint"
// NeoFSRPCEndpointFlag is a long flag name for a NeoFS RPC endpoint.
NeoFSRPCEndpointFlag = "fs-rpc-endpoint"
)
// Wallet is a set of flags used for wallet operations.
var Wallet = []cli.Flag{
@ -101,6 +108,22 @@ var RPC = []cli.Flag{
},
}
// NeoFSRPC is a set of flags used for NeoFS RPC connections (endpoint).
var NeoFSRPC = []cli.Flag{&cli.StringSliceFlag{
Name: NeoFSRPCEndpointFlag,
Aliases: []string{"fsr"},
Usage: "List of NeoFS storage node RPC addresses (comma-separated or multiple --fs-rpc-endpoint flags)",
Required: true,
Action: func(ctx *cli.Context, fsRpcEndpoints []string) error {
for _, endpoint := range fsRpcEndpoints {
if endpoint == "" {
return cli.Exit("NeoFS RPC endpoint cannot contain empty values", 1)
}
}
return nil
},
}}
// Historic is a flag for commands that can perform historic invocations.
var Historic = &cli.StringFlag{
Name: "historic",
@ -180,6 +203,26 @@ func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cl
return c, nil
}
// GetNeoFSClientPool returns a NeoFS pool and a signer for the given Context.
func GetNeoFSClientPool(ctx *cli.Context, acc *wallet.Account) (user.Signer, neofs.PoolWrapper, error) {
rpcNeoFS := ctx.StringSlice(NeoFSRPCEndpointFlag)
signer := user.NewAutoIDSignerRFC6979(acc.PrivateKey().PrivateKey)
params := pool.DefaultOptions()
params.SetHealthcheckTimeout(neofs.DefaultHealthcheckTimeout)
params.SetNodeDialTimeout(neofs.DefaultDialTimeout)
params.SetNodeStreamTimeout(neofs.DefaultStreamTimeout)
p, err := pool.New(pool.NewFlatNodeParams(rpcNeoFS), signer, params)
if err != nil {
return nil, neofs.PoolWrapper{}, fmt.Errorf("failed to create NeoFS pool: %w", err)
}
pWrapper := neofs.PoolWrapper{Pool: p}
if err = pWrapper.Dial(context.Background()); err != nil {
return nil, neofs.PoolWrapper{}, fmt.Errorf("failed to dial NeoFS pool: %w", err)
}
return signer, pWrapper, nil
}
// GetInvoker returns an invoker using the given RPC client, context and signers.
// It parses "--historic" parameter to adjust it.
func GetInvoker(c *rpcclient.Client, ctx *cli.Context, signers []transaction.Signer) (*invoker.Invoker, cli.ExitCoder) {

View file

@ -30,7 +30,7 @@ func dumpBin(ctx *cli.Context) error {
count := uint32(ctx.Uint("count"))
start := uint32(ctx.Uint("start"))
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
chain, _, prometheus, pprof, err := InitBCWithMetrics(cfg, log)
if err != nil {
return err
}

View file

@ -140,10 +140,11 @@ func newGraceContext() context.Context {
return ctx
}
func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) {
chain, _, err := initBlockChain(cfg, log)
// InitBCWithMetrics initializes the blockchain with metrics with the given configuration.
func InitBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, *metrics.Service, *metrics.Service, error) {
chain, store, err := initBlockChain(cfg, log)
if err != nil {
return nil, nil, nil, cli.Exit(err, 1)
return nil, nil, nil, nil, cli.Exit(err, 1)
}
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log)
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log)
@ -151,14 +152,14 @@ func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *m
go chain.Run()
err = prometheus.Start()
if err != nil {
return nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Prometheus service: %w", err), 1)
return nil, nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Prometheus service: %w", err), 1)
}
err = pprof.Start()
if err != nil {
return nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Pprof service: %w", err), 1)
return nil, nil, nil, nil, cli.Exit(fmt.Errorf("failed to start Pprof service: %w", err), 1)
}
return chain, prometheus, pprof, nil
return chain, store, prometheus, pprof, nil
}
func dumpDB(ctx *cli.Context) error {
@ -189,7 +190,7 @@ func dumpDB(ctx *cli.Context) error {
defer outStream.Close()
writer := io.NewBinWriterFromIO(outStream)
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
chain, _, prometheus, pprof, err := InitBCWithMetrics(cfg, log)
if err != nil {
return err
}
@ -249,7 +250,7 @@ func restoreDB(ctx *cli.Context) error {
cfg.ApplicationConfiguration.SaveStorageBatch = true
}
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
chain, _, prometheus, pprof, err := InitBCWithMetrics(cfg, log)
if err != nil {
return err
}
@ -470,7 +471,7 @@ func startServer(ctx *cli.Context) error {
return cli.Exit(err, 1)
}
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
chain, _, prometheus, pprof, err := InitBCWithMetrics(cfg, log)
if err != nil {
return cli.Exit(err, 1)
}

View file

@ -185,11 +185,11 @@ func TestInitBCWithMetrics(t *testing.T) {
})
t.Run("bad store", func(t *testing.T) {
_, _, _, err = initBCWithMetrics(config.Config{}, logger)
_, _, _, _, err = InitBCWithMetrics(config.Config{}, logger)
require.Error(t, err)
})
chain, prometheus, pprof, err := initBCWithMetrics(cfg, logger)
chain, _, prometheus, pprof, err := InitBCWithMetrics(cfg, logger)
require.NoError(t, err)
t.Cleanup(func() {
chain.Close()

View file

@ -208,8 +208,15 @@ func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyalty
return res, err
}
// Ensure *NftRoyaltyRecipientShare is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&NftRoyaltyRecipientShare{})
// Ensure *NftRoyaltyRecipientShare is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&NftRoyaltyRecipientShare{})
// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -247,3 +254,58 @@ func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing NftRoyaltyRecipientShare.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = stackitem.NewByteArray(res.Address.BytesBE()), error(nil)
if err != nil {
return nil, fmt.Errorf("field Address: %w", err)
}
items = append(items, itm)
itm, err = (*stackitem.BigInteger)(res.Share), error(nil)
if err != nil {
return nil, fmt.Errorf("field Share: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing NftRoyaltyRecipientShare.
// It implements [smartcontract.Convertible] interface so that NftRoyaltyRecipientShare
// could be used with invokers.
func (res *NftRoyaltyRecipientShare) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.Address)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Address: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.Share)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Share: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}

View file

@ -203,8 +203,15 @@ func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyalty
return res, err
}
// Ensure *NftRoyaltyRecipientShare is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&NftRoyaltyRecipientShare{})
// Ensure *NftRoyaltyRecipientShare is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&NftRoyaltyRecipientShare{})
// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -242,3 +249,58 @@ func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing NftRoyaltyRecipientShare.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = stackitem.NewByteArray(res.Address.BytesBE()), error(nil)
if err != nil {
return nil, fmt.Errorf("field Address: %w", err)
}
items = append(items, itm)
itm, err = (*stackitem.BigInteger)(res.Share), error(nil)
if err != nil {
return nil, fmt.Errorf("field Share: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing NftRoyaltyRecipientShare.
// It implements [smartcontract.Convertible] interface so that NftRoyaltyRecipientShare
// could be used with invokers.
func (res *NftRoyaltyRecipientShare) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.Address)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Address: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.Share)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Share: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}

View file

@ -8,6 +8,7 @@ import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
@ -200,8 +201,15 @@ func itemToCrazyStruct(item stackitem.Item, err error) (*CrazyStruct, error) {
return res, err
}
// Ensure *CrazyStruct is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&CrazyStruct{})
// Ensure *CrazyStruct is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&CrazyStruct{})
// FromStackItem retrieves fields of CrazyStruct from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *CrazyStruct) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -230,6 +238,61 @@ func (res *CrazyStruct) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing CrazyStruct.
// It implements [stackitem.Convertible] interface.
func (res *CrazyStruct) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
itm, err = stackitem.NewBool(res.B), error(nil)
if err != nil {
return nil, fmt.Errorf("field B: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing CrazyStruct.
// It implements [smartcontract.Convertible] interface so that CrazyStruct
// could be used with invokers.
func (res *CrazyStruct) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.B)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}
// itemToSimpleStruct converts stack item into *SimpleStruct.
// NULL item is returned as nil pointer without error.
func itemToSimpleStruct(item stackitem.Item, err error) (*SimpleStruct, error) {
@ -245,8 +308,15 @@ func itemToSimpleStruct(item stackitem.Item, err error) (*SimpleStruct, error) {
return res, err
}
// Ensure *SimpleStruct is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&SimpleStruct{})
// Ensure *SimpleStruct is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&SimpleStruct{})
// FromStackItem retrieves fields of SimpleStruct from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *SimpleStruct) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -269,6 +339,49 @@ func (res *SimpleStruct) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing SimpleStruct.
// It implements [stackitem.Convertible] interface.
func (res *SimpleStruct) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 1)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing SimpleStruct.
// It implements [smartcontract.Convertible] interface so that SimpleStruct
// could be used with invokers.
func (res *SimpleStruct) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 1)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}
// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events
// with "! complicated name %$#" name from the provided [result.ApplicationLog].
func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) {

View file

@ -8,6 +8,7 @@ import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
@ -200,8 +201,15 @@ func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
return res, err
}
// Ensure *Unnamed is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&Unnamed{})
// Ensure *Unnamed is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&Unnamed{})
// FromStackItem retrieves fields of Unnamed from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -230,6 +238,61 @@ func (res *Unnamed) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing Unnamed.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
itm, err = stackitem.NewBool(res.B), error(nil)
if err != nil {
return nil, fmt.Errorf("field B: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing Unnamed.
// It implements [smartcontract.Convertible] interface so that Unnamed
// could be used with invokers.
func (res *Unnamed) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.B)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}
// itemToUnnamedX converts stack item into *UnnamedX.
// NULL item is returned as nil pointer without error.
func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
@ -245,8 +308,15 @@ func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
return res, err
}
// Ensure *UnnamedX is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&UnnamedX{})
// Ensure *UnnamedX is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&UnnamedX{})
// FromStackItem retrieves fields of UnnamedX from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -269,6 +339,49 @@ func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing UnnamedX.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 1)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing UnnamedX.
// It implements [smartcontract.Convertible] interface so that UnnamedX
// could be used with invokers.
func (res *UnnamedX) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 1)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}
// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events
// with "! complicated name %$#" name from the provided [result.ApplicationLog].
func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
@ -384,8 +385,15 @@ func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
return res, err
}
// Ensure *Unnamed is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&Unnamed{})
// Ensure *Unnamed is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&Unnamed{})
// FromStackItem retrieves fields of Unnamed from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -408,6 +416,49 @@ func (res *Unnamed) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing Unnamed.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 1)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing Unnamed.
// It implements [smartcontract.Convertible] interface so that Unnamed
// could be used with invokers.
func (res *Unnamed) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 1)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}
// itemToUnnamedX converts stack item into *UnnamedX.
// NULL item is returned as nil pointer without error.
func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
@ -423,8 +474,15 @@ func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
return res, err
}
// Ensure *UnnamedX is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&UnnamedX{})
// Ensure *UnnamedX is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&UnnamedX{})
// FromStackItem retrieves fields of UnnamedX from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -452,3 +510,58 @@ func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing UnnamedX.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
itm, err = stackitem.NewBool(res.B), error(nil)
if err != nil {
return nil, fmt.Errorf("field B: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing UnnamedX.
// It implements [smartcontract.Convertible] interface so that UnnamedX
// could be used with invokers.
func (res *UnnamedX) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.B)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
@ -380,8 +381,15 @@ func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
return res, err
}
// Ensure *Unnamed is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&Unnamed{})
// Ensure *Unnamed is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&Unnamed{})
// FromStackItem retrieves fields of Unnamed from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -404,6 +412,49 @@ func (res *Unnamed) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing Unnamed.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 1)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing Unnamed.
// It implements [smartcontract.Convertible] interface so that Unnamed
// could be used with invokers.
func (res *Unnamed) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 1)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}
// itemToUnnamedX converts stack item into *UnnamedX.
// NULL item is returned as nil pointer without error.
func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
@ -419,8 +470,15 @@ func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
return res, err
}
// Ensure *UnnamedX is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&UnnamedX{})
// Ensure *UnnamedX is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&UnnamedX{})
// FromStackItem retrieves fields of UnnamedX from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -448,3 +506,58 @@ func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
return nil
}
// ToStackItem creates [stackitem.Item] representing UnnamedX.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = (*stackitem.BigInteger)(res.I), error(nil)
if err != nil {
return nil, fmt.Errorf("field I: %w", err)
}
items = append(items, itm)
itm, err = stackitem.NewBool(res.B), error(nil)
if err != nil {
return nil, fmt.Errorf("field B: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing UnnamedX.
// It implements [smartcontract.Convertible] interface so that UnnamedX
// could be used with invokers.
func (res *UnnamedX) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.I)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field I: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.B)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field B: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}

View file

@ -16,6 +16,35 @@ import (
"github.com/urfave/cli/v2"
)
var neoFSFlags = append([]cli.Flag{
&cli.StringFlag{
Name: "container",
Aliases: []string{"cid"},
Usage: "NeoFS container ID to upload objects to",
Required: true,
Action: cmdargs.EnsureNotEmpty("container"),
},
&flags.AddressFlag{
Name: "address",
Usage: "Address to use for signing the uploading and searching transactions in NeoFS",
},
&cli.UintFlag{
Name: "retries",
Usage: "Maximum number of NeoFS node request retries",
Value: neofs.MaxRetries,
Action: func(context *cli.Context, u uint) error {
if u < 1 {
return cli.Exit("retries should be greater than 0", 1)
}
return nil
},
},
&cli.UintFlag{
Name: "searchers",
Usage: "Number of concurrent searches for objects",
Value: 100,
}}, options.NeoFSRPC...)
// NewCommands returns util commands for neo-go CLI.
func NewCommands() []*cli.Command {
// By default, RPC flag is required. sendtx and txdump may be called without provided rpc-endpoint.
@ -35,42 +64,17 @@ func NewCommands() []*cli.Command {
}, options.RPC...)
txCancelFlags = append(txCancelFlags, options.Wallet...)
uploadBinFlags := append([]cli.Flag{
&cli.StringSliceFlag{
Name: "fs-rpc-endpoint",
Aliases: []string{"fsr"},
Usage: "List of NeoFS storage node RPC addresses (comma-separated or multiple --fs-rpc-endpoint flags)",
Required: true,
Action: func(ctx *cli.Context, fsRpcEndpoints []string) error {
for _, endpoint := range fsRpcEndpoints {
if endpoint == "" {
return cli.Exit("NeoFS RPC endpoint cannot contain empty values", 1)
}
}
return nil
},
&cli.StringFlag{
Name: "block-attribute",
Usage: "Attribute key of the block object",
Value: neofs.DefaultBlockAttribute,
Action: cmdargs.EnsureNotEmpty("block-attribute"),
},
&cli.StringFlag{
Name: "container",
Aliases: []string{"cid"},
Usage: "NeoFS container ID to upload blocks to",
Required: true,
Action: cmdargs.EnsureNotEmpty("container"),
},
&cli.StringFlag{
Name: "block-attribute",
Usage: "Attribute key of the block object",
Required: true,
Action: cmdargs.EnsureNotEmpty("block-attribute"),
},
&cli.StringFlag{
Name: "index-attribute",
Usage: "Attribute key of the index file object",
Required: true,
Action: cmdargs.EnsureNotEmpty("index-attribute"),
},
&flags.AddressFlag{
Name: "address",
Usage: "Address to use for signing the uploading and searching transactions in NeoFS",
Name: "index-attribute",
Usage: "Attribute key of the index file object",
Value: neofs.DefaultIndexFileAttribute,
Action: cmdargs.EnsureNotEmpty("index-attribute"),
},
&cli.UintFlag{
Name: "index-file-size",
@ -80,27 +84,24 @@ func NewCommands() []*cli.Command {
&cli.UintFlag{
Name: "workers",
Usage: "Number of workers to fetch and upload blocks concurrently",
Value: 50,
},
&cli.UintFlag{
Name: "searchers",
Usage: "Number of concurrent searches for blocks",
Value: 20,
},
&cli.UintFlag{
Name: "retries",
Usage: "Maximum number of Neo/NeoFS node request retries",
Value: neofs.MaxRetries,
Action: func(context *cli.Context, u uint) error {
if u < 1 {
return cli.Exit("retries should be greater than 0", 1)
}
return nil
},
},
options.Debug,
}, options.RPC...)
uploadBinFlags = append(uploadBinFlags, options.Wallet...)
uploadBinFlags = append(uploadBinFlags, neoFSFlags...)
uploadStateFlags := append([]cli.Flag{
&cli.StringFlag{
Name: "state-attribute",
Usage: "Attribute key of the state object",
Value: neofs.DefaultStateAttribute,
Action: cmdargs.EnsureNotEmpty("state-attribute"),
},
options.Debug, options.Config, options.ConfigFile, options.RelativePath,
}, options.Wallet...)
uploadStateFlags = append(uploadStateFlags, options.Network...)
uploadStateFlags = append(uploadStateFlags, neoFSFlags...)
return []*cli.Command{
{
Name: "util",
@ -185,6 +186,13 @@ func NewCommands() []*cli.Command {
Action: uploadBin,
Flags: uploadBinFlags,
},
{
Name: "upload-state",
Usage: "Start the node, traverse MPT and upload MPT nodes to the NeoFS container at every StateSyncInterval number of blocks",
UsageText: "neo-go util upload-state --fs-rpc-endpoint <address1>[,<address2>[...]] --container <cid> --state-attribute state --wallet <wallet> [--wallet-config <config>] [--address <address>] [--searchers <num>] [--retries <num>] [--debug] [--config-path path] [-p/-m/-t] [--config-file file]",
Action: uploadState,
Flags: uploadStateFlags,
},
},
},
}

View file

@ -19,28 +19,14 @@ import (
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/pool"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/urfave/cli/v2"
)
// poolWrapper wraps a NeoFS pool to adapt its Close method to return an error.
type poolWrapper struct {
*pool.Pool
}
// Close closes the pool and returns nil.
func (p poolWrapper) Close() error {
p.Pool.Close()
return nil
}
func uploadBin(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
rpcNeoFS := ctx.StringSlice("fs-rpc-endpoint")
containerIDStr := ctx.String("container")
attr := ctx.String("block-attribute")
numWorkers := ctx.Uint("workers")
maxParallelSearches := ctx.Uint("searchers")
@ -52,12 +38,6 @@ func uploadBin(ctx *cli.Context) error {
if err != nil {
return cli.Exit(fmt.Sprintf("failed to load account: %v", err), 1)
}
var containerID cid.ID
if err = containerID.DecodeString(containerIDStr); err != nil {
return cli.Exit(fmt.Sprintf("failed to decode container ID: %v", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
rpc, err := options.GetRPCClient(gctx, ctx)
@ -65,39 +45,20 @@ func uploadBin(ctx *cli.Context) error {
return cli.Exit(fmt.Sprintf("failed to create RPC client: %v", err), 1)
}
signer := user.NewAutoIDSignerRFC6979(acc.PrivateKey().PrivateKey)
params := pool.DefaultOptions()
params.SetHealthcheckTimeout(neofs.DefaultHealthcheckTimeout)
params.SetNodeDialTimeout(neofs.DefaultDialTimeout)
params.SetNodeStreamTimeout(neofs.DefaultStreamTimeout)
p, err := pool.New(pool.NewFlatNodeParams(rpcNeoFS), signer, params)
signer, pWrapper, err := options.GetNeoFSClientPool(ctx, acc)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to create NeoFS pool: %v", err), 1)
return cli.Exit(err, 1)
}
pWrapper := poolWrapper{p}
if err = pWrapper.Dial(context.Background()); err != nil {
return cli.Exit(fmt.Sprintf("failed to dial NeoFS pool: %v", err), 1)
}
defer p.Close()
var containerObj container.Container
err = retry(func() error {
containerObj, err = p.ContainerGet(ctx.Context, containerID, client.PrmContainerGet{})
return err
}, maxRetries, debug)
if err != nil {
return cli.Exit(fmt.Errorf("failed to get container with ID %s: %w", containerID, err), 1)
}
containerMagic := containerObj.Attribute("Magic")
defer pWrapper.Close()
v, err := rpc.GetVersion()
if err != nil {
return cli.Exit(fmt.Sprintf("failed to get version from RPC: %v", err), 1)
}
magic := strconv.Itoa(int(v.Protocol.Network))
if containerMagic != magic {
return cli.Exit(fmt.Sprintf("container magic %s does not match the network magic %s", containerMagic, magic), 1)
containerID, err := getContainer(ctx, pWrapper, magic, maxRetries, debug)
if err != nil {
return cli.Exit(err, 1)
}
currentBlockHeight, err := rpc.GetBlockCount()
@ -138,7 +99,7 @@ func retry(action func() error, maxRetries uint, debug bool) error {
}
// uploadBlocksAndIndexFiles uploads the blocks and index files to the container using the pool.
func uploadBlocksAndIndexFiles(ctx *cli.Context, p poolWrapper, rpc *rpcclient.Client, signer user.Signer, containerID cid.ID, attr, indexAttributeKey string, buf []byte, currentIndexFileID, indexFileSize, currentBlockHeight uint, numWorkers, maxRetries uint, debug bool) error {
func uploadBlocksAndIndexFiles(ctx *cli.Context, p neofs.PoolWrapper, rpc *rpcclient.Client, signer user.Signer, containerID cid.ID, attr, indexAttributeKey string, buf []byte, currentIndexFileID, indexFileSize, currentBlockHeight uint, numWorkers, maxRetries uint, debug bool) error {
if currentIndexFileID*indexFileSize >= currentBlockHeight {
fmt.Fprintf(ctx.App.Writer, "No new blocks to upload. Need to upload starting from %d, current height %d\n", currentIndexFileID*indexFileSize, currentBlockHeight)
return nil
@ -257,7 +218,7 @@ func uploadBlocksAndIndexFiles(ctx *cli.Context, p poolWrapper, rpc *rpcclient.C
if err != nil {
return fmt.Errorf("failed to upload index file: %w", err)
}
fmt.Println("Successfully uploaded index file ", indexFileStart/indexFileSize)
fmt.Fprintln(ctx.App.Writer, "Successfully uploaded index file ", indexFileStart/indexFileSize)
}
clear(buf)
}
@ -265,7 +226,7 @@ func uploadBlocksAndIndexFiles(ctx *cli.Context, p poolWrapper, rpc *rpcclient.C
}
// searchIndexFile returns the ID and buffer for the next index file to be uploaded.
func searchIndexFile(ctx *cli.Context, p poolWrapper, containerID cid.ID, privKeys *keys.PrivateKey, signer user.Signer, indexFileSize uint, blockAttributeKey, attributeKey string, maxParallelSearches, maxRetries uint, debug bool) (uint, []byte, error) {
func searchIndexFile(ctx *cli.Context, p neofs.PoolWrapper, containerID cid.ID, privKeys *keys.PrivateKey, signer user.Signer, indexFileSize uint, blockAttributeKey, attributeKey string, maxParallelSearches, maxRetries uint, debug bool) (uint, []byte, error) {
var (
// buf is used to store OIDs of the uploaded blocks.
buf = make([]byte, indexFileSize*oid.Size)
@ -281,20 +242,16 @@ func searchIndexFile(ctx *cli.Context, p poolWrapper, containerID cid.ID, privKe
filters.AddFilter("IndexSize", fmt.Sprintf("%d", indexFileSize), object.MatchStringEqual)
for i := 0; ; i++ {
indexIDs := searchObjects(ctx.Context, p, containerID, privKeys, attributeKey, uint(i), uint(i+1), 1, maxRetries, debug, errCh, filters)
count := 0
for range indexIDs {
count++
if count > 1 {
select {
case errCh <- fmt.Errorf("duplicated index file %d found", i):
default:
}
return
}
resOIDs := make([]oid.ID, 0, 1)
for id := range indexIDs {
resOIDs = append(resOIDs, id)
}
if count == 0 {
if len(resOIDs) == 0 {
break
}
if len(resOIDs) > 1 {
fmt.Fprintf(ctx.App.Writer, "WARN: %d duplicated index files with index %d found: %s\n", len(resOIDs), i, resOIDs)
}
existingIndexCount++
}
fmt.Fprintf(ctx.App.Writer, "Current index files count: %d\n", existingIndexCount)
@ -361,7 +318,7 @@ func searchIndexFile(ctx *cli.Context, p poolWrapper, containerID cid.ID, privKe
// searchObjects searches in parallel for objects with attribute GE startIndex and LT
// endIndex. It returns a buffered channel of resulting object IDs and closes it once
// OID search is finished. Errors are sent to errCh in a non-blocking way.
func searchObjects(ctx context.Context, p poolWrapper, containerID cid.ID, privKeys *keys.PrivateKey, blockAttributeKey string, startIndex, endIndex, maxParallelSearches, maxRetries uint, debug bool, errCh chan error, additionalFilters ...object.SearchFilters) chan oid.ID {
func searchObjects(ctx context.Context, p neofs.PoolWrapper, containerID cid.ID, privKeys *keys.PrivateKey, blockAttributeKey string, startIndex, endIndex, maxParallelSearches, maxRetries uint, debug bool, errCh chan error, additionalFilters ...object.SearchFilters) chan oid.ID {
var res = make(chan oid.ID, 2*neofs.DefaultSearchBatchSize)
go func() {
var wg sync.WaitGroup
@ -423,7 +380,7 @@ func searchObjects(ctx context.Context, p poolWrapper, containerID cid.ID, privK
}
// uploadObj uploads object to the container using provided settings.
func uploadObj(ctx context.Context, p poolWrapper, signer user.Signer, containerID cid.ID, objData []byte, attrs []object.Attribute) (oid.ID, error) {
func uploadObj(ctx context.Context, p neofs.PoolWrapper, signer user.Signer, containerID cid.ID, objData []byte, attrs []object.Attribute) (oid.ID, error) {
var (
hdr object.Object
prmObjectPutInit client.PrmObjectPutInit
@ -465,3 +422,28 @@ func getBlockIndex(header object.Object, attribute string) (int, error) {
}
return -1, fmt.Errorf("attribute %s not found", attribute)
}
// getContainer gets container by ID and checks its magic.
func getContainer(ctx *cli.Context, p neofs.PoolWrapper, expectedMagic string, maxRetries uint, debug bool) (cid.ID, error) {
var (
containerObj container.Container
err error
containerIDStr = ctx.String("container")
)
var containerID cid.ID
if err = containerID.DecodeString(containerIDStr); err != nil {
return containerID, fmt.Errorf("failed to decode container ID: %w", err)
}
err = retry(func() error {
containerObj, err = p.ContainerGet(ctx.Context, containerID, client.PrmContainerGet{})
return err
}, maxRetries, debug)
if err != nil {
return containerID, fmt.Errorf("failed to get container: %w", err)
}
containerMagic := containerObj.Attribute("Magic")
if containerMagic != expectedMagic {
return containerID, fmt.Errorf("container magic mismatch: expected %s, got %s", expectedMagic, containerMagic)
}
return containerID, nil
}

206
cli/util/upload_state.go Normal file
View file

@ -0,0 +1,206 @@
package util
import (
"fmt"
"strconv"
"time"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/cli/server"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
gio "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neofs-sdk-go/client"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
)
func uploadState(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.Exit(err, 1)
}
attr := ctx.String("state-attribute")
maxRetries := ctx.Uint("retries")
debug := ctx.Bool("debug")
acc, _, err := options.GetAccFromContext(ctx)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to load account: %v", err), 1)
}
signer, p, err := options.GetNeoFSClientPool(ctx, acc)
if err != nil {
return cli.Exit(err, 1)
}
defer p.Close()
log, _, logCloser, err := options.HandleLoggingParams(debug, cfg.ApplicationConfiguration)
if err != nil {
return cli.Exit(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
}
chain, store, prometheus, pprof, err := server.InitBCWithMetrics(cfg, log)
if err != nil {
return err
}
defer func() {
pprof.ShutDown()
prometheus.ShutDown()
chain.Close()
}()
if chain.GetConfig().Ledger.KeepOnlyLatestState || chain.GetConfig().Ledger.RemoveUntraceableBlocks {
return cli.Exit("only full-state node is supported: disable KeepOnlyLatestState and RemoveUntraceableBlocks", 1)
}
syncInterval := cfg.ProtocolConfiguration.StateSyncInterval
if syncInterval == 0 {
syncInterval = core.DefaultStateSyncInterval
}
containerID, err := getContainer(ctx, p, strconv.Itoa(int(chain.GetConfig().Magic)), maxRetries, debug)
if err != nil {
return cli.Exit(err, 1)
}
stateObjCount, err := searchStateIndex(ctx, p, containerID, acc.PrivateKey(), attr, syncInterval, maxRetries, debug)
if err != nil {
return cli.Exit(fmt.Sprintf("failed searching existing states: %v", err), 1)
}
stateModule := chain.GetStateModule()
currentHeight := int(stateModule.CurrentLocalHeight())
currentStateIndex := currentHeight / syncInterval
if currentStateIndex <= stateObjCount {
log.Info("no new states to upload",
zap.Int("number of uploaded state objects", stateObjCount),
zap.Int("latest state is uploaded for block", stateObjCount*syncInterval),
zap.Int("current height", currentHeight),
zap.Int("StateSyncInterval", syncInterval))
return nil
}
log.Info("starting uploading",
zap.Int("number of uploaded state objects", stateObjCount),
zap.Int("next state to upload for block", stateObjCount*syncInterval),
zap.Int("current height", currentHeight),
zap.Int("StateSyncInterval", syncInterval),
zap.Int("number of states to upload", currentStateIndex-stateObjCount))
for state := stateObjCount; state < currentStateIndex; state++ {
height := uint32(state * syncInterval)
stateRoot, err := stateModule.GetStateRoot(height)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to get state root for height %d: %v", height, err), 1)
}
h, err := chain.GetHeader(chain.GetHeaderHash(height))
if err != nil {
return cli.Exit(fmt.Sprintf("failed to get header %d: %v", height, err), 1)
}
var (
hdr object.Object
prmObjectPutInit client.PrmObjectPutInit
attrs = []object.Attribute{
*object.NewAttribute(attr, strconv.Itoa(int(height))),
*object.NewAttribute("Timestamp", strconv.FormatInt(time.Now().Unix(), 10)),
*object.NewAttribute("StateRoot", stateRoot.Root.StringLE()),
*object.NewAttribute("StateSyncInterval", strconv.Itoa(syncInterval)),
*object.NewAttribute("BlockTime", strconv.FormatUint(h.Timestamp, 10)),
}
)
hdr.SetContainerID(containerID)
hdr.SetOwner(signer.UserID())
hdr.SetAttributes(attrs...)
err = retry(func() error {
writer, err := p.ObjectPutInit(ctx.Context, hdr, signer, prmObjectPutInit)
if err != nil {
return err
}
start := time.Now()
wrt := gio.NewBinWriterFromIO(writer)
wrt.WriteB(byte(0))
wrt.WriteU32LE(uint32(chain.GetConfig().Magic))
wrt.WriteU32LE(height)
wrt.WriteBytes(stateRoot.Root[:])
err = traverseMPT(stateRoot.Root, store, wrt)
if err != nil {
_ = writer.Close()
return err
}
err = writer.Close()
if err != nil {
return err
}
duration := time.Since(start)
res := writer.GetResult()
log.Info("uploaded state object",
zap.String("object ID", res.StoredObjectID().String()),
zap.Uint32("height", height),
zap.Duration("time spent", duration))
return nil
}, maxRetries, debug)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to upload object at height %d: %v", height, err), 1)
}
}
return nil
}
func searchStateIndex(ctx *cli.Context, p neofs.PoolWrapper, containerID cid.ID, privKeys *keys.PrivateKey,
attributeKey string, syncInterval int, maxRetries uint, debug bool,
) (int, error) {
var (
doneCh = make(chan struct{})
errCh = make(chan error)
objCount = 0
)
go func() {
defer close(doneCh)
for i := 0; ; i++ {
indexIDs := searchObjects(ctx.Context, p, containerID, privKeys,
attributeKey, uint(i*syncInterval), uint(i*syncInterval)+1, 1, maxRetries, debug, errCh)
resOIDs := make([]oid.ID, 0, 1)
for id := range indexIDs {
resOIDs = append(resOIDs, id)
}
if len(resOIDs) == 0 {
break
}
if len(resOIDs) > 1 {
fmt.Fprintf(ctx.App.Writer, "WARN: %d duplicated state objects with %s: %d found: %s\n", len(resOIDs), attributeKey, i, resOIDs)
}
objCount++
}
}()
select {
case err := <-errCh:
return objCount, err
case <-doneCh:
return objCount, nil
}
}
func traverseMPT(root util.Uint256, store storage.Store, writer *gio.BinWriter) error {
cache := storage.NewMemCachedStore(store)
billet := mpt.NewBillet(root, mpt.ModeAll, mpt.DummySTTempStoragePrefix, cache)
err := billet.Traverse(func(pathToNode []byte, node mpt.Node, nodeBytes []byte) bool {
writer.WriteVarBytes(nodeBytes)
return writer.Err != nil
}, false)
if err != nil {
return fmt.Errorf("billet traversal error: %w", err)
}
return nil
}

View file

@ -204,8 +204,6 @@ func TestUploadBin(t *testing.T) {
e.In.WriteString("one\r")
e.RunWithErrorCheckExit(t, "failed to load account", append(args, "--cid", "test", "--wallet", "./not-exist.json", "--rpc-endpoint", "https://test")...)
e.In.WriteString("one\r")
e.RunWithErrorCheckExit(t, "failed to decode container ID", append(args, "--cid", "test", "--wallet", testcli.ValidatorWallet, "--rpc-endpoint", "https://test")...)
e.In.WriteString("one\r")
e.RunWithErrorCheckExit(t, "failed to create RPC client", append(args, "--cid", "9iVfUg8aDHKjPC4LhQXEkVUM4HDkR7UCXYLs8NQwYfSG", "--wallet", testcli.ValidatorWallet, "--rpc-endpoint", "https://test")...)
e.In.WriteString("one\r")
e.RunWithErrorCheckExit(t, "failed to dial NeoFS pool", append(args, "--cid", "9iVfUg8aDHKjPC4LhQXEkVUM4HDkR7UCXYLs8NQwYfSG", "--wallet", testcli.ValidatorWallet, "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])...)

View file

@ -93,6 +93,6 @@ ApplicationConfiguration:
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "VCVr6RKg1hfJR7v36xvjbaXTncj1waPi94u9EFyEttW"
ContainerID: "BP71MqY7nJhpuHfdQU3infRSjMgVmSFFt9GfG2GGMZJj"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -111,6 +111,6 @@ ApplicationConfiguration:
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "7a1cn9LNmAcHjESKWxRGG7RSZ55YHJF6z2xDLTCuTZ6c"
ContainerID: "3RCdP3ZubyKyo8qFeo7EJPryidTZaGCMdUjqFJaaEKBV"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -100,6 +100,6 @@ ApplicationConfiguration:
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "FidCcVzUxzY3PeUksCnnpUQ8h8PJeYPessX8FRMuveSy"
ContainerID: "98xz5YeanzxRCpH6EfUhECVm2MynGYchDN4naJViHT9M"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -113,6 +113,6 @@ ApplicationConfiguration:
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "87JRc7vyWcjW8uS32LMoLTAj4ckCzFZWfKbacjU3sAob"
ContainerID: "A8nGtDemWrm2SjfcGAG6wvrxmXwqc5fwr8ezNDm6FraT"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -22,6 +22,7 @@ ProtocolConfiguration:
Basilisk: 6
Cockatrice: 9
Domovoi: 12
Echidna: 13
ApplicationConfiguration:
SkipBlockVerification: false

View file

@ -73,39 +73,16 @@ parameter.
Once all blocks available in the NeoFS container are processed, the service
shuts down automatically.
### NeoFS Upload Command
The `upload-bin` command is designed to fetch blocks from the RPC node and upload
them to the NeoFS container.
It also creates and uploads index files. Below is an example usage of the command:
### NeoFS block uploading command
The `util upload-bin` command is designed to fetch blocks from the RPC node and upload
them to the NeoFS container. It also creates and uploads index files. Below is an
example usage of the command:
```shell
./bin/neo-go util upload-bin --cid 9iVfUg8aDHKjPC4LhQXEkVUM4HDkR7UCXYLs8NQwYfSG --wallet-config ./wallet-config.yml --block-attribute Block --index-attribute Index --rpc-endpoint https://rpc.t5.n3.nspcc.ru:20331 -fsr st1.t5.fs.neo.org:8080 -fsr st2.t5.fs.neo.org:8080 -fsr st3.t5.fs.neo.org:8080
```
The command supports the following options:
```
NAME:
neo-go util upload-bin - Fetch blocks from RPC node and upload them to the NeoFS container
USAGE:
neo-go util upload-bin --fs-rpc-endpoint <address1>[,<address2>[...]] --container <cid> --block-attribute block --index-attribute index --rpc-endpoint <node> [--timeout <time>] --wallet <wallet> [--wallet-config <config>] [--address <address>] [--workers <num>] [--searchers <num>] [--index-file-size <size>] [--retries <num>] [--debug]
OPTIONS:
--fs-rpc-endpoint value, --fsr value [ --fs-rpc-endpoint value, --fsr value ] List of NeoFS storage node RPC addresses (comma-separated or multiple --fs-rpc-endpoint flags)
--container value, --cid value NeoFS container ID to upload blocks to
--block-attribute value Attribute key of the block object
--index-attribute value Attribute key of the index file object
--address value Address to use for signing the uploading and searching transactions in NeoFS
--index-file-size value Size of index file (default: 128000)
--workers value Number of workers to fetch, upload and search blocks concurrently (default: 50)
--searchers value Number of concurrent searches for blocks (default: 20)
--retries value Maximum number of Neo/NeoFS node request retries (default: 5)
--debug, -d Enable debug logging (LOTS of output, overrides configuration) (default: false)
--rpc-endpoint value, -r value RPC node address
--timeout value, -s value Timeout for the operation (default: 10s)
--wallet value, -w value Wallet to use to get the key for transaction signing; conflicts with --wallet-config flag
--wallet-config value Path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag
--help, -h show help
```
Run `./bin/neo-go util upload-bin --help` to see the full list of supported options.
This command works as follows:
1. Fetches the current block height from the RPC node.
@ -117,10 +94,33 @@ This command works as follows:
7. Repeats steps 4-6 until the current block height is reached.
If the command is interrupted, it can be resumed. It starts the uploading process
from the last uploaded index file.
from the last uploaded index file.
For a given block sequence, only one type of index file is supported. If new index
files are needed (different `index-file-size` or `index-attribute`), the entire
block sequence must be uploaded from the beginning. Please, add a comment to the
files are needed (different `index-file-size` or `index-attribute`), `upload-bin`
will upload the entire block sequence starting from genesis since no migration is
supported yet by this command. Please, add a comment to the
[#3744](https://github.com/nspcc-dev/neo-go/issues/3744) issue if you need this
functionality.
functionality.
### NeoFS state uploading command
The `util upload-state` command is used to start a node, traverse the MPT over the
smart contract storage, and upload MPT nodes to a NeoFS container at every
`StateSyncInterval` number of blocks. Below is an example usage of the command:
```shell
./bin/neo-go util upload-state --cid 9iVfUg8aDHKjPC4LhQXEkVUM4HDkR7UCXYLs8NQwYfSG --wallet-config ./wallet-config.yml --state-attribute State -m -fsr st1.t5.fs.neo.org:8080 -fsr st2.t5.fs.neo.org:8080 -fsr st3.t5.fs.neo.org:8080
```
Run `./bin/neo-go util upload-state --help` to see the full list of supported options.
This command works as follows:
1. Searches for the state objects stored in NeoFS to find the latest uploaded object.
2. Checks if new state objects could be uploaded given the current local state height.
3. Traverses the MPT nodes (pre-order) starting from the stateroot at the height of the
latest uploaded state object down to its children.
4. Uploads the MPT nodes to the NeoFS container.
5. Repeats steps 3-4 with a step equal to the `StateSyncInterval` number of blocks.
If the command is interrupted, it can be resumed. It starts the uploading process
from the last uploaded state object.

View file

@ -18,7 +18,7 @@ node-related settings described in the table below.
| --- | --- | --- | --- |
| DBConfiguration | [DB Configuration](#DB-Configuration) | | Describes configuration for database. See the [DB Configuration](#DB-Configuration) section for details. |
| LogLevel | `string` | "info" | Minimal logged messages level (can be "debug", "info", "warn", "error", "dpanic", "panic" or "fatal"). |
| GarbageCollectionPeriod | `uint32` | 10000 | Controls MPT garbage collection interval (in blocks) for configurations with `RemoveUntraceableBlocks` enabled and `KeepOnlyLatestState` disabled. In this mode the node stores a number of MPT trees (corresponding to `MaxTraceableBlocks` and `StateSyncInterval`), but the DB needs to be clean from old entries from time to time. Doing it too often will cause too much processing overhead, doing it too rarely will leave more useless data in the DB. |
| GarbageCollectionPeriod | `uint32` | 10000 | Controls MPT garbage collection interval (in blocks) for configurations with `RemoveUntraceableBlocks` enabled and `KeepOnlyLatestState` disabled. In this mode the node stores a number of MPT trees (corresponding to `MaxTraceableBlocks` and `StateSyncInterval`), but the DB needs to be clean from old entries from time to time. Doing it too often will cause too much processing overhead (it requires going through the whole DB which can take minutes), doing it too rarely will leave more useless data in the DB. Always compare this to `MaxTraceableBlocks`, values lower than 10% of it are likely too low, values higher than 50% are likely to leave more garbage than is possible to collect. The default value is more aligned with NeoFS networks that have low MTB values, but for N3 mainnet it's too low. |
| KeepOnlyLatestState | `bool` | `false` | Specifies if MPT should only store the latest state (or a set of latest states, see `P2PStateExchangeExtensions` section in the ProtocolConfiguration for details). If true, DB size will be smaller, but older roots won't be accessible. This value should remain the same for the same database. | |
| LogPath | `string` | "", so only console logging | File path where to store node logs. |
| NeoFSBlockFetcher | [NeoFS BlockFetcher Configuration](#NeoFS-BlockFetcher-Configuration) | | NeoFS BlockFetcher module configuration. See the [NeoFS BlockFetcher Configuration](#NeoFS-BlockFetcher-Configuration) section for details. |
@ -35,6 +35,7 @@ node-related settings described in the table below.
| SaveStorageBatch | `bool` | `false` | Enables storage batch saving before every persist. It is similar to StorageDump plugin for C# node. |
| SkipBlockVerification | `bool` | `false` | Allows to disable verification of received/processed blocks (including cryptographic checks). |
| StateRoot | [State Root Configuration](#State-Root-Configuration) | | State root module configuration. See the [State Root Configuration](#State-Root-Configuration) section for details. |
| SaveInvocations | `bool` | `false` | Determines if additional smart contract invocation details are stored. If enabled, the `getapplicationlog` RPC method will return a new field with invocation details for the transaction. See the [RPC](rpc.md#applicationlog-invocations) documentation for more information. |
### P2P Configuration
@ -100,7 +101,12 @@ DBConfiguration:
```
where:
- `Type` is the database type (string value). Supported types: `leveldb`, `boltdb` and
`inmemory` (not recommended for production usage).
`inmemory` (not recommended for production usage). LevelDB is better for archive nodes
that store all data, it better deals with writes in general, but any tail-cutting node
options seriously degrade its performance. BoltDB works much better in various
performance tests, however it can seriously degrade GC in case the DB size is bigger
than the amount of available memory. BoltDB is also more memory-demanding for some
operations, so GC can be problematic from that angle as well.
- `LevelDBOptions` are settings for LevelDB. Includes the DB files path and ReadOnly mode toggle.
If ReadOnly mode is on, then an error will be returned on attempt to connect to unexisting or empty
database. Database doesn't allow changes in this mode, a warning will be logged on DB persist attempts.
@ -245,6 +251,7 @@ RPC:
MaxRequestBodyBytes: 5242880
MaxRequestHeaderBytes: 1048576
MaxWebSocketClients: 64
MaxWebSocketFeeds: 16
SessionEnabled: false
SessionExpirationTime: 15
SessionBackedByMPT: false
@ -290,6 +297,9 @@ where:
number (64 by default). Attempts to establish additional connections will
lead to websocket handshake failures. Use "-1" to disable websocket
connections (0 will lead to using the default value).
- `MaxWebSocketFeeds` -- the maximum simultaneous event subscriptions number
for a single client (16 by default). Attemps to create additional subscriptions
will lead to error.
- `SessionEnabled` denotes whether session-based iterator JSON-RPC API is enabled.
If true, then all iterators got from `invoke*` calls will be stored as sessions
on the server side available for further traverse. `traverseiterator` and
@ -470,7 +480,7 @@ in development and can change in an incompatible way.
| `Basilisk` | Enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. Increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. Enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. | https://github.com/nspcc-dev/neo-go/pull/3056 <br> https://github.com/neo-project/neo/pull/2881 <br> https://github.com/nspcc-dev/neo-go/pull/3080 <br> https://github.com/neo-project/neo/pull/2883 <br> https://github.com/nspcc-dev/neo-go/pull/3085 <br> https://github.com/neo-project/neo/pull/2810 |
| `Cockatrice` | Introduces the ability to update native contracts. Includes a couple of new native smart contract APIs: `keccak256` of native CryptoLib contract and `getCommitteeAddress` of native NeoToken contract. | https://github.com/nspcc-dev/neo-go/pull/3402 <br> https://github.com/neo-project/neo/pull/2942 <br> https://github.com/nspcc-dev/neo-go/pull/3301 <br> https://github.com/neo-project/neo/pull/2925 <br> https://github.com/nspcc-dev/neo-go/pull/3362 <br> https://github.com/neo-project/neo/pull/3154 |
| `Domovoi` | Makes node use executing contract state for the contract call permissions check instead of the state stored in the native Management contract. In C# also makes System.Runtime.GetNotifications interop properly count stack references of notification parameters which prevents users from creating objects that exceed MaxStackSize constraint, but NeoGo has never had this bug, thus proper behaviour is preserved even before HFDomovoi. It results in the fact that some T5 testnet transactions have different ApplicationLogs compared to the C# node, but the node states match. | https://github.com/nspcc-dev/neo-go/pull/3476 <br> https://github.com/neo-project/neo/pull/3290 <br> https://github.com/nspcc-dev/neo-go/pull/3473 <br> https://github.com/neo-project/neo/pull/3290 <br> https://github.com/neo-project/neo/pull/3301 <br> https://github.com/nspcc-dev/neo-go/pull/3485 |
| `Echidna` | No changes for now | https://github.com/nspcc-dev/neo-go/pull/3554 |
| `Echidna` | Introduces `Designation` event extension with `Old` and `New` roles data to native RoleManagement contract. Adds support for `base64UrlEncode` and `base64UrlDecode` methods to native StdLib contract. Extends the list of required call flags for `registerCandidate`, `unregisterCandidate`and `vote` methods of native NeoToken contract with AllowNotify flag. Enables `onNEP17Payment` method of NEO contract for candidate registration. Introduces constraint for maximum number of execution notifications. | https://github.com/nspcc-dev/neo-go/pull/3554 <br> https://github.com/nspcc-dev/neo-go/pull/3761 <br> https://github.com/nspcc-dev/neo-go/pull/3554 <br> https://github.com/neo-project/neo/pull/3597 <br> https://github.com/nspcc-dev/neo-go/pull/3700 <br> https://github.com/nspcc-dev/neo-go/pull/3640 <br> https://github.com/neo-project/neo/pull/3548 |
## DB compatibility
@ -490,6 +500,7 @@ affect this:
- `GarbageCollectionPeriod` must be the same
- `KeepOnlyLatestState` must be the same
- `RemoveUntraceableBlocks` must be the same
- `SaveInvocations` must be the same
BotlDB is also known to be incompatible between machines with different
endianness. Nothing is known for LevelDB wrt this, so it's not recommended

View file

@ -58,6 +58,9 @@ method. Upon successful subscription, clients receive subscription ID for
subsequent management of this subscription. Subscription is only valid for
connection lifetime, no long-term client identification is being made.
The maximum number of simultaneous subscriptions can be set server-side
via `MaxWebSocketFeeds` setting.
Errors are not described down below, but they can be returned as standard
JSON-RPC errors (most often caused by invalid parameters).

View file

@ -248,6 +248,15 @@ block. It can be removed in future versions, but at the moment you can use it
to see how much GAS is burned with a particular block (because system fees are
burned).
#### `getblocknotifications` call
This method returns notifications from a block organized by trigger type.
Supports filtering by contract and event name (the same filter as provided
for subscriptions to execution results, see [notifications specification](notifications.md).
The resulting JSON is an object with three (if matched) field: "onpersist",
"application" and "postpersist" containing arrays of notifications (same JSON
as used in notification service) for the respective triggers.
#### Historic calls
A set of `*historic` extension methods provide the ability of interacting with
@ -356,6 +365,62 @@ to various blockchain events (with simple event filtering) and receive them on
the client as JSON-RPC notifications. More details on that are written in the
[notifications specification](notifications.md).
#### `applicationlog` call invocations
The `SaveInvocations` configuration setting causes the RPC server to store smart contract
invocation details as part of the application logs. This feature is specifically useful to
capture information in the absence of `System.Runtime.Notify` calls for the given smart
contract method. Other use-cases are described in [this issue](https://github.com/neo-project/neo/issues/3386).
Example transaction on Testnet which interacts with the native PolicyContract:
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"txid": "0xd6fe5f61d9cb34d6324db1be42c056d02ba1f1f6cd0bd3f3c6bb24faaaeef2a9",
"executions": [
{
"trigger": "Application",
"vmstate": "HALT",
"gasconsumed": "2028120",
"stack": [
{
"type": "Any"
}
],
"notifications": [],
"exception": null,
"invocations": [
{
"hash": "0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b",
"method": "setFeePerByte",
"arguments": {
"type": "Array",
"value": [
{
"type": "Integer",
"value": "100"
}
]
},
"argumentscount": 1,
"truncated": false
}
]
}
]
}
}
```
For security reasons the `arguments` field data may result in `null` if the count exceeds 2048.
In such case the `Truncated` field will be set to `true`.
The invocation records are presented in a flat structure in the order as how they were executed.
Note that invocation records for faulted transactions are kept and are present in the
applicationlog. This behaviour differs from notifications which are omitted for faulted transactions.
## Reference
* [JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification)

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/engine
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/events
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/iterator
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -1,6 +1,6 @@
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11", "NEP-24"]
supportedstandards: ["NEP-11", "NEP-24", "NEP-27"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens", "royaltyInfo"]
events:
- name: Transfer

View file

@ -3,13 +3,13 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd-nns
go 1.22
require (
github.com/nspcc-dev/neo-go v0.106.4-0.20241016130346-d8e945978af6
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
github.com/nspcc-dev/neo-go v0.107.3-0.20241223145705-86b2493edd6a
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2
github.com/stretchr/testify v1.9.0
)
require (
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.14.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
@ -18,7 +18,6 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
@ -28,11 +27,11 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b // indirect
github.com/nspcc-dev/hrw/v2 v2.0.1 // indirect
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 // indirect
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 // indirect
github.com/nspcc-dev/hrw/v2 v2.0.2 // indirect
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea // indirect
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20241205083504-335d9fe90f24 // indirect
github.com/nspcc-dev/rfc6979 v0.2.3 // indirect
github.com/nspcc-dev/tzhash v1.7.2 // indirect
github.com/nspcc-dev/tzhash v1.8.2 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.20.2 // indirect
@ -57,8 +56,8 @@ require (
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.24.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect

View file

@ -1,9 +1,17 @@
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.14.2 h1:YXVoyPndbdvcEVcseEovVfp0qjJp7S+i5+xgp/Nfbdc=
github.com/bits-and-blooms/bitset v1.14.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
@ -12,17 +20,43 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao=
github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -30,15 +64,11 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
@ -61,31 +91,47 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nspcc-dev/dbft v0.3.0 h1:P9dFQje43sabA6w89l1OqGe1BvnO7DdHcu27w6mfnOE=
github.com/nspcc-dev/dbft v0.3.0/go.mod h1:oFE6paSC/yfFh9mcNU6MheMGOYXK9+sPiRk3YMoz49o=
github.com/nspcc-dev/dbft v0.3.1 h1:3qoc65CVJMtdL/627JZH+1Jz839LmdsVN52L4mlP5z8=
github.com/nspcc-dev/dbft v0.3.1/go.mod h1:BNvJkPKTE28r+qRaAk2C3VoL2J9qzox3fvEeJbh7EWE=
github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b h1:DRG4cRqIOmI/nUPggMgR92Jxt63Lxsuz40m5QpdvYXI=
github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b/go.mod h1:d3cUseu4Asxfo9/QA/w4TtGjM0AbC9ynyab+PfH+Bso=
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/nspcc-dev/neo-go v0.106.4-0.20241016130346-d8e945978af6 h1:3/V1U2ZgbFZQ5xGjTD9NMsz4NHzPy/nYJzcaHDVDu88=
github.com/nspcc-dev/neo-go v0.106.4-0.20241016130346-d8e945978af6/go.mod h1:ds91T4WJwtk7eWUo0fuVC36HpTQKkkdj5AjNxbjXAR0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
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=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12/go.mod h1:JdsEM1qgNukrWqgOBDChcYp8oY4XUzidcKaxY4hNJvQ=
github.com/nspcc-dev/hrw/v2 v2.0.2 h1:Vuc2Yu96MCv1YDUjErMuCt5tq+g/43/Y89u/XfyLkRI=
github.com/nspcc-dev/hrw/v2 v2.0.2/go.mod h1:XRsG20axGJfr0Ytcau/UcZ/9NF54RmUIqmoYKuuliSo=
github.com/nspcc-dev/neo-go v0.107.3-0.20241223145705-86b2493edd6a h1:n9jfbHsFb2H4DsZgwsVfQcvFB4+3EROmJruz8GYlIHM=
github.com/nspcc-dev/neo-go v0.107.3-0.20241223145705-86b2493edd6a/go.mod h1:hng8Bi42di31z1nXtWOboiJYwuZSgb2Wjd68peBIy8I=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea h1:mK0EMGLvunXcFyq7fBURS/CsN4MH+4nlYiqn6pTwWAU=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea/go.mod h1:YzhD4EZmC9Z/PNyd7ysC7WXgIgURc9uCG1UWDeV027Y=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20241205083504-335d9fe90f24 h1:+6KYoXnhs6LfGnn5f+4puuOj3M3MeofBw9iQn7LFG04=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20241205083504-335d9fe90f24/go.mod h1:INZZXiTr9L7gWFeg3RBuB1laH2h9+vnomvg1XE42zQU=
github.com/nspcc-dev/rfc6979 v0.2.3 h1:QNVykGZ3XjFwM/88rGfV3oj4rKNBy+nYI6jM7q19hDI=
github.com/nspcc-dev/rfc6979 v0.2.3/go.mod h1:q3sCL1Ed7homjqYK8KmFSzEmm+7Ngyo7PePbZanhaDE=
github.com/nspcc-dev/tzhash v1.7.2 h1:iRXoa9TJqH/DQO7FFcqpq9BdruF9E7/xnFGlIghl5J4=
github.com/nspcc-dev/tzhash v1.7.2/go.mod h1:oHiH0qwmTsZkeVs7pvCS5cVXUaLhXxSFvnmnZ++ijm4=
github.com/nspcc-dev/tzhash v1.8.2 h1:ebRCbPoEuoqrhC6sSZmrT/jI3h1SzCWakxxV6gp5QAg=
github.com/nspcc-dev/tzhash v1.8.2/go.mod h1:SFwvvB1KyKm45vdWpcOCFpklkUEsXtddnHsk+zq298g=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -95,10 +141,18 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
@ -111,18 +165,40 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
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.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -170,18 +246,16 @@ golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -1,6 +1,6 @@
name: "HASHY NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11", "NEP-24"]
supportedstandards: ["NEP-11", "NEP-24", "NEP-27"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties", "royaltyInfo"]
events:
- name: Transfer

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/oracle
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/runtime
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/storage
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/timer
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/token
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -3,18 +3,18 @@ module github.com/nspcc-dev/neo-go/examples/zkp/cubic
go 1.22
require (
github.com/consensys/gnark v0.11.0
github.com/consensys/gnark-crypto v0.14.0
github.com/consensys/gnark v0.12.0
github.com/consensys/gnark-crypto v0.15.0
github.com/nspcc-dev/neo-go v0.106.3
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.14.2 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/bavard v0.1.27 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
@ -25,14 +25,13 @@ require (
github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/ingonyama-zk/icicle v1.1.0 // indirect
github.com/ingonyama-zk/iciclegnark v0.1.0 // indirect
github.com/ingonyama-zk/icicle/v3 v3.1.1-0.20241118092657-fccdb2f0921b // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mmcloughlin/addchain v0.4.0 // 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/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 // indirect
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 // 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
@ -51,13 +50,13 @@ require (
go.etcd.io/bbolt v1.3.9 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

View file

@ -2,20 +2,20 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.14.2 h1:YXVoyPndbdvcEVcseEovVfp0qjJp7S+i5+xgp/Nfbdc=
github.com/bits-and-blooms/bitset v1.14.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark v0.11.0 h1:YlndnlbRAoIEA+aIIHzNIW4P0dCIOM9/jCVzsXf356c=
github.com/consensys/gnark v0.11.0/go.mod h1:2LbheIOxsBI1a9Ck1XxUoy6PRnH28mSI9qrvtN2HwDY=
github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
github.com/consensys/bavard v0.1.27 h1:j6hKUrGAy/H+gpNrpLU3I26n1yc+VMGmd6ID5+gAhOs=
github.com/consensys/bavard v0.1.27/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
github.com/consensys/gnark v0.12.0 h1:XgQ1kh2R6fHuf5fBYl+i7TxR+QTbGQuZaaqqkk5nLO0=
github.com/consensys/gnark v0.12.0/go.mod h1:WDvuIQ8qrRvWT9NhTrib84WeLVBSGhSTrbQBXs1yR5w=
github.com/consensys/gnark-crypto v0.15.0 h1:OXsWnhheHV59eXIzhL5OIexa/vqTK8wtRYQCtwfMDtY=
github.com/consensys/gnark-crypto v0.15.0/go.mod h1:Ke3j06ndtPTVvo++PhGNgvm+lgpLvzbcE2MqljY7diU=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@ -59,10 +59,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ingonyama-zk/icicle v1.1.0 h1:a2MUIaF+1i4JY2Lnb961ZMvaC8GFs9GqZgSnd9e95C8=
github.com/ingonyama-zk/icicle v1.1.0/go.mod h1:kAK8/EoN7fUEmakzgZIYdWy1a2rBnpCaZLqSHwZWxEk=
github.com/ingonyama-zk/iciclegnark v0.1.0 h1:88MkEghzjQBMjrYRJFxZ9oR9CTIpB8NG2zLeCJSvXKQ=
github.com/ingonyama-zk/iciclegnark v0.1.0/go.mod h1:wz6+IpyHKs6UhMMoQpNqz1VY+ddfKqC/gRwR/64W6WU=
github.com/ingonyama-zk/icicle/v3 v3.1.1-0.20241118092657-fccdb2f0921b h1:AvQTK7l0PTHODD06PVQX1Tn2o29sRIaKIDOvTJmKurY=
github.com/ingonyama-zk/icicle/v3 v3.1.1-0.20241118092657-fccdb2f0921b/go.mod h1:e0JHb27/P6WorCJS3YolbY5XffS4PGBuoW38OthLkDs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -90,8 +88,8 @@ github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUO
github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA=
github.com/nspcc-dev/neo-go v0.106.3 h1:HEyhgkjQY+HfBzotMJ12xx2VuOUphkngZ4kEkjvXDtE=
github.com/nspcc-dev/neo-go v0.106.3/go.mod h1:3vEwJ2ld12N7HRGCaH/l/7EwopplC/+8XdIdPDNmD/M=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
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=
@ -131,8 +129,8 @@ github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
@ -153,8 +151,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
@ -166,8 +164,8 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -180,15 +178,15 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/zkp/xor
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

6
go.mod
View file

@ -13,9 +13,9 @@ require (
github.com/holiman/uint256 v1.3.1
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/mr-tron/base58 v1.2.0
github.com/nspcc-dev/dbft v0.3.1
github.com/nspcc-dev/dbft v0.3.2
github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20241205083504-335d9fe90f24
github.com/nspcc-dev/rfc6979 v0.2.3
github.com/pierrec/lz4 v2.6.1+incompatible
@ -30,7 +30,6 @@ require (
golang.org/x/term v0.27.0
golang.org/x/text v0.21.0
golang.org/x/tools v0.24.0
google.golang.org/grpc v1.65.0
gopkg.in/yaml.v3 v3.0.1
)
@ -73,6 +72,7 @@ require (
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)

8
go.sum
View file

@ -142,14 +142,14 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nspcc-dev/dbft v0.3.1 h1:3qoc65CVJMtdL/627JZH+1Jz839LmdsVN52L4mlP5z8=
github.com/nspcc-dev/dbft v0.3.1/go.mod h1:BNvJkPKTE28r+qRaAk2C3VoL2J9qzox3fvEeJbh7EWE=
github.com/nspcc-dev/dbft v0.3.2 h1:8AFRpV6JZFn1kEPJB7fVQKUm06PzJ69jFxSdOhTEfJo=
github.com/nspcc-dev/dbft v0.3.2/go.mod h1:tpBE0IRebgucPzKGGxv2Iy7s4knpKqODv157Gc/m1RE=
github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b h1:DRG4cRqIOmI/nUPggMgR92Jxt63Lxsuz40m5QpdvYXI=
github.com/nspcc-dev/go-ordered-json v0.0.0-20240830112754-291b000d1f3b/go.mod h1:d3cUseu4Asxfo9/QA/w4TtGjM0AbC9ynyab+PfH+Bso=
github.com/nspcc-dev/hrw/v2 v2.0.2 h1:Vuc2Yu96MCv1YDUjErMuCt5tq+g/43/Y89u/XfyLkRI=
github.com/nspcc-dev/hrw/v2 v2.0.2/go.mod h1:XRsG20axGJfr0Ytcau/UcZ/9NF54RmUIqmoYKuuliSo=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea h1:mK0EMGLvunXcFyq7fBURS/CsN4MH+4nlYiqn6pTwWAU=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea/go.mod h1:YzhD4EZmC9Z/PNyd7ysC7WXgIgURc9uCG1UWDeV027Y=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12.0.20241205083504-335d9fe90f24 h1:+6KYoXnhs6LfGnn5f+4puuOj3M3MeofBw9iQn7LFG04=

View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/internal/examples/oracle
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2

View file

@ -1,2 +1,2 @@
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6 h1:rTnsU+Y/bP1bLN/SNWmOKEexmSeniMQe5bOJxXNbXgg=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241212130705-ea0a6114d2d6/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2 h1:4Bfi6A1kPpaTDuwbDVc6x+R4WXgoNN9wIq6XobDlXHs=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20241223145456-80e18222bca2/go.mod h1:kVLzmbeJJdbIPF2bUYhD8YppIiLXnRQj5yqNZvzbOL0=

View file

@ -49,6 +49,11 @@ type FakeStateSync struct {
AddMPTNodesFunc func(nodes [][]byte) error
}
// HeaderHeight returns the height of the latest stored header.
func (s *FakeStateSync) HeaderHeight() uint32 {
return 0
}
// NewFakeChain returns a new FakeChain structure.
func NewFakeChain() *FakeChain {
return NewFakeChainWithCustomCfg(nil)
@ -447,6 +452,9 @@ func (s *FakeStateSync) Init(currChainHeight uint32) error {
// NeedHeaders implements the StateSync interface.
func (s *FakeStateSync) NeedHeaders() bool { return s.RequestHeaders.Load() }
// NeedBlocks implements the StateSync interface.
func (s *FakeStateSync) NeedBlocks() bool { return false }
// NeedMPTNodes implements the StateSync interface.
func (s *FakeStateSync) NeedMPTNodes() bool {
panic("TODO")
@ -464,3 +472,13 @@ func (s *FakeStateSync) Traverse(root util.Uint256, process func(node mpt.Node,
func (s *FakeStateSync) GetUnknownMPTNodesBatch(limit int) []util.Uint256 {
panic("TODO")
}
// GetConfig implements the StateSync interface.
func (s *FakeStateSync) GetConfig() config.Blockchain {
panic("TODO")
}
// SetOnStageChanged implements the StateSync interface.
func (s *FakeStateSync) SetOnStageChanged(func()) {
panic("TODO")
}

View file

@ -468,12 +468,12 @@ func CreateManifest(di *DebugInfo, o *Options) (*manifest.Manifest, error) {
return m, err
}
if m.ABI.GetMethod(manifest.MethodOnNEP11Payment, -1) != nil {
if err := standard.CheckABI(m, manifest.NEP11Payable); err != nil {
if err := standard.CheckABI(m, manifest.NEP26StandardName); err != nil {
return m, err
}
}
if m.ABI.GetMethod(manifest.MethodOnNEP17Payment, -1) != nil {
if err := standard.CheckABI(m, manifest.NEP17Payable); err != nil {
if err := standard.CheckABI(m, manifest.NEP27StandardName); err != nil {
return m, err
}
}

View file

@ -64,6 +64,7 @@ func (c Config) Blockchain() Blockchain {
return Blockchain{
ProtocolConfiguration: c.ProtocolConfiguration,
Ledger: c.ApplicationConfiguration.Ledger,
NeoFSBlockFetcher: c.ApplicationConfiguration.NeoFSBlockFetcher,
}
}

View file

@ -44,7 +44,8 @@ const (
// match. See #3485 for details.
HFDomovoi // Domovoi
// HFEchidna represents hard-fork introduced in #3554 (ported from
// https://github.com/neo-project/neo/pull/3454).
// https://github.com/neo-project/neo/pull/3454), #3640 (ported from
// https://github.com/neo-project/neo/pull/3548).
HFEchidna // Echidna
// hfLast denotes the end of hardforks enum. Consider adding new hardforks
// before hfLast.

View file

@ -22,6 +22,8 @@ type Ledger struct {
// SkipBlockVerification allows to disable verification of received
// blocks (including cryptographic checks).
SkipBlockVerification bool `yaml:"SkipBlockVerification"`
// SaveInvocations enables smart contract invocation data saving.
SaveInvocations bool `yaml:"SaveInvocations"`
}
// Blockchain is a set of settings for core.Blockchain to use, it includes protocol
@ -29,4 +31,5 @@ type Ledger struct {
type Blockchain struct {
ProtocolConfiguration
Ledger
NeoFSBlockFetcher
}

View file

@ -49,6 +49,8 @@ type (
P2PSigExtensions bool `yaml:"P2PSigExtensions"`
// P2PStateExchangeExtensions enables additional P2P MPT state data exchange logic.
P2PStateExchangeExtensions bool `yaml:"P2PStateExchangeExtensions"`
// NeoFSStateSyncExtensions enables state data exchange logic via NeoFS.
NeoFSStateSyncExtensions bool `yaml:"NeoFSStateSyncExtensions"`
// ReservedAttributes allows to have reserved attributes range for experimental or private purposes.
ReservedAttributes bool `yaml:"ReservedAttributes"`

View file

@ -19,6 +19,7 @@ type (
MaxRequestBodyBytes int `yaml:"MaxRequestBodyBytes"`
MaxRequestHeaderBytes int `yaml:"MaxRequestHeaderBytes"`
MaxWebSocketClients int `yaml:"MaxWebSocketClients"`
MaxWebSocketFeeds int `yaml:"MaxWebSocketFeeds"`
SessionEnabled bool `yaml:"SessionEnabled"`
SessionExpirationTime int `yaml:"SessionExpirationTime"`
SessionBackedByMPT bool `yaml:"SessionBackedByMPT"`

View file

@ -58,7 +58,7 @@ type Ledger interface {
// BlockQueuer is an interface to the block queue manager sufficient for Service.
type BlockQueuer interface {
PutBlock(block *coreb.Block) error
Put(queueable *coreb.Block) error
}
// Service represents a consensus instance.
@ -623,7 +623,7 @@ func (s *service) processBlock(b dbft.Block[util.Uint256]) error {
bb := &b.(*neoBlock).Block
bb.Script = *(s.getBlockWitness(bb))
if err := s.BlockQueue.PutBlock(bb); err != nil {
if err := s.BlockQueue.Put(bb); err != nil {
// The block might already be added via the regular network
// interaction.
if _, errget := s.Chain.GetBlock(bb.Hash()); errget != nil {

View file

@ -528,7 +528,7 @@ type testBlockQueuer struct {
var _ = BlockQueuer(testBlockQueuer{})
// PutBlock implements BlockQueuer interface.
func (bq testBlockQueuer) PutBlock(b *coreb.Block) error {
func (bq testBlockQueuer) Put(b *coreb.Block) error {
return bq.bc.AddBlock(b)
}

View file

@ -51,6 +51,13 @@ type auxBlockIn struct {
Transactions []json.RawMessage `json:"tx"`
}
// GetIndex returns the index of the block. This method should be used
// for interfaces only. As generics don't support structural types
// ref. golang/go#51259.
func (b *Block) GetIndex() uint32 {
return b.Index
}
// ComputeMerkleRoot computes Merkle tree root hash based on actual block's data.
func (b *Block) ComputeMerkleRoot() util.Uint256 {
hashes := make([]util.Uint256, len(b.Transactions))

View file

@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
@ -80,6 +81,13 @@ type baseAux struct {
Witnesses []transaction.Witness `json:"witnesses"`
}
// GetIndex returns the index of the block. This method should be used
// for interfaces only. As generics don't support structural types
// ref. golang/go#51259.
func (b *Header) GetIndex() uint32 {
return b.Index
}
// Hash returns the hash of the block. Notice that it is cached internally,
// so no matter how you change the [Header] after the first invocation of this
// method it won't change. To get an updated hash in case you're changing
@ -228,3 +236,31 @@ func (b *Header) UnmarshalJSON(data []byte) error {
}
return nil
}
// GetExpectedHeaderSize returns the expected Header size with the given number of validators.
// To calculate genesis block Header size numOfValidators should be 0.
func GetExpectedHeaderSize(stateRootInHeader bool, numOfValidators int) int {
var size int
// Genesis block case.
if numOfValidators == 0 {
// 1 byte for the PUSH1 opcode.
size = expectedHeaderSizeWithEmptyWitness + 1
} else {
m := smartcontract.GetDefaultHonestNodeCount(numOfValidators)
// expectedHeaderSizeWithEmptyWitness contains 2 bytes for zero-length (new(Header)).Script.Invocation/Verification
// InvocationScript:
// 64 is the size of the default signature length + 2 bytes length and opcode
// 2 = 1 push opcode + 1 length
// VerifcationScript:
// m = 1 bytes
// 33 = 1 push opcode + 1 length + 33 bytes for public key
// n = 1 bytes
// 5 for SYSCALL
size = expectedHeaderSizeWithEmptyWitness + (1+1+64)*m + 2 + numOfValidators*(1+1+33) + 2 + 5
}
if stateRootInHeader {
size += util.Uint256Size
}
return size
}

View file

@ -61,7 +61,8 @@ const (
defaultTimePerBlock = 15 * time.Second
// HeaderVerificationGasLimit is the maximum amount of GAS for block header verification.
HeaderVerificationGasLimit = 3_00000000 // 3 GAS
defaultStateSyncInterval = 40000
// DefaultStateSyncInterval is the default interval for state sync.
DefaultStateSyncInterval = 40000
// defaultBlockTimesCache should be sufficient for tryRunGC() to get in
// sync with storeBlock(). Most of the time they differ by some thousands of
@ -70,6 +71,13 @@ const (
// either, the next cycle will still do the job (only transfers need this,
// MPT won't notice at all).
defaultBlockTimesCache = 8
// persistSamples is the number of persist velocity samples to use for
// storeBlock limit.
persistSamples = 10
// persistMinForSampling is the minimal number of keys to take persist
// time into account wrt persist velocity.
persistMinForSampling = 100
)
// stateChangeStage denotes the stage of state modification process.
@ -165,6 +173,14 @@ type Blockchain struct {
// Current persisted block count.
persistedHeight uint32
// keysPerPersist is the average number of persisted keys per persist
// time limit.
keysPerPersist uint32
// persistCond is signaled each time persist cycle ends, this wakes
// storeBlock if needed (when it has too many queued blocks)
persistCond *sync.Cond
// Index->Timestamp cache for garbage collector. Headers can be gone
// by the time it runs, so we use a tiny little cache to sync block
// removal (performed in storeBlock()) with transfer/MPT GC (tryRunGC())
@ -284,6 +300,9 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
log.Info("MaxValidUntilBlockIncrement is not set or wrong, using default value",
zap.Uint32("MaxValidUntilBlockIncrement", cfg.MaxValidUntilBlockIncrement))
}
if cfg.P2PStateExchangeExtensions && cfg.NeoFSStateSyncExtensions {
return nil, errors.New("P2PStateExchangeExtensions and NeoFSStateSyncExtensions cannot be enabled simultaneously")
}
if cfg.P2PStateExchangeExtensions {
if !cfg.StateRootInHeader {
return nil, errors.New("P2PStatesExchangeExtensions are enabled, but StateRootInHeader is off")
@ -292,7 +311,17 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
return nil, errors.New("P2PStateExchangeExtensions can be enabled either on MPT-complete node (KeepOnlyLatestState=false) or on light GC-enabled node (RemoveUntraceableBlocks=true)")
}
if cfg.StateSyncInterval <= 0 {
cfg.StateSyncInterval = defaultStateSyncInterval
cfg.StateSyncInterval = DefaultStateSyncInterval
log.Info("StateSyncInterval is not set or wrong, using default value",
zap.Int("StateSyncInterval", cfg.StateSyncInterval))
}
}
if cfg.NeoFSStateSyncExtensions {
if !cfg.NeoFSBlockFetcher.Enabled {
return nil, errors.New("NeoFSStateSyncExtensions are enabled, but NeoFSBlockFetcher is off")
}
if cfg.StateSyncInterval <= 0 {
cfg.StateSyncInterval = DefaultStateSyncInterval
log.Info("StateSyncInterval is not set or wrong, using default value",
zap.Int("StateSyncInterval", cfg.StateSyncInterval))
}
@ -338,6 +367,7 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
contracts: *native.NewContracts(cfg.ProtocolConfiguration),
}
bc.persistCond = sync.NewCond(&bc.lock)
bc.gcBlockTimes, _ = lru.New[uint32, uint64](defaultBlockTimesCache) // Never errors for positive size
bc.stateRoot = stateroot.NewModule(cfg, bc.VerifyWitness, bc.log, bc.dao.Store)
bc.contracts.Designate.StateRootService = bc.stateRoot
@ -435,6 +465,7 @@ func (bc *Blockchain) init() error {
KeepOnlyLatestState: bc.config.Ledger.KeepOnlyLatestState,
Magic: uint32(bc.config.Magic),
Value: version,
SaveInvocations: bc.config.SaveInvocations,
}
bc.dao.PutVersion(ver)
bc.dao.Version = ver
@ -472,6 +503,10 @@ func (bc *Blockchain) init() error {
return fmt.Errorf("protocol configuration Magic mismatch (old=%v, new=%v)",
ver.Magic, bc.config.Magic)
}
if ver.SaveInvocations != bc.config.SaveInvocations {
return fmt.Errorf("SaveInvocations setting mismatch (old=%v, new=%v)",
ver.SaveInvocations, bc.config.SaveInvocations)
}
bc.dao.Version = ver
bc.persistent.Version = ver
@ -1088,7 +1123,7 @@ func (bc *Blockchain) isHardforkEnabled(hf *config.Hardfork, blockHeight uint32)
hfs := bc.config.Hardforks
if hf != nil {
start, ok := hfs[hf.String()]
if !ok || start < blockHeight {
if !ok || start > blockHeight {
return false
}
}
@ -1102,7 +1137,7 @@ func (bc *Blockchain) Run() {
persistTimer := time.NewTimer(persistInterval)
defer func() {
persistTimer.Stop()
if _, err := bc.persist(true); err != nil {
if _, err := bc.persist(); err != nil {
bc.log.Warn("failed to persist", zap.Error(err))
}
if err := bc.dao.Store.Close(); err != nil {
@ -1112,27 +1147,24 @@ func (bc *Blockchain) Run() {
close(bc.runToExitCh)
}()
go bc.notificationDispatcher()
var nextSync bool
for {
select {
case <-bc.stopCh:
return
case <-persistTimer.C:
var oldPersisted uint32
var gcDur time.Duration
if bc.config.Ledger.RemoveUntraceableBlocks {
oldPersisted = atomic.LoadUint32(&bc.persistedHeight)
}
dur, err := bc.persist(nextSync)
dur, err := bc.persist()
if err != nil {
bc.log.Warn("failed to persist blockchain", zap.Error(err))
bc.log.Error("failed to persist blockchain", zap.Error(err))
}
if bc.config.Ledger.RemoveUntraceableBlocks {
gcDur = bc.tryRunGC(oldPersisted)
dur += bc.tryRunGC(oldPersisted)
}
nextSync = dur > persistInterval*2
interval := persistInterval - dur - gcDur
interval := persistInterval - dur
interval = max(interval, time.Microsecond) // Reset doesn't work with zero or negative value.
persistTimer.Reset(interval)
}
@ -1159,8 +1191,8 @@ func (bc *Blockchain) tryRunGC(oldHeight uint32) time.Duration {
oldHeight /= bc.config.Ledger.GarbageCollectionPeriod
newHeight /= bc.config.Ledger.GarbageCollectionPeriod
if tgtBlock > int64(bc.config.Ledger.GarbageCollectionPeriod) && newHeight != oldHeight {
dur = bc.stateRoot.GC(uint32(tgtBlock), bc.store)
dur += bc.removeOldTransfers(uint32(tgtBlock))
dur = bc.removeOldTransfers(uint32(tgtBlock))
dur += bc.stateRoot.GC(uint32(tgtBlock), bc.store)
}
return dur
}
@ -1644,7 +1676,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
}
for index := start; index < stop; index++ {
ts, err := kvcache.DeleteBlock(bc.GetHeaderHash(index), bc.config.Ledger.RemoveUntraceableHeaders)
if bc.config.Ledger.RemoveUntraceableHeaders && index%bc.config.Ledger.GarbageCollectionPeriod == 0 {
if index%bc.config.Ledger.GarbageCollectionPeriod == 0 {
_ = bc.gcBlockTimes.Add(index, ts)
}
if err != nil {
@ -1741,6 +1773,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
Stack: v.Estack().ToArray(),
Events: systemInterop.Notifications,
FaultException: faultException,
Invocations: systemInterop.InvocationCalls,
},
}
appExecResults = append(appExecResults, aer)
@ -1798,6 +1831,14 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
}
bc.lock.Lock()
// Wait for a while if we're lagging behind the persistence routine,
// it's too easy to OOM otherwise. Keep in mind that this check can't
// be perfect, so some tolerance (accepting more) is OK to have.
var persistVelocity = atomic.LoadUint32(&bc.keysPerPersist)
for persistVelocity != 0 && uint32(bc.dao.Store.Len()) > persistVelocity*4 {
bc.persistCond.Wait()
}
_, err = aerCache.Persist()
if err != nil {
bc.lock.Unlock()
@ -2149,7 +2190,7 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
}
// persist flushes current in-memory Store contents to the persistent storage.
func (bc *Blockchain) persist(isSync bool) (time.Duration, error) {
func (bc *Blockchain) persist() (time.Duration, error) {
var (
start = time.Now()
duration time.Duration
@ -2157,11 +2198,7 @@ func (bc *Blockchain) persist(isSync bool) (time.Duration, error) {
err error
)
if isSync {
persisted, err = bc.dao.PersistSync()
} else {
persisted, err = bc.dao.Persist()
}
persisted, err = bc.dao.Persist()
if err != nil {
return 0, err
}
@ -2178,6 +2215,20 @@ func (bc *Blockchain) persist(isSync bool) (time.Duration, error) {
return 0, err
}
duration = time.Since(start)
// Low number of keys is not representative and duration _can_
// be zero in tests on strange platforms like Windows.
if duration > 0 && persisted > persistMinForSampling {
var (
currentVelocity = uint32(int64(persisted) * int64(persistInterval) / int64(duration))
persistVelocity = atomic.LoadUint32(&bc.keysPerPersist)
)
if persistVelocity != 0 {
currentVelocity = min(currentVelocity, 2*persistVelocity) // Normalize sudden spikes.
currentVelocity = (persistVelocity*(persistSamples-1) + currentVelocity) / persistSamples
} // Otherwise it's the first sample and we take it as is.
atomic.StoreUint32(&bc.keysPerPersist, currentVelocity)
updateEstimatedPersistVelocityMetric(currentVelocity)
}
bc.log.Info("persisted to disk",
zap.Uint32("blocks", diff),
zap.Int("keys", persisted),
@ -2188,6 +2239,7 @@ func (bc *Blockchain) persist(isSync bool) (time.Duration, error) {
// update monitoring metrics.
updatePersistedHeightMetric(bHeight)
}
bc.persistCond.Signal()
return duration, nil
}
@ -2798,7 +2850,7 @@ func (bc *Blockchain) PoolTxWithData(t *transaction.Transaction, data any, mp *m
if verificationFunction != nil {
err := verificationFunction(t, data)
if err != nil {
return err
return fmt.Errorf("data verification failed: %w", err)
}
}
return bc.verifyAndPoolTx(t, mp, feer, data)
@ -3117,3 +3169,8 @@ func (bc *Blockchain) GetStoragePrice() int64 {
}
return bc.contracts.Policy.GetStoragePriceInternal(bc.dao)
}
// GetTrimmedBlock returns a block with only the header and transaction hashes.
func (bc *Blockchain) GetTrimmedBlock(hash util.Uint256) (*block.Block, error) {
return bc.dao.GetBlock(hash)
}

View file

@ -63,7 +63,7 @@ func TestAddBlock(t *testing.T) {
assert.Equal(t, lastBlock.Hash(), bc.CurrentHeaderHash())
// This one tests persisting blocks, so it does need to persist()
_, err = bc.persist(false)
_, err = bc.persist()
require.NoError(t, err)
key := make([]byte, 1+util.Uint256Size)

View file

@ -272,7 +272,7 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) {
_, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Domovoi (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err)
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Echidna (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err)
})
t.Run("good", func(t *testing.T) {

View file

@ -448,6 +448,7 @@ type Version struct {
KeepOnlyLatestState bool
Magic uint32
Value string
SaveInvocations bool
}
const (
@ -455,6 +456,7 @@ const (
p2pSigExtensionsBit
p2pStateExchangeExtensionsBit
keepOnlyLatestStateBit
saveInvocationsBit
)
// FromBytes decodes v from a byte-slice.
@ -482,6 +484,7 @@ func (v *Version) FromBytes(data []byte) error {
v.P2PSigExtensions = data[i+2]&p2pSigExtensionsBit != 0
v.P2PStateExchangeExtensions = data[i+2]&p2pStateExchangeExtensionsBit != 0
v.KeepOnlyLatestState = data[i+2]&keepOnlyLatestStateBit != 0
v.SaveInvocations = data[i+2]&saveInvocationsBit != 0
m := i + 3
if len(data) == m+4 {
@ -505,6 +508,9 @@ func (v *Version) Bytes() []byte {
if v.KeepOnlyLatestState {
mask |= keepOnlyLatestStateBit
}
if v.SaveInvocations {
mask |= saveInvocationsBit
}
res := append([]byte(v.Value), '\x00', byte(v.StoragePrefix), mask)
res = binary.LittleEndian.AppendUint32(res, v.Magic)
return res

View file

@ -35,6 +35,9 @@ const (
DefaultBaseExecFee = 30
// ContextNonceDataLen is a length of [Context.NonceData] in bytes.
ContextNonceDataLen = 16
// MaxNotificationCount is the maximum number of notifications per single
// application execution.
MaxNotificationCount = 512
)
// Ledger is the interface to Blockchain required for Context functionality.
@ -63,6 +66,7 @@ type Context struct {
VM *vm.VM
Functions []Function
Invocations map[util.Uint160]int
InvocationCalls []state.ContractInvocation
cancelFuncs []context.CancelFunc
getContract func(*dao.Simple, util.Uint160) (*state.Contract, error)
baseExecFee int64
@ -70,6 +74,7 @@ type Context struct {
loadToken func(ic *Context, id int32) error
GetRandomCounter uint32
signers []transaction.Signer
SaveInvocations bool
}
// NewContext returns new interop context.
@ -78,22 +83,23 @@ func NewContext(trigger trigger.Type, bc Ledger, d *dao.Simple, baseExecFee, bas
loadTokenFunc func(ic *Context, id int32) error,
block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
dao := d.GetPrivate()
cfg := bc.GetConfig().ProtocolConfiguration
cfg := bc.GetConfig()
return &Context{
Chain: bc,
Network: uint32(cfg.Magic),
Hardforks: cfg.Hardforks,
Natives: natives,
Trigger: trigger,
Block: block,
Tx: tx,
DAO: dao,
Log: log,
Invocations: make(map[util.Uint160]int),
getContract: getContract,
baseExecFee: baseExecFee,
baseStorageFee: baseStorageFee,
loadToken: loadTokenFunc,
Chain: bc,
Network: uint32(cfg.Magic),
Hardforks: cfg.Hardforks,
Natives: natives,
Trigger: trigger,
Block: block,
Tx: tx,
DAO: dao,
Log: log,
Invocations: make(map[util.Uint160]int),
getContract: getContract,
baseExecFee: baseExecFee,
baseStorageFee: baseStorageFee,
loadToken: loadTokenFunc,
SaveInvocations: cfg.SaveInvocations,
}
}
@ -213,7 +219,7 @@ type ContractMD struct {
mdCache map[config.Hardfork]*HFSpecificContractMD
// onManifestConstruction is a callback for manifest finalization.
onManifestConstruction func(*manifest.Manifest)
onManifestConstruction func(*manifest.Manifest, config.Hardfork)
}
// HFSpecificContractMD is a hardfork-specific native contract descriptor.
@ -225,7 +231,7 @@ type HFSpecificContractMD struct {
// NewContractMD returns Contract with the specified fields set. onManifestConstruction callback every time
// after hardfork-specific manifest creation and aimed to finalize the manifest.
func NewContractMD(name string, id int32, onManifestConstruction ...func(*manifest.Manifest)) *ContractMD {
func NewContractMD(name string, id int32, onManifestConstruction ...func(*manifest.Manifest, config.Hardfork)) *ContractMD {
c := &ContractMD{Name: name}
if len(onManifestConstruction) != 0 {
c.onManifestConstruction = onManifestConstruction[0]
@ -250,10 +256,10 @@ func (c *ContractMD) HFSpecificContractMD(hf *config.Hardfork) *HFSpecificContra
}
md, ok := c.mdCache[key]
if !ok {
panic(fmt.Errorf("native contract descriptor cache is not initialized: contract %s, hardfork %s", c.Hash.StringLE(), key))
panic(fmt.Errorf("native contract descriptor cache is not initialized: contract %s, hardfork %s", c.Name, key))
}
if md == nil {
panic(fmt.Errorf("native contract descriptor cache is nil: contract %s, hardfork %s", c.Hash.StringLE(), key))
panic(fmt.Errorf("native contract descriptor cache is nil: contract %s, hardfork %s", c.Name, key))
}
return md
}
@ -339,7 +345,7 @@ func (c *ContractMD) buildHFSpecificMD(hf config.Hardfork) {
m.ABI.Methods = abiMethods
m.ABI.Events = abiEvents
if c.onManifestConstruction != nil {
c.onManifestConstruction(m)
c.onManifestConstruction(m, hf)
}
md := &HFSpecificContractMD{
ContractBase: state.ContractBase{
@ -562,10 +568,18 @@ func (ic *Context) IsHardforkActivation(hf config.Hardfork) bool {
}
// AddNotification creates notification event and appends it to the notification list.
func (ic *Context) AddNotification(hash util.Uint160, name string, item *stackitem.Array) {
func (ic *Context) AddNotification(hash util.Uint160, name string, item *stackitem.Array) error {
if ic.IsHardforkEnabled(config.HFEchidna) {
// Do not check persisting triggers to avoid native persist failure. Do not check
// verification trigger since verification context is loaded with ReadOnly flag.
if ic.Trigger == trigger.Application && len(ic.Notifications) == MaxNotificationCount {
return fmt.Errorf("notification count shouldn't exceed %d", MaxNotificationCount)
}
}
ic.Notifications = append(ic.Notifications, state.NotificationEvent{
ScriptHash: hash,
Name: name,
Item: item,
})
return nil
}

View file

@ -1,6 +1,7 @@
package contract
import (
"bytes"
"errors"
"fmt"
"math/big"
@ -69,6 +70,18 @@ func Call(ic *interop.Context) error {
return fmt.Errorf("method not found: %s/%d", method, len(args))
}
hasReturn := md.ReturnType != smartcontract.VoidType
if ic.SaveInvocations {
var (
arrCount = len(args)
argBytes []byte
)
if argBytes, err = ic.DAO.GetItemCtx().Serialize(stackitem.NewArray(args), false); err != nil {
argBytes = nil
}
ci := state.NewContractInvocation(u, method, bytes.Clone(argBytes), uint32(arrCount))
ic.InvocationCalls = append(ic.InvocationCalls, *ci)
}
return callInternal(ic, cs, method, fs, hasReturn, args, true)
}

View file

@ -114,8 +114,7 @@ func Notify(ic *interop.Context) error {
if len(bytes) > MaxNotificationSize {
return fmt.Errorf("notification size shouldn't exceed %d", MaxNotificationSize)
}
ic.AddNotification(curHash, name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
return nil
return ic.AddNotification(curHash, name, stackitem.DeepCopy(stackitem.NewArray(args), true).(*stackitem.Array))
}
// LoadScript takes a script and arguments from the stack and loads it into the VM.

View file

@ -11,6 +11,14 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
)
// DummySTTempStoragePrefix is a dummy contract storage item prefix that may be
// passed to Billet constructor when Billet's restoring functionality is not
// used, i.e. for those situations when only traversal functionality is used.
// Note that using this prefix for MPT restoring is an error which will lead to
// a panic since Billet must have the ability to save contract storage items to
// the underlying DB during MPT restore process.
const DummySTTempStoragePrefix = 0x00
var (
// ErrRestoreFailed is returned when replacing HashNode by its "unhashed"
// candidate fails.
@ -37,7 +45,10 @@ type Billet struct {
// NewBillet returns a new billet for MPT trie restoring. It accepts a MemCachedStore
// to decouple storage errors from logic errors so that all storage errors are
// processed during `store.Persist()` at the caller. Another benefit is
// that every `Put` can be considered an atomic operation.
// that every `Put` can be considered an atomic operation. Note that mode
// parameter must match precisely the Trie mode that is used in the underlying
// DB to store the MPT nodes. Using wrong mode will lead to improper MPT nodes
// decoding and even runtime panic.
func NewBillet(rootHash util.Uint256, mode TrieMode, prefix storage.KeyPrefix, store *storage.MemCachedStore) *Billet {
return &Billet{
TempStoragePrefix: prefix,
@ -65,7 +76,7 @@ func (b *Billet) RestoreHashNode(path []byte, node Node) error {
// If it's a leaf, then put into temporary contract storage.
if leaf, ok := node.(*LeafNode); ok {
if b.TempStoragePrefix == 0 {
if b.TempStoragePrefix == DummySTTempStoragePrefix {
panic("invalid storage prefix")
}
k := append([]byte{byte(b.TempStoragePrefix)}, fromNibbles(path)...)

View file

@ -612,7 +612,7 @@ func (t *Trie) Find(prefix, from []byte, maxNum int) ([]storage.KeyValue, error)
res []storage.KeyValue
count int
)
b := NewBillet(t.root.Hash(), t.mode, 0, t.Store)
b := NewBillet(t.root.Hash(), t.mode, DummySTTempStoragePrefix, t.Store)
process := func(pathToNode []byte, node Node, _ []byte) bool {
if leaf, ok := node.(*LeafNode); ok {
if from == nil || !bytes.Equal(pathToNode, from) { // (*Billet).traverse includes `from` path into result if so. Need to filter out manually.

View file

@ -94,7 +94,7 @@ func (m *TrieStore) Seek(rng storage.SeekRange, f func(k, v []byte) bool) {
}
}
b := NewBillet(m.trie.root.Hash(), m.trie.mode, 0, m.trie.Store)
b := NewBillet(m.trie.root.Hash(), m.trie.mode, DummySTTempStoragePrefix, m.trie.Store)
process := func(pathToNode []byte, node Node, _ []byte) bool {
if leaf, ok := node.(*LeafNode); ok {
// (*Billet).traverse includes `from` path into the result if so. It's OK for Seek, so shouldn't be filtered out.

View file

@ -121,7 +121,15 @@ func newDesignate(initialNodeRoles map[noderoles.Role]keys.PublicKeys) *Designat
eDesc := newEventDescriptor(DesignationEventName,
manifest.NewParameter("Role", smartcontract.IntegerType),
manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
eMD := newEvent(eDesc)
eMD := newEvent(eDesc, config.HFDefault, config.HFEchidna)
s.AddEvent(eMD)
eDesc = newEventDescriptor(DesignationEventName,
manifest.NewParameter("Role", smartcontract.IntegerType),
manifest.NewParameter("BlockIndex", smartcontract.IntegerType),
manifest.NewParameter("Old", smartcontract.ArrayType),
manifest.NewParameter("New", smartcontract.ArrayType))
eMD = newEvent(eDesc, config.HFEchidna)
s.AddEvent(eMD)
return s
@ -412,11 +420,20 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
return fmt.Errorf("failed to update Designation role data cache: %w", err)
}
ic.AddNotification(s.Hash, DesignationEventName, stackitem.NewArray([]stackitem.Item{
ntf := stackitem.NewArray([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(r))),
stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))),
}))
return nil
})
if ic.IsHardforkEnabled(config.HFEchidna) {
old, _, err := s.GetDesignatedByRole(ic.DAO, r, ic.Block.Index)
if err != nil {
return fmt.Errorf("failed to get old nodes for role %d: %w", r, err)
}
ntf.Append(pubsToArray(old))
ntf.Append(pubsToArray(pubs))
}
return ic.AddNotification(s.Hash, DesignationEventName, ntf)
}
func (s *Designate) getRole(item stackitem.Item) (noderoles.Role, bool) {

View file

@ -372,7 +372,10 @@ func (m *Management) deployWithData(ic *interop.Context, args []stackitem.Item)
panic(err)
}
m.callDeploy(ic, newcontract, args[2], false)
m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
err = m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
if err != nil {
panic(err)
}
return contractToStack(newcontract)
}
@ -444,7 +447,10 @@ func (m *Management) updateWithData(ic *interop.Context, args []stackitem.Item)
panic(err)
}
m.callDeploy(ic, contract, args[2], true)
m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
err = m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
if err != nil {
panic(err)
}
return stackitem.Null{}
}
@ -497,7 +503,10 @@ func (m *Management) destroy(ic *interop.Context, sis []stackitem.Item) stackite
if err != nil {
panic(err)
}
m.emitNotification(ic, contractDestroyNotificationName, hash)
err = m.emitNotification(ic, contractDestroyNotificationName, hash)
if err != nil {
panic(err)
}
return stackitem.Null{}
}
@ -610,8 +619,6 @@ func (m *Management) OnPersist(ic *interop.Context) error {
for _, hf := range config.Hardforks {
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
isUpdate = true
activation := hf // avoid loop variable pointer exporting.
activeIn = &activation // reuse ActiveIn variable for the initialization hardfork.
// Break immediately since native Initialize should be called starting from the first hardfork in a raw
// (if there are multiple hardforks with the same enabling height).
break
@ -626,6 +633,10 @@ func (m *Management) OnPersist(ic *interop.Context) error {
currentActiveHFs = append(currentActiveHFs, hf)
}
}
// activeIn is not included into the activeHFs list.
if activeIn != nil && activeIn.Cmp(latestHF) > 0 {
latestHF = *activeIn
}
if !(isDeploy || isUpdate) {
continue
}
@ -668,7 +679,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
// The rest of activating hardforks also require initialization.
for _, hf := range currentActiveHFs {
if err := native.Initialize(ic, &hf, hfSpecificMD); err != nil {
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err)
return fmt.Errorf("initializing %s native contract at HF %s: %w", md.Name, hf, err)
}
}
@ -681,7 +692,10 @@ func (m *Management) OnPersist(ic *interop.Context) error {
if isUpdate {
ntfName = contractUpdateNotificationName
}
m.emitNotification(ic, ntfName, cs.Hash)
err = m.emitNotification(ic, ntfName, cs.Hash)
if err != nil {
return err
}
}
return nil
@ -806,8 +820,8 @@ func (m *Management) getNextContractID(d *dao.Simple) (int32, error) {
return ret, nil
}
func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) {
ic.AddNotification(m.Hash, name, stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}))
func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) error {
return ic.AddNotification(m.Hash, name, stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}))
}
func checkScriptAndMethods(ic *interop.Context, script []byte, methods []manifest.Method) error {

View file

@ -39,7 +39,7 @@ func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
}
defer g.BuildHFSpecificMD(g.ActiveIn())
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
nep17 := newNEP17Native(nativenames.Gas, gasContractID, nil)
nep17.symbol = "GAS"
nep17.decimals = 8
nep17.factor = GASFactor

View file

@ -170,7 +170,11 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
n := &NEO{}
defer n.BuildHFSpecificMD(n.ActiveIn())
nep17 := newNEP17Native(nativenames.Neo, neoContractID)
nep17 := newNEP17Native(nativenames.Neo, neoContractID, func(m *manifest.Manifest, hf config.Hardfork) {
if hf.Cmp(config.HFEchidna) >= 0 {
m.SupportedStandards = append(m.SupportedStandards, manifest.NEP27StandardName)
}
})
nep17.symbol = "NEO"
nep17.decimals = 0
nep17.factor = 1
@ -192,18 +196,34 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
desc = newDescriptor("registerCandidate", smartcontract.BoolType,
manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
md = newMethodAndPrice(n.registerCandidate, 0, callflag.States)
md = newMethodAndPrice(n.registerCandidate, 0, callflag.States, config.HFDefault, config.HFEchidna)
n.AddMethod(md, desc)
md = newMethodAndPrice(n.registerCandidate, 0, callflag.States|callflag.AllowNotify, config.HFEchidna)
n.AddMethod(md, desc)
desc = newDescriptor("unregisterCandidate", smartcontract.BoolType,
manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
md = newMethodAndPrice(n.unregisterCandidate, 1<<16, callflag.States)
md = newMethodAndPrice(n.unregisterCandidate, 1<<16, callflag.States, config.HFDefault, config.HFEchidna)
n.AddMethod(md, desc)
md = newMethodAndPrice(n.unregisterCandidate, 1<<16, callflag.States|callflag.AllowNotify, config.HFEchidna)
n.AddMethod(md, desc)
desc = newDescriptor("onNEP17Payment", smartcontract.VoidType,
manifest.NewParameter("from", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("data", smartcontract.AnyType))
md = newMethodAndPrice(n.onNEP17Payment, 1<<15, callflag.States|callflag.AllowNotify, config.HFEchidna)
n.AddMethod(md, desc)
desc = newDescriptor("vote", smartcontract.BoolType,
manifest.NewParameter("account", smartcontract.Hash160Type),
manifest.NewParameter("voteTo", smartcontract.PublicKeyType))
md = newMethodAndPrice(n.vote, 1<<16, callflag.States)
md = newMethodAndPrice(n.vote, 1<<16, callflag.States, config.HFDefault, config.HFEchidna)
n.AddMethod(md, desc)
md = newMethodAndPrice(n.vote, 1<<16, callflag.States|callflag.AllowNotify, config.HFEchidna)
n.AddMethod(md, desc)
desc = newDescriptor("getCandidates", smartcontract.ArrayType)
@ -459,9 +479,12 @@ func (n *NEO) OnPersist(ic *interop.Context) error {
ic.DAO.PutStorageItem(n.ID, prefixCommittee, cache.committee.Bytes(ic.DAO.GetItemCtx()))
if oldCommittee != nil {
ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
err := ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{
oldCommittee, newCommittee,
}))
if err != nil {
return err
}
}
}
return nil
@ -815,20 +838,56 @@ func (n *NEO) CalculateNEOHolderReward(d *dao.Simple, value *big.Int, start, end
func (n *NEO) registerCandidate(ic *interop.Context, args []stackitem.Item) stackitem.Item {
pub := toPublicKey(args[0])
ok, err := runtime.CheckKeyedWitness(ic, pub)
if err != nil {
panic(err)
} else if !ok {
return stackitem.NewBool(false)
if !ic.IsHardforkEnabled(config.HFEchidna) {
ok, err := runtime.CheckKeyedWitness(ic, pub)
if err != nil {
panic(err)
} else if !ok {
return stackitem.NewBool(false)
}
}
if !ic.VM.AddGas(n.getRegisterPriceInternal(ic.DAO)) {
panic("insufficient gas")
}
err = n.RegisterCandidateInternal(ic, pub)
var err = n.RegisterCandidateInternal(ic, pub)
return stackitem.NewBool(err == nil)
}
// RegisterCandidateInternal registers pub as a new candidate.
func (n *NEO) checkRegisterCandidate(ic *interop.Context, pub *keys.PublicKey) error {
ok, err := runtime.CheckKeyedWitness(ic, pub)
if err != nil {
panic(err)
} else if !ok {
return errors.New("not witnessed by the key owner")
}
return n.RegisterCandidateInternal(ic, pub)
}
func (n *NEO) onNEP17Payment(ic *interop.Context, args []stackitem.Item) stackitem.Item {
var (
caller = ic.VM.GetCallingScriptHash()
_ = toUint160(args[0])
amount = toBigInt(args[1])
pub = toPublicKey(args[2])
regPrice = n.getRegisterPriceInternal(ic.DAO)
)
if caller != n.GAS.Hash {
panic("only GAS is accepted")
}
if !amount.IsInt64() || amount.Int64() != regPrice {
panic(fmt.Errorf("incorrect GAS amount for registration (expected %d)", regPrice))
}
var err = n.checkRegisterCandidate(ic, pub)
if err != nil {
panic(err)
}
n.GAS.burn(ic, n.Hash, amount)
return stackitem.Null{}
}
// RegisterCandidateInternal registers pub as a new candidate. This method must not be
// called outside of VM since it panics on critical errors.
func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
var emitEvent = true
@ -843,16 +902,23 @@ func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey
c.Registered = true
}
err := putConvertibleToDAO(n.ID, ic.DAO, key, c)
if err != nil {
return err
}
if emitEvent {
cache := ic.DAO.GetRWCache(n.ID).(*NeoCache)
cache.votesChanged = true
ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(pub.Bytes()),
stackitem.NewBool(c.Registered),
stackitem.NewBigInteger(&c.Votes),
}))
if err != nil {
// Panic since it's a critical error that must abort execution.
panic(err)
}
}
return err
return nil
}
func (n *NEO) unregisterCandidate(ic *interop.Context, args []stackitem.Item) stackitem.Item {
@ -867,7 +933,8 @@ func (n *NEO) unregisterCandidate(ic *interop.Context, args []stackitem.Item) st
return stackitem.NewBool(err == nil)
}
// UnregisterCandidateInternal unregisters pub as a candidate.
// UnregisterCandidateInternal unregisters pub as a candidate. This method must not be
// called outside of VM since it panics on critical errors.
func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
var err error
@ -887,14 +954,21 @@ func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicK
if !ok {
err = putConvertibleToDAO(n.ID, ic.DAO, key, c)
}
if err != nil {
return err
}
if emitEvent {
ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
err := ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(pub.Bytes()),
stackitem.NewBool(c.Registered),
stackitem.NewBigInteger(&c.Votes),
}))
if err != nil {
// Panic since it's a critical error that must abort execution.
panic(err)
}
}
return err
return nil
}
func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
@ -907,7 +981,8 @@ func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return stackitem.NewBool(err == nil)
}
// VoteInternal votes from account h for validarors specified in pubs.
// VoteInternal votes from account h for validarors specified in pubs. This method
// must not be called outside of VM since it panics on critical errors.
func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.PublicKey) error {
ok, err := runtime.CheckHashedWitness(ic, h)
if err != nil {
@ -969,12 +1044,16 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public
}
ic.DAO.PutStorageItem(n.ID, key, acc.Bytes(ic.DAO.GetItemCtx()))
ic.AddNotification(n.Hash, "Vote", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(n.Hash, "Vote", stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(h.BytesBE()),
keyToStackItem(oldVote),
keyToStackItem(pub),
stackitem.NewBigInteger(&acc.Balance),
}))
if err != nil {
// Panic since it's a critical error that must abort execution.
panic(err)
}
if newGas != nil { // Can be if it was already distributed in the same block.
n.GAS.mint(ic, h, newGas, true)

View file

@ -45,9 +45,12 @@ func (c *nep17TokenNative) Metadata() *interop.ContractMD {
return &c.ContractMD
}
func newNEP17Native(name string, id int32) *nep17TokenNative {
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id, func(m *manifest.Manifest) {
func newNEP17Native(name string, id int32, onManifestConstruction func(m *manifest.Manifest, hf config.Hardfork)) *nep17TokenNative {
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id, func(m *manifest.Manifest, hf config.Hardfork) {
m.SupportedStandards = []string{manifest.NEP17StandardName}
if onManifestConstruction != nil {
onManifestConstruction(m, hf)
}
})}
desc := newDescriptor("symbol", smartcontract.StringType)
@ -143,7 +146,11 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint
}
}
}()
c.emitTransfer(ic, from, to, amount)
err := c.emitTransfer(ic, from, to, amount)
if err != nil {
skipPostCalls = true
panic(err)
}
if to == nil || !callOnPayment {
return
}
@ -167,8 +174,8 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint
}
}
func (c *nep17TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) {
ic.AddNotification(c.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{
func (c *nep17TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) error {
return ic.AddNotification(c.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{
addrToStackItem(from),
addrToStackItem(to),
stackitem.NewBigInteger(amount),

View file

@ -117,20 +117,33 @@ func testGetSetCache(t *testing.T, c *neotest.ContractInvoker, name string, defa
}
}
func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, nodes keys.PublicKeys) {
func setNodesByRole(t *testing.T, designateInvoker *neotest.ContractInvoker, ok bool, r noderoles.Role, nodes keys.PublicKeys, oldKeys ...keys.PublicKeys) {
pubs := make([]any, len(nodes))
for i := range nodes {
pubs[i] = nodes[i].Bytes()
}
if ok {
h := designateInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int64(r), pubs)
ntf := stackitem.NewArray([]stackitem.Item{
stackitem.Make(int64(r)),
stackitem.Make(designateInvoker.Chain.BlockHeight()),
})
if len(oldKeys) > 0 {
old := stackitem.NewArray([]stackitem.Item{})
for _, key := range oldKeys[0] {
old.Append(stackitem.NewByteArray(key.Bytes()))
}
newKeys := stackitem.NewArray([]stackitem.Item{})
for _, key := range nodes {
newKeys.Append(stackitem.NewByteArray(key.Bytes()))
}
ntf.Append(old)
ntf.Append(newKeys)
}
designateInvoker.CheckTxNotificationEvent(t, h, 0, state.NotificationEvent{
ScriptHash: designateInvoker.Hash,
Name: native.DesignationEventName,
Item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(int64(r)),
stackitem.Make(designateInvoker.Chain.BlockHeight()),
}),
Item: ntf,
})
} else {
designateInvoker.InvokeFail(t, "", "designateAsRole", int64(r), pubs)

View file

@ -56,6 +56,29 @@ func TestDesignate_DesignateAsRole(t *testing.T) {
})
}
func TestDesignate_DesignateAsRole_Echidna(t *testing.T) {
c := newCustomNativeClient(t, nativenames.Designation, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{
config.HFEchidna.String(): 3,
}
})
designateInvoker := c.WithSigners(c.Committee)
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
oldPubs := keys.PublicKeys{priv.PublicKey()}
checkNodeRoles(t, designateInvoker, true, noderoles.Oracle, 0, keys.PublicKeys{}) // BlockHeight is 1.
setNodesByRole(t, designateInvoker, true, noderoles.Oracle, oldPubs) // BlockHeight is 2.
checkNodeRoles(t, designateInvoker, true, noderoles.Oracle, designateInvoker.Chain.BlockHeight()+1, oldPubs) // BlockHeight is 3.
priv, err = keys.NewPrivateKey()
require.NoError(t, err)
pubs := keys.PublicKeys{priv.PublicKey()}
setNodesByRole(t, designateInvoker, true, noderoles.Oracle, pubs, oldPubs) // BlockHeight is 4.
checkNodeRoles(t, designateInvoker, true, noderoles.Oracle, designateInvoker.Chain.BlockHeight()+1, pubs) // BlockHeight is 5.
}
type dummyOracle struct {
updateNodes func(k keys.PublicKeys)
}

View file

@ -48,7 +48,7 @@ var (
nativenames.Policy: `{"id":-7,"hash":"0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"PolicyContract","abi":{"methods":[{"name":"blockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false},{"name":"getAttributeFee","offset":7,"parameters":[{"name":"attributeType","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"getExecFeeFactor","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"getFeePerByte","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"getStoragePrice","offset":28,"parameters":[],"returntype":"Integer","safe":true},{"name":"isBlocked","offset":35,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":true},{"name":"setAttributeFee","offset":42,"parameters":[{"name":"attributeType","type":"Integer"},{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setExecFeeFactor","offset":49,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setFeePerByte","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setStoragePrice","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"unblockAccount","offset":70,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Oracle: `{"id":-9,"hash":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"OracleContract","abi":{"methods":[{"name":"finish","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"getPrice","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"request","offset":14,"parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setPrice","offset":21,"parameters":[{"name":"price","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":28,"parameters":[],"returntype":"Boolean","safe":true}],"events":[{"name":"OracleRequest","parameters":[{"name":"Id","type":"Integer"},{"name":"RequestContract","type":"Hash160"},{"name":"Url","type":"String"},{"name":"Filter","type":"String"}]},{"name":"OracleResponse","parameters":[{"name":"Id","type":"Integer"},{"name":"OriginalTx","type":"Hash256"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Notary: `{"id":-10,"hash":"0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"Notary","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"expirationOf","offset":7,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"getMaxNotValidBeforeDelta","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"lockDepositUntil","offset":21,"parameters":[{"name":"address","type":"Hash160"},{"name":"till","type":"Integer"}],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"setMaxNotValidBeforeDelta","offset":35,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":42,"parameters":[{"name":"signature","type":"Signature"}],"returntype":"Boolean","safe":true},{"name":"withdraw","offset":49,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Notary: `{"id":-10,"hash":"0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"Notary","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"expirationOf","offset":7,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"getMaxNotValidBeforeDelta","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"lockDepositUntil","offset":21,"parameters":[{"name":"address","type":"Hash160"},{"name":"till","type":"Integer"}],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"setMaxNotValidBeforeDelta","offset":35,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":42,"parameters":[{"name":"signature","type":"Signature"}],"returntype":"Boolean","safe":true},{"name":"withdraw","offset":49,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-27"],"trusts":[],"extra":null},"updatecounter":0}`,
}
// cockatriceCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
// under assumption that hardforks from Aspidochelone to Cockatrice (included) are enabled.
@ -56,6 +56,11 @@ var (
nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"keccak256","offset":42,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":49,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":70,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1325686241},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":77,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":84,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":91,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":98,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":105,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":112,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":119,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":126,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":133,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
}
echidnaCSS = map[string]string{
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"onNEP17Payment","offset":77,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"registerCandidate","offset":84,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":91,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":98,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":105,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":112,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":119,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":126,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":133,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":140,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17","NEP-27"],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.StdLib: `{"id":-2,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2681632925},"manifest":{"name":"StdLib","abi":{"methods":[{"name":"atoi","offset":0,"parameters":[{"name":"value","type":"String"}],"returntype":"Integer","safe":true},{"name":"atoi","offset":7,"parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"base58CheckDecode","offset":14,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58CheckEncode","offset":21,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base58Decode","offset":28,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58Encode","offset":35,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base64Decode","offset":42,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base64Encode","offset":49,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base64UrlDecode","offset":56,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base64UrlEncode","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"deserialize","offset":70,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"itoa","offset":77,"parameters":[{"name":"value","type":"Integer"}],"returntype":"String","safe":true},{"name":"itoa","offset":84,"parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","safe":true},{"name":"jsonDeserialize","offset":91,"parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"jsonSerialize","offset":98,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"memoryCompare","offset":105,"parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":112,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":119,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":126,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","safe":true},{"name":"serialize","offset":133,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"strLen","offset":140,"parameters":[{"name":"str","type":"String"}],"returntype":"Integer","safe":true},{"name":"stringSplit","offset":147,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","safe":true},{"name":"stringSplit","offset":154,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"},{"name":"Old","type":"Array"},{"name":"New","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
}
)
func init() {
@ -63,6 +68,9 @@ func init() {
if _, ok := cockatriceCSS[k]; !ok {
cockatriceCSS[k] = v
}
if _, ok := echidnaCSS[k]; !ok {
echidnaCSS[k] = cockatriceCSS[k]
}
}
}
@ -114,6 +122,7 @@ func TestManagement_GenesisNativeState(t *testing.T) {
config.HFAspidochelone.String(): 100500,
config.HFBasilisk.String(): 100500,
config.HFCockatrice.String(): 100500,
config.HFEchidna.String(): 100500,
}
cfg.P2PSigExtensions = true
})
@ -149,6 +158,19 @@ func TestManagement_GenesisNativeState(t *testing.T) {
})
check(t, mgmt, cockatriceCSS)
})
t.Run("Echidna enabled", func(t *testing.T) {
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): 0,
config.HFEchidna.String(): 0,
}
cfg.P2PSigExtensions = true
})
check(t, mgmt, echidnaCSS)
})
}
func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {

View file

@ -740,6 +740,13 @@ func TestNEO_CalculateBonus(t *testing.T) {
accH := acc.Signers[0].ScriptHash()
rewardDistance := 10
// We have 11 blocks made by transactions above and we need block 13 to get Echidna.
// Otherwise this happens:
// logger.go:146: 2024-12-24T17:52:18.160+0300 WARN contract invocation failed {"tx": "603d1b0e29e7aaf50513689be9d5bb946c7f7fec8836f0e90897c825fb762c13", "block": 13, "error": "at instruction 120 (SYSCALL): System.Contract.CallNative failed: gas limit exceeded"}
for range 3 {
e.AddNewBlock(t)
}
t.Run("Zero", func(t *testing.T) {
initialGASBalance := e.Chain.GetUtilityTokenBalance(accH)
for range rewardDistance {
@ -900,3 +907,86 @@ func TestNEO_GetCandidates(t *testing.T) {
neoCommitteeInvoker.Invoke(t, expected, "getCandidates")
checkGetAllCandidates(t, expected)
}
func TestNEO_RegisterViaNEP27(t *testing.T) {
neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000)
neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)
e := neoCommitteeInvoker.Executor
neoHash := e.NativeHash(t, nativenames.Neo)
cfg := e.Chain.GetConfig()
candidatesCount := cfg.GetCommitteeSize(0) - 1
// Register a set of candidates and vote for them.
voters := make([]neotest.Signer, candidatesCount)
candidates := make([]neotest.Signer, candidatesCount)
for i := range candidatesCount {
voters[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration
candidates[i] = e.NewAccount(t, 2000_0000_0000)
}
stack, err := neoCommitteeInvoker.TestInvoke(t, "getRegisterPrice")
require.NoError(t, err)
registrationPrice, err := stack.Pop().Item().TryInteger()
require.NoError(t, err)
// We have 11 blocks made by transactions above and we need block 13 to get Echidna.
for range 3 {
e.AddNewBlock(t)
}
gasValidatorsInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Gas))
txes := make([]*transaction.Transaction, 0, candidatesCount*3)
for i := range candidatesCount {
transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(candidatesCount+1-i)*1000000, nil)
txes = append(txes, transferTx)
registerTx := gasValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "transfer", candidates[i].(neotest.SingleSigner).Account().ScriptHash(), neoHash, registrationPrice, candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, registerTx)
voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, voteTx)
}
neoValidatorsInvoker.AddNewBlock(t, txes...)
for _, tx := range txes {
e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer` and `vote` return boolean values
}
// Ensure NEO holds no GAS.
stack, err = gasValidatorsInvoker.TestInvoke(t, "balanceOf", neoHash)
require.NoError(t, err)
balance, err := stack.Pop().Item().TryInteger()
require.NoError(t, err)
require.Equal(t, 0, balance.Sign())
var expected = make([]stackitem.Item, candidatesCount)
for i := range expected {
pub := candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()
v := stackitem.NewBigInteger(big.NewInt(int64(candidatesCount-i+1) * 1000000))
expected[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(pub),
v,
})
neoCommitteeInvoker.Invoke(t, v, "getCandidateVote", pub)
}
slices.SortFunc(expected, func(a, b stackitem.Item) int {
return bytes.Compare(a.Value().([]stackitem.Item)[0].Value().([]byte), b.Value().([]stackitem.Item)[0].Value().([]byte))
})
neoCommitteeInvoker.Invoke(t, stackitem.NewArray(expected), "getCandidates")
// Invalid cases.
var newCand = voters[0]
// Missing data.
gasValidatorsInvoker.WithSigners(newCand).InvokeFail(t, "invalid conversion", "transfer", newCand.(neotest.SingleSigner).Account().ScriptHash(), neoHash, registrationPrice, nil)
// Invalid data.
gasValidatorsInvoker.WithSigners(newCand).InvokeFail(t, "unexpected EOF", "transfer", newCand.(neotest.SingleSigner).Account().ScriptHash(), neoHash, registrationPrice, []byte{2, 2, 2})
// NEO transfer.
neoValidatorsInvoker.WithSigners(newCand).InvokeFail(t, "only GAS is accepted", "transfer", newCand.(neotest.SingleSigner).Account().ScriptHash(), neoHash, 1, newCand.(neotest.SingleSigner).Account().PublicKey().Bytes())
// Incorrect amount.
gasValidatorsInvoker.WithSigners(newCand).InvokeFail(t, "incorrect GAS amount", "transfer", newCand.(neotest.SingleSigner).Account().ScriptHash(), neoHash, 1, newCand.(neotest.SingleSigner).Account().PublicKey().Bytes())
// Incorrect witness.
var anotherAcc = e.NewAccount(t, 2000_0000_0000)
gasValidatorsInvoker.WithSigners(newCand).InvokeFail(t, "not witnessed by the key owner", "transfer", newCand.(neotest.SingleSigner).Account().ScriptHash(), neoHash, registrationPrice, anotherAcc.(neotest.SingleSigner).Account().PublicKey().Bytes())
}

View file

@ -72,7 +72,9 @@ func copyNotaryCache(src, dst *NotaryCache) {
// newNotary returns Notary native contract.
func newNotary() *Notary {
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID, func(m *manifest.Manifest, hf config.Hardfork) {
m.SupportedStandards = []string{manifest.NEP17Payable}
})}
defer n.BuildHFSpecificMD(n.ActiveIn())
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,

View file

@ -316,10 +316,13 @@ func (o *Oracle) FinishInternal(ic *interop.Context) error {
if err != nil {
return ErrRequestNotFound
}
ic.AddNotification(o.Hash, "OracleResponse", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(o.Hash, "OracleResponse", stackitem.NewArray([]stackitem.Item{
stackitem.Make(resp.ID),
stackitem.Make(req.OriginalTxID.BytesBE()),
}))
if err != nil {
return err
}
origTx, _, err := ic.DAO.GetTransaction(req.OriginalTxID)
if err != nil {
@ -422,12 +425,15 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string
} else {
filterNotif = stackitem.Null{}
}
ic.AddNotification(o.Hash, "OracleRequest", stackitem.NewArray([]stackitem.Item{
err = ic.AddNotification(o.Hash, "OracleRequest", stackitem.NewArray([]stackitem.Item{
stackitem.Make(id),
stackitem.Make(ic.VM.GetCallingScriptHash().BytesBE()),
stackitem.Make(url),
filterNotif,
}))
if err != nil {
return err
}
req := &state.OracleRequest{
OriginalTxID: o.getOriginalTxID(ic.DAO, ic.Tx),
GasForResponse: gas.Uint64(),

View file

@ -100,6 +100,16 @@ func newStd() *Std {
md = newMethodAndPrice(s.base64Decode, 1<<5, callflag.NoneFlag)
s.AddMethod(md, desc)
desc = newDescriptor("base64UrlEncode", smartcontract.StringType,
manifest.NewParameter("data", smartcontract.ByteArrayType))
md = newMethodAndPrice(s.base64UrlEncode, 1<<5, callflag.NoneFlag, config.HFEchidna)
s.AddMethod(md, desc)
desc = newDescriptor("base64UrlDecode", smartcontract.ByteArrayType,
manifest.NewParameter("s", smartcontract.StringType))
md = newMethodAndPrice(s.base64UrlDecode, 1<<5, callflag.NoneFlag, config.HFEchidna)
s.AddMethod(md, desc)
desc = newDescriptor("base58Encode", smartcontract.StringType,
manifest.NewParameter("data", smartcontract.ByteArrayType))
md = newMethodAndPrice(s.base58Encode, 1<<13, callflag.NoneFlag)
@ -314,6 +324,23 @@ func (s *Std) base64Decode(_ *interop.Context, args []stackitem.Item) stackitem.
return stackitem.NewByteArray(result)
}
func (s *Std) base64UrlEncode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
src := s.toLimitedBytes(args[0])
result := base64.URLEncoding.EncodeToString(src)
return stackitem.NewByteArray([]byte(result))
}
func (s *Std) base64UrlDecode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
src := s.toLimitedString(args[0])
result, err := base64.URLEncoding.DecodeString(src)
if err != nil {
panic(err)
}
return stackitem.NewByteArray(result)
}
func (s *Std) base58Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item {
src := s.toLimitedBytes(args[0])
result := base58.Encode(src)

View file

@ -170,7 +170,10 @@ func TestStdLibJSON(t *testing.T) {
func TestStdLibEncodeDecode(t *testing.T) {
s := newStd()
original := []byte("my pretty string")
// Source C# implementation: https://github.com/neo-project/neo/blob/216c39eddd03de2cb1f6f8aa5b3c19be60606f27/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs#s#L417-L418
originalUrl := []byte("Subject=test@example.com&Issuer=https://example.com")
encoded64 := base64.StdEncoding.EncodeToString(original)
encoded64Url := "U3ViamVjdD10ZXN0QGV4YW1wbGUuY29tJklzc3Vlcj1odHRwczovL2V4YW1wbGUuY29t"
encoded58 := base58.Encode(original)
encoded58Check := base58neogo.CheckEncode(original)
ic := &interop.Context{VM: vm.New()}
@ -188,6 +191,16 @@ func TestStdLibEncodeDecode(t *testing.T) {
require.PanicsWithError(t, ErrTooBigInput.Error(),
func() { s.base64Encode(ic, bigInputArgs) })
})
t.Run("Encode64Url", func(t *testing.T) {
require.NotPanics(t, func() {
actual = s.base64UrlEncode(ic, []stackitem.Item{stackitem.Make(originalUrl)})
})
require.Equal(t, stackitem.Make(encoded64Url), actual)
})
t.Run("Encode64Url/error", func(t *testing.T) {
require.PanicsWithError(t, ErrTooBigInput.Error(),
func() { s.base64UrlEncode(ic, bigInputArgs) })
})
t.Run("Encode58", func(t *testing.T) {
require.NotPanics(t, func() {
actual = s.base58Encode(ic, []stackitem.Item{stackitem.Make(original)})
@ -224,6 +237,22 @@ func TestStdLibEncodeDecode(t *testing.T) {
require.PanicsWithError(t, ErrTooBigInput.Error(),
func() { s.base64Decode(ic, bigInputArgs) })
})
t.Run("Decode64Url/positive", func(t *testing.T) {
require.NotPanics(t, func() {
actual = s.base64UrlDecode(ic, []stackitem.Item{stackitem.Make(encoded64Url)})
})
require.Equal(t, stackitem.Make(originalUrl), actual)
})
t.Run("Decode64Url/error", func(t *testing.T) {
require.Panics(t, func() {
_ = s.base64UrlDecode(ic, []stackitem.Item{stackitem.Make(encoded64Url + "%")})
})
require.Panics(t, func() {
_ = s.base64UrlDecode(ic, []stackitem.Item{stackitem.NewInterop(nil)})
})
require.PanicsWithError(t, ErrTooBigInput.Error(),
func() { s.base64UrlDecode(ic, bigInputArgs) })
})
t.Run("Decode58/positive", func(t *testing.T) {
require.NotPanics(t, func() {
actual = s.base58Decode(ic, []stackitem.Item{stackitem.Make(encoded58)})

View file

@ -22,6 +22,14 @@ var (
Namespace: "neogo",
},
)
// estimatedPersistVelocity prometheus metric.
estimatedPersistVelocity = prometheus.NewGauge(
prometheus.GaugeOpts{
Help: "Estimation of persist velocity per cycle (1s by default)",
Name: "estimated_persist_velocity",
Namespace: "neogo",
},
)
// headerHeight prometheus metric.
headerHeight = prometheus.NewGauge(
prometheus.GaugeOpts{
@ -44,6 +52,7 @@ func init() {
prometheus.MustRegister(
blockHeight,
persistedHeight,
estimatedPersistVelocity,
headerHeight,
mempoolUnsortedTx,
)
@ -53,6 +62,10 @@ func updatePersistedHeightMetric(pHeight uint32) {
persistedHeight.Set(float64(pHeight))
}
func updateEstimatedPersistVelocityMetric(v uint32) {
estimatedPersistVelocity.Set(float64(v))
}
func updateHeaderHeightMetric(hHeight uint32) {
headerHeight.Set(float64(hHeight))
}

View file

@ -0,0 +1,120 @@
package state
import (
"encoding/json"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// ContractInvocation contains method call information.
// The Arguments field will be nil if serialization of the arguments exceeds the predefined limit
// of [stackitem.MaxSerialized] (for security reasons). In that case Truncated will be set to true.
type ContractInvocation struct {
Hash util.Uint160 `json:"contract"`
Method string `json:"method"`
// Arguments are the arguments as passed to the `args` parameter of System.Contract.Call
// for use in the RPC Server and RPC Client.
Arguments *stackitem.Array `json:"arguments"`
// argumentsBytes is the serialized arguments used at the interop level.
argumentsBytes []byte
ArgumentsCount uint32 `json:"argumentscount"`
Truncated bool `json:"truncated"`
}
// contractInvocationAux is an auxiliary struct for ContractInvocation JSON marshalling.
type contractInvocationAux struct {
Hash util.Uint160 `json:"hash"`
Method string `json:"method"`
Arguments json.RawMessage `json:"arguments,omitempty"`
ArgumentsCount uint32 `json:"argumentscount"`
Truncated bool `json:"truncated"`
}
// NewContractInvocation returns a new ContractInvocation.
func NewContractInvocation(hash util.Uint160, method string, argBytes []byte, argCnt uint32) *ContractInvocation {
return &ContractInvocation{
Hash: hash,
Method: method,
argumentsBytes: argBytes,
ArgumentsCount: argCnt,
Truncated: argBytes == nil,
}
}
// DecodeBinary implements the Serializable interface.
func (ci *ContractInvocation) DecodeBinary(r *io.BinReader) {
ci.Hash.DecodeBinary(r)
ci.Method = r.ReadString()
ci.ArgumentsCount = r.ReadU32LE()
ci.Truncated = r.ReadBool()
if !ci.Truncated {
ci.argumentsBytes = r.ReadVarBytes()
}
}
// EncodeBinary implements the Serializable interface.
func (ci *ContractInvocation) EncodeBinary(w *io.BinWriter) {
ci.EncodeBinaryWithContext(w, stackitem.NewSerializationContext())
}
// EncodeBinaryWithContext is the same as EncodeBinary, but allows to efficiently reuse
// stack item serialization context.
func (ci *ContractInvocation) EncodeBinaryWithContext(w *io.BinWriter, sc *stackitem.SerializationContext) {
ci.Hash.EncodeBinary(w)
w.WriteString(ci.Method)
w.WriteU32LE(ci.ArgumentsCount)
w.WriteBool(ci.Truncated)
if !ci.Truncated {
w.WriteVarBytes(ci.argumentsBytes)
}
}
// MarshalJSON implements the json.Marshaler interface.
func (ci ContractInvocation) MarshalJSON() ([]byte, error) {
var item []byte
if ci.Arguments == nil && ci.argumentsBytes != nil {
si, err := stackitem.Deserialize(ci.argumentsBytes)
if err != nil {
return nil, err
}
item, err = stackitem.ToJSONWithTypes(si.(*stackitem.Array))
if err != nil {
item = nil
}
}
return json.Marshal(contractInvocationAux{
Hash: ci.Hash,
Method: ci.Method,
Arguments: item,
ArgumentsCount: ci.ArgumentsCount,
Truncated: ci.Truncated,
})
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (ci *ContractInvocation) UnmarshalJSON(data []byte) error {
aux := new(contractInvocationAux)
if err := json.Unmarshal(data, aux); err != nil {
return err
}
var args *stackitem.Array
if aux.Arguments != nil {
arguments, err := stackitem.FromJSONWithTypes(aux.Arguments)
if err != nil {
return err
}
if t := arguments.Type(); t != stackitem.ArrayT {
return fmt.Errorf("failed to convert invocation state of type %s to array", t.String())
}
args = arguments.(*stackitem.Array)
}
ci.Method = aux.Method
ci.Hash = aux.Hash
ci.ArgumentsCount = aux.ArgumentsCount
ci.Truncated = aux.Truncated
ci.Arguments = args
return nil
}

View file

@ -0,0 +1,51 @@
package state
import (
"testing"
json "github.com/nspcc-dev/go-ordered-json"
"github.com/nspcc-dev/neo-go/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestContractInvocation_MarshalUnmarshalJSON(t *testing.T) {
t.Run("truncated", func(t *testing.T) {
ci := NewContractInvocation(util.Uint160{}, "fakeMethodCall", nil, 1)
testserdes.MarshalUnmarshalJSON(t, ci, new(ContractInvocation))
})
t.Run("not truncated", func(t *testing.T) {
si := stackitem.NewArray([]stackitem.Item{stackitem.NewBool(false)})
argBytes, err := stackitem.NewSerializationContext().Serialize(si, false)
require.NoError(t, err)
ci := NewContractInvocation(util.Uint160{}, "fakeMethodCall", argBytes, 1)
// Marshal and Unmarshal are asymmetric, test manually
out, err := json.Marshal(&ci)
require.NoError(t, err)
var ci2 ContractInvocation
err = json.Unmarshal(out, &ci2)
require.NoError(t, err)
require.Equal(t, ci.Hash, ci2.Hash)
require.Equal(t, ci.Method, ci2.Method)
require.Equal(t, ci.Truncated, ci2.Truncated)
require.Equal(t, ci.ArgumentsCount, ci2.ArgumentsCount)
require.Equal(t, si, ci2.Arguments)
})
}
func TestContractInvocation_EncodeDecodeBinary(t *testing.T) {
t.Run("truncated", func(t *testing.T) {
ci := NewContractInvocation(util.Uint160{}, "fakeMethodCall", nil, 1)
testserdes.EncodeDecodeBinary(t, ci, new(ContractInvocation))
})
t.Run("not truncated", func(t *testing.T) {
si := stackitem.NewArray([]stackitem.Item{stackitem.NewBool(false)})
argBytes, err := stackitem.NewSerializationContext().Serialize(si, false)
require.NoError(t, err)
ci := NewContractInvocation(util.Uint160{}, "fakeMethodCall", argBytes, 1)
testserdes.EncodeDecodeBinary(t, ci, new(ContractInvocation))
})
}

View file

@ -12,6 +12,18 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
)
const (
// saveInvocationsBit acts as a marker using the VMState to indicate whether contract
// 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/nspcc-dev/neo-go/pull/3569#discussion_r1909357541
saveInvocationsBit = 0x80
// cleanSaveInvocationsBitMask is used to remove the save invocations marker bit from
// the VMState.
cleanSaveInvocationsBitMask = saveInvocationsBit ^ 0xFF
)
// NotificationEvent is a tuple of the scripthash that has emitted the Item as a
// notification and the item itself.
type NotificationEvent struct {
@ -78,6 +90,10 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) {
func (aer *AppExecResult) EncodeBinaryWithContext(w *io.BinWriter, sc *stackitem.SerializationContext) {
w.WriteBytes(aer.Container[:])
w.WriteB(byte(aer.Trigger))
invocLen := len(aer.Invocations)
if invocLen > 0 {
aer.VMState |= saveInvocationsBit
}
w.WriteB(byte(aer.VMState))
w.WriteU64LE(uint64(aer.GasConsumed))
// Stack items are expected to be marshaled one by one.
@ -95,6 +111,12 @@ func (aer *AppExecResult) EncodeBinaryWithContext(w *io.BinWriter, sc *stackitem
aer.Events[i].EncodeBinaryWithContext(w, sc)
}
w.WriteVarBytes([]byte(aer.FaultException))
if invocLen > 0 {
w.WriteVarUint(uint64(invocLen))
for i := range aer.Invocations {
aer.Invocations[i].EncodeBinaryWithContext(w, sc)
}
}
}
// DecodeBinary implements the Serializable interface.
@ -120,6 +142,10 @@ func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
aer.Stack = arr
r.ReadArray(&aer.Events)
aer.FaultException = r.ReadString()
if aer.VMState&saveInvocationsBit != 0 {
r.ReadArray(&aer.Invocations)
aer.VMState &= cleanSaveInvocationsBitMask
}
}
// notificationEventAux is an auxiliary struct for NotificationEvent JSON marshalling.
@ -209,16 +235,18 @@ type Execution struct {
Stack []stackitem.Item
Events []NotificationEvent
FaultException string
Invocations []ContractInvocation
}
// executionAux represents an auxiliary struct for Execution JSON marshalling.
type executionAux struct {
Trigger string `json:"trigger"`
VMState string `json:"vmstate"`
GasConsumed int64 `json:"gasconsumed,string"`
Stack json.RawMessage `json:"stack"`
Events []NotificationEvent `json:"notifications"`
FaultException *string `json:"exception"`
Trigger string `json:"trigger"`
VMState string `json:"vmstate"`
GasConsumed int64 `json:"gasconsumed,string"`
Stack json.RawMessage `json:"stack"`
Events []NotificationEvent `json:"notifications"`
FaultException *string `json:"exception"`
Invocations []ContractInvocation `json:"invocations"`
}
// MarshalJSON implements the json.Marshaler interface.
@ -246,6 +274,7 @@ func (e Execution) MarshalJSON() ([]byte, error) {
Stack: st,
Events: e.Events,
FaultException: exception,
Invocations: e.Invocations,
})
}
@ -287,6 +316,7 @@ func (e *Execution) UnmarshalJSON(data []byte) error {
if aux.FaultException != nil {
e.FaultException = *aux.FaultException
}
e.Invocations = aux.Invocations
return nil
}

View file

@ -93,11 +93,15 @@ type Module struct {
billet *mpt.Billet
jumpCallback func(p uint32) error
// stageChangedCallback is an optional callback that is triggered whenever
// the sync stage changes.
stageChangedCallback func()
}
// NewModule returns new instance of statesync module.
func NewModule(bc Ledger, stateMod *stateroot.Module, log *zap.Logger, s *dao.Simple, jumpCallback func(p uint32) error) *Module {
if !(bc.GetConfig().P2PStateExchangeExtensions && bc.GetConfig().Ledger.RemoveUntraceableBlocks) {
if !(bc.GetConfig().P2PStateExchangeExtensions && bc.GetConfig().Ledger.RemoveUntraceableBlocks) && !bc.GetConfig().NeoFSStateSyncExtensions {
return &Module{
dao: s,
bc: bc,
@ -120,7 +124,13 @@ func NewModule(bc Ledger, stateMod *stateroot.Module, log *zap.Logger, s *dao.Si
// Init initializes state sync module for the current chain's height with given
// callback for MPT nodes requests.
func (s *Module) Init(currChainHeight uint32) error {
oldStage := s.syncStage
s.lock.Lock()
defer func() {
if s.syncStage != oldStage {
s.notifyStageChanged()
}
}()
defer s.lock.Unlock()
if s.syncStage != none {
@ -176,6 +186,20 @@ func (s *Module) Init(currChainHeight uint32) error {
return s.defineSyncStage()
}
// SetOnStageChanged sets callback that is triggered whenever the sync stage changes.
func (s *Module) SetOnStageChanged(cb func()) {
s.lock.Lock()
defer s.lock.Unlock()
s.stageChangedCallback = cb
}
// notifyStageChanged triggers stage change callback if it's set.
func (s *Module) notifyStageChanged() {
if s.stageChangedCallback != nil {
s.stageChangedCallback()
}
}
// TemporaryPrefix accepts current storage prefix and returns prefix
// to use for storing intermediate items during synchronization.
func TemporaryPrefix(currPrefix storage.KeyPrefix) storage.KeyPrefix {
@ -287,7 +311,13 @@ func (s *Module) getLatestSavedBlock(p uint32) uint32 {
// AddHeaders validates and adds specified headers to the chain.
func (s *Module) AddHeaders(hdrs ...*block.Header) error {
oldStage := s.syncStage
s.lock.Lock()
defer func() {
if s.syncStage != oldStage {
s.notifyStageChanged()
}
}()
defer s.lock.Unlock()
if s.syncStage != initialized {
@ -306,7 +336,13 @@ func (s *Module) AddHeaders(hdrs ...*block.Header) error {
// AddBlock verifies and saves block skipping executable scripts.
func (s *Module) AddBlock(block *block.Block) error {
oldStage := s.syncStage
s.lock.Lock()
defer func() {
if s.syncStage != oldStage {
s.notifyStageChanged()
}
}()
defer s.lock.Unlock()
if s.syncStage&headersSynced == 0 || s.syncStage&blocksSynced != 0 {
@ -359,7 +395,13 @@ func (s *Module) AddBlock(block *block.Block) error {
// AddMPTNodes tries to add provided set of MPT nodes to the MPT billet if they are
// not yet collected.
func (s *Module) AddMPTNodes(nodes [][]byte) error {
oldStage := s.syncStage
s.lock.Lock()
defer func() {
if s.syncStage != oldStage {
s.notifyStageChanged()
}
}()
defer s.lock.Unlock()
if s.syncStage&headersSynced == 0 || s.syncStage&mptSynced != 0 {
@ -425,6 +467,12 @@ func (s *Module) restoreNode(n mpt.Node) error {
// If so, then jumping to P state sync point occurs. It is not protected by lock, thus caller
// should take care of it.
func (s *Module) checkSyncIsCompleted() {
oldStage := s.syncStage
defer func() {
if s.syncStage != oldStage {
s.notifyStageChanged()
}
}()
if s.syncStage != headersSynced|mptSynced|blocksSynced {
return
}
@ -484,6 +532,14 @@ func (s *Module) NeedMPTNodes() bool {
return s.syncStage&headersSynced != 0 && s.syncStage&mptSynced == 0
}
// NeedBlocks returns whether the module hasn't completed blocks synchronisation.
func (s *Module) NeedBlocks() bool {
s.lock.RLock()
defer s.lock.RUnlock()
return s.syncStage&headersSynced != 0 && s.syncStage&blocksSynced == 0
}
// Traverse traverses local MPT nodes starting from the specified root down to its
// children calling `process` for each serialised node until stop condition is satisfied.
func (s *Module) Traverse(root util.Uint256, process func(node mpt.Node, nodeBytes []byte) bool) error {
@ -495,7 +551,7 @@ func (s *Module) Traverse(root util.Uint256, process func(node mpt.Node, nodeByt
if s.bc.GetConfig().Ledger.KeepOnlyLatestState || s.bc.GetConfig().Ledger.RemoveUntraceableBlocks {
mode |= mpt.ModeLatest
}
b := mpt.NewBillet(root, mode, 0, storage.NewMemCachedStore(s.dao.Store))
b := mpt.NewBillet(root, mode, mpt.DummySTTempStoragePrefix, storage.NewMemCachedStore(s.dao.Store))
return b.Traverse(func(pathToNode []byte, node mpt.Node, nodeBytes []byte) bool {
return process(node, nodeBytes)
}, false)
@ -508,3 +564,13 @@ func (s *Module) GetUnknownMPTNodesBatch(limit int) []util.Uint256 {
return s.mptpool.GetBatch(limit)
}
// HeaderHeight returns the height of the latest stored header.
func (s *Module) HeaderHeight() uint32 {
return s.bc.HeaderHeight()
}
// GetConfig returns current blockchain configuration.
func (s *Module) GetConfig() config.Blockchain {
return s.bc.GetConfig()
}

View file

@ -19,12 +19,14 @@ func TestMemCachedPutGetDelete(t *testing.T) {
s.Put(key, value)
require.Equal(t, 1, s.Len())
result, err := s.Get(key)
assert.Nil(t, err)
require.Equal(t, value, result)
s.Delete(key)
require.Equal(t, 1, s.Len()) // deletion marker
_, err = s.Get(key)
assert.NotNil(t, err)
assert.Equal(t, err, ErrKeyNotFound)

Some files were not shown because too many files have changed in this diff Show more