Compare commits

...

547 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
9189b3eb7d
Merge pull request #3753 from nspcc-dev/rel-0.107.2
CHANGELOG: release v0.107.2
2024-12-13 19:47:58 +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
Anna Shaleva
fec1ac8aee CHANGELOG: release v0.107.2
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-13 19:32:05 +03:00
Anna Shaleva
53ab704971 docs: add defaults to NeoFS BlockFetcher configuration
Follow #3742.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-13 19:32:04 +03:00
Anna Shaleva
3f651cde27
Merge pull request #3756 from nspcc-dev/neofs-sdk-update
cli: refactor `neofs-sdk-go` related operations in `upload-bin`
2024-12-13 19:30:09 +03:00
Ekaterina Pavlova
39cec7d0d6 cli: refactor neofs-sdk-go related operations in upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-13 19:19:15 +03:00
Anna Shaleva
388ee25cd6
Merge pull request #3725 from nspcc-dev/neofs-sdk-update
*: update `neofs-sdk-go`
2024-12-13 17:52:56 +03:00
Ekaterina Pavlova
9e3f75e977 *: update neofs-sdk-go
Update to the version without pool panics
https://github.com/nspcc-dev/neofs-sdk-go/pull/643.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-13 17:07:52 +03:00
Anna Shaleva
f82088b851
Merge pull request #3749 from nspcc-dev/timestamp
cli: change block timestamp attribute of objects in `upload-bin`
2024-12-13 17:00:14 +03:00
Ekaterina Pavlova
be4fc98041 cli: refactor upload-bin handler to reuse SDK built-in functionality
Close #3714

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-13 16:36:37 +03:00
Ekaterina Pavlova
247ee831e5 cli: change block timestamp attribute of objects in upload-bin
Close #3654

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-13 16:35:14 +03:00
Anna Shaleva
e0c8bebd05
Merge pull request #3751 from nspcc-dev/debug-upload-bin
cli: add debug on retry in `upload-bin`
2024-12-13 16:33:43 +03:00
Anna Shaleva
9834b83cf0
Merge pull request #3750 from nspcc-dev/removeuntraceableheaders
RemoveUntraceableHeaders
2024-12-13 16:30:31 +03:00
Roman Khimov
90b6a42331 network: make GetHeaders best-effort
Be a nice neighbor, try to reply with whatever headers we have, don't fail
completely because of a single missing header.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-13 16:09:00 +03:00
Roman Khimov
9599fba24f core: fix removing old transfer data with RemoveUntraceableHeaders
Transfer data is timestamp-based, previously it always had and used headers,
no we can go via a small cache (we don't want it to grow or be stored forever).
Otherwise it's unable to do the job:

    2024-12-13T12:55:15.056+0300    ERROR   failed to find block header for transfer GC     {"time": "19.066µs", "error": "key not found"}

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-13 16:09:00 +03:00
Roman Khimov
7d89a53043 core: drop redundant tgtBlock normalization
It's there since 423c7883b8, but looks like it
never changed anything, the same thing is done six lines above and tgtBlock is
not changed since.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-13 16:09:00 +03:00
Roman Khimov
c53b0645bb core: extend NewBlockchain coverage a bit
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-13 16:09:00 +03:00
Roman Khimov
c7f5f173ae core: introduce RemoveUntraceableHeaders
With blocks available from NeoFS we can drop them from the local DB.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-13 16:09:00 +03:00
Ekaterina Pavlova
46cbfab264 cli: add more debug info about retry to the upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-13 13:52:33 +03:00
Anna Shaleva
38f635bce0
Merge pull request #3742 from nspcc-dev/default-blockfetcher-config
services: add default values for NeoFSBlockFetcher configuration
2024-12-13 12:17:56 +03:00
Ekaterina Pavlova
65bdc82da8 *: move constant and NeoFS related code into separate package
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-13 11:43:19 +03:00
Ekaterina Pavlova
c79ffa967f services: add default values for NeoFSBlockFetcher configuration
The minimum sufficient configuration is Addresses and ContainerID,
example:
```
  NeoFSBlockFetcher:
    Enabled: true
    Addresses:
      - st1.storage.fs.neo.org:8080
      - st2.storage.fs.neo.org:8080
      - st3.storage.fs.neo.org:8080
      - st4.storage.fs.neo.org:8080
    ContainerID: "87JRc7vyWcjW8uS32LMoLTAj4ckCzFZWfKbacjU3sAob"
```

Close #3718

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-13 11:39:22 +03:00
Anna Shaleva
283ca5cb6c
Merge pull request #3748 from nspcc-dev/fix-gomod
go.mod: tidy
2024-12-12 16:41:57 +03:00
Anna Shaleva
3c372de570 *: update interop deps
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-12 16:08:32 +03:00
Anna Shaleva
cb4b21fcf4
Merge pull request #3735 from nspcc-dev/index-files-put
cli: update `upload-bin` to create index files during block uploading
2024-12-12 16:08:05 +03:00
Anna Shaleva
ea0a6114d2 go.mod: tidy
Fix linter after #3746 and #3747:
```
 Running [/home/runner/golangci-lint-1.62.2-linux-amd64/golangci-lint run --path-prefix=scripts --config=/home/runner/work/neo-go/neo-go/nspcc-gh/.golangci.yml] in [/home/runner/work/neo-go/neo-go/scripts] ...
  level=error msg="Running error: context loading failed: no go files to analyze: running `go mod tidy` may solve the problem"

  Error: golangci-lint exit with code 5
```

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-12 16:07:05 +03:00
Roman Khimov
15cc559c73
Merge pull request #3747 from nspcc-dev/dependabot/go_modules/examples/nft-nd-nns/golang.org/x/crypto-0.31.0
build(deps): bump golang.org/x/crypto from 0.26.0 to 0.31.0 in /examples/nft-nd-nns
2024-12-12 12:38:07 +03:00
dependabot[bot]
1447158f2b
build(deps): bump golang.org/x/crypto in /examples/nft-nd-nns
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 09:16:57 +00:00
Ekaterina Pavlova
62615f8c7e cli: update upload-bin to create index files during block uploading
Close #3655
Close #3652

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-12 12:08:49 +03:00
Roman Khimov
e530ba0791
Merge pull request #3746 from nspcc-dev/dependabot/go_modules/golang.org/x/crypto-0.31.0
build(deps): bump golang.org/x/crypto from 0.26.0 to 0.31.0
2024-12-12 12:04:39 +03:00
dependabot[bot]
b058b6cf21
build(deps): bump golang.org/x/crypto from 0.26.0 to 0.31.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 09:00:01 +00:00
Anna Shaleva
6d1eea307b
Merge pull request #3696 from nspcc-dev/basic-chain-tests
basicchain: use UnitTestNet default config for generation
2024-12-12 10:51:42 +03:00
Ekaterina Pavlova
e993c1bdac basicchain: use UnitTestNet default config
It's important to have the same chain configuration for all tests.
Otherwise, a mismatched hardfork configuration is used to dump/restore
the basic chain.

Close #3681

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-11 21:42:00 +03:00
Anna Shaleva
4fa5fdc39c
Merge pull request #3715 from nspcc-dev/sub-test
rpcsrv: fix `TestFilteredSubscriptions`
2024-12-11 20:50:52 +03:00
Ekaterina Pavlova
0bd378770d rpcsrv: fix TestFilteredSubscriptions
Close #3693

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-11 14:18:23 +03:00
Roman Khimov
727262a95a
Merge pull request #3737 from nspcc-dev/fix-timer-drain
services: fix timer draining
2024-12-09 15:06:30 +03:00
Anna Shaleva
827acfca23 services: fix timer draining
Ref. #3736.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-09 12:11:01 +03:00
Anna Shaleva
35c2c5cc0e
Merge pull request #3736 from nspcc-dev/retry
services: fix defer function in `retry` of NeoFSBlockFetcher
2024-12-09 12:08:45 +03:00
Ekaterina Pavlova
3010324c4f services: fix defer function in retry of NeoFSBlockFetcher
Drain the channel only if it has pending events. This avoids trying to
read from an already-empty channel.

Close #3734

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-12-08 00:04:14 +03:00
Anna Shaleva
5f92da21fa
Merge pull request #3731 from nspcc-dev/rel-0.107.1
CHANGELOG: release 0.107.1
2024-12-06 16:48:31 +03:00
Anna Shaleva
703e066acc CHANGELOG: release 0.107.1
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-06 16:26:02 +03:00
Anna Shaleva
c84dac501d
Merge pull request #3730 from nspcc-dev/tune-getaddr-trigger
network: take into account good known peers when thinking of GetAddr
2024-12-06 15:55:32 +03:00
Roman Khimov
703bf6c458 network: take into account good known peers when thinking of GetAddr
They will be returned to pool when disconnected anyway. On a smaller network
this can make a difference because there are not a lot of addresses in the
pool usually.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-06 15:31:33 +03:00
Anna Shaleva
2bc41dbe30
Merge pull request #3724 from nspcc-dev/hf-latest-stable
Latest stable hardforks
2024-12-06 13:18:03 +03:00
Anna Shaleva
a68856c27c
Merge pull request #3728 from nspcc-dev/blockfetcher-close
Fix blockfetcher closing
2024-12-06 13:07:19 +03:00
Roman Khimov
5f09e00e44 docs: introduce Echidna hardfork
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-06 12:24:00 +03:00
Roman Khimov
fc705433a3 docs: move Hardforks into a section of their own
It's impossible to maintain a 4K line. I'm not even sure new table works good
enough, but at least it's per-hardfork.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-06 12:24:00 +03:00
Roman Khimov
a164db92cc config: replace LatestHardfork() with HFLatestStable
Function doesn't make much sense here. The change is rather trivial and this
is not expected to be used by external code, so no deprecation.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-06 12:24:00 +03:00
Roman Khimov
981ae10091 config: declare and use the latest stable hardfork
Differentiate released and stable ones from test-only not-yet-ready and such.
Enabled only stable ones by default to avoid surprises in private networks
when some beta hardfork is made available with some node release.

Fixes #3719.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-06 12:23:46 +03:00
Roman Khimov
109319d220 network: avoid accidental double-close of blockFetcherFin channel
It's not supposed to happen, but if blockfetcher goes wild and calls this
twice it can ruin the system, so better safe than sorry.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-06 11:39:44 +03:00
Roman Khimov
c812150d83 network: avoid looping forever
blockFetcherFin is closed by the block fetcher, so it will forever return
something in this loop making it an infinite loop, creating useless load
and affecting normal node operation.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-06 11:35:54 +03:00
Anna Shaleva
49267f3412
Merge pull request #3726 from nspcc-dev/fix-unexpected-empty-payload
network: fix "unexpected empty payload: CMDVersion"
2024-12-05 23:34:05 +03:00
Anna Shaleva
1aed0faeec
Merge pull request #3727 from nspcc-dev/network-optimaln
network: optimize optimalN for small networks
2024-12-05 23:26:41 +03:00
Roman Khimov
d69f8ebbab network: optimize optimalN for small networks
Consider 10-node network. FanOut is 6 for it. optimalN is 12. But it's a
10-node network, you can't have more than 9 peers there. So adjust the formula
for netSize-1.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-05 22:31:19 +03:00
Roman Khimov
121307d349 network: fix "unexpected empty payload: CMDVersion"
We can have _a lot_ of these in the log, but the only reason they happen is
because we're trying to interpret length before checking for reader error,
CMDVersion is just 0.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-12-05 18:34:55 +03:00
Roman Khimov
8952922c4d
Merge pull request #3723 from nspcc-dev/fix-deal-timeout
blockfetcher: decrease default pool deal timeout
2024-12-04 17:29:11 +03:00
Anna Shaleva
2244d2ad75 blockfetcher: decrease default pool deal timeout
Dealing is fast, we don't need 10 minutes to check that connection can
(or can't) be established. Improve documentation along the way.

Close #3721.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-04 16:34:09 +03:00
Roman Khimov
0879018e03
Merge pull request #3722 from nspcc-dev/fs-testnet-forks
config: enable hardforks for default NeoFS testnet config
2024-12-04 12:26:27 +03:00
Anna Shaleva
f7ee66ff8b config: enable hardforks for default NeoFS testnet config
Otherwise all hardforks enabled by default from genesis which leads to
errors like this if we introduce new hardfork:
```
Dec 04 02:46:46 titan1 neofs-node[157390]: warn        client/multi.go:43        could not establish connection to RPC node        {"endpoint": "wss://rpc.morph.t5.fs.neo.org/ws", "error": "WS client initialization: failed to get network magic: unexpected hardfork: Echidna"}
```

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-04 12:22:07 +03:00
Roman Khimov
9a3923097b
Merge pull request #3717 from nspcc-dev/rel-0.107.0
CHANGELOG: release 0.107.0
2024-12-03 12:00:20 +03:00
Anna Shaleva
656d098f5c CHANGELOG: release 0.107.0
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-03 10:41:32 +03:00
Anna Shaleva
20ea4c40f5 docs: adjust NeoFS BlockFetcher configuration example
Set container ID to N3 Mainnet container ID, extend the list of storage
nodes.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-12-02 19:51:45 +03:00
Anna Shaleva
6a7d2bc250
Merge pull request #3712 from nspcc-dev/move-metrics
*: move metric `neogo_version` out of pkg/network
2024-12-02 10:53:54 +03:00
Anna Shaleva
fde3ab0c88 ROADMAP: update roadmap
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-29 19:55:22 +03:00
Roman Khimov
a3c2d82e03
Merge pull request #3713 from nspcc-dev/enable-fetcher
config: enable NeoFSBlockFetcher by default
2024-11-29 19:55:19 +03:00
Anna Shaleva
ba822cfa76 config: enable NeoFSBlockFetcher by default
Best and newest features have to be shown to our users!

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-29 19:28:03 +03:00
Roman Khimov
7b82c759ce
Merge pull request #3711 from nspcc-dev/fetch-dbft
go.mod: upgrade dBFT version
2024-11-29 18:39:56 +03:00
Anna Shaleva
9778fd88fb *: migrate onto simplified dbft.PublicKey interface
Adopt https://github.com/nspcc-dev/dbft/pull/137.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-29 18:33:19 +03:00
Anna Shaleva
a5cf1635d8 go.mod: fetch dbft v0.3.1
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-29 18:33:03 +03:00
Ekaterina Pavlova
ebb71497a5 *: move metric neogo_version out of pkg/network
Close #3682

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 16:58:11 +03:00
Anna Shaleva
dc2d22110f
Merge pull request #3709 from nspcc-dev/compile
neotest: extend cached compiled contract identifier in CompileFile
2024-11-29 14:46:19 +03:00
Ekaterina Pavlova
3f95476ce3 neotest: extend cached compiled contract identifier in CompileFile
Close #3542

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 14:43:37 +03:00
Anna Shaleva
ac073e2ada
Merge pull request #3710 from nspcc-dev/fix-dockerfiles
Dockerfile: migrate to AS keyword usage
2024-11-29 14:38:09 +03:00
Anna Shaleva
7ba295ca99
Merge pull request #3708 from nspcc-dev/rpcwrapper
cli: add check config field for `generate-rpcwrapper`
2024-11-29 14:34:02 +03:00
Anna Shaleva
3b7a7dc767
Merge pull request #3706 from nspcc-dev/blockfetcher
NeoFS BlockFetcher: add pool with retries
2024-11-29 14:32:19 +03:00
Anna Shaleva
24127c18ff Dockerfile: migrate to AS keyword usage
Fix the following Docker image build warning that stops privnet image build:
```
 1 warning found (use docker --debug to expand):
 - FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 3)
```

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-29 14:16:32 +03:00
Ekaterina Pavlova
870421e950 cli: add check config field for generate-rpcwrapper
Close #3646

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:53:40 +03:00
Ekaterina Pavlova
718ff9aa0a blockfetcher: add check on IndexFileSize of index files
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:37:50 +03:00
Ekaterina Pavlova
14c980a685 network: refactor return value from NeoFSBlockFetcher service
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:37:50 +03:00
Ekaterina Pavlova
119ca27994 blockfetcher: fix block enqueue logic
Previously, the `blockQueuer` routine, which enqueues blocks into
`bQueue`, could be blocked on enqueing newer blocks if older blocks
downloading is delayed by NeoFS.

The `blocksCh` channel, acting as a queue ordered by download speed,
conflicted with the BQueue requirement for strict sequential enqueuing
(expecting an exact range of blocks), resulting in a deadlock that
stalled the process.

Before with default config settings:
```
2024-11-27T17:12:19.348+0300	INFO	persisted to disk	{"blocks":
 0, "keys": 116, "headerHeight": 0, "blockHeight": 0, "took": "15
 .509083ms"}
2024-11-27T17:19:39.574+0300	INFO	persisted to disk	{"blocks":
 16, "keys": 11107, "headerHeight": 216768, "blockHeight": 216768,
 "took": "62.762041ms"}
```
Average block persistence speed: 492.40 block/s
Average blocks number for each persist log: 584.28

After:

```
2024-11-27T17:29:03.362+0300	INFO	persisted to disk	{"blocks":
 0, "keys": 116, "headerHeight": 0, "blockHeight": 0, "took": "19
 .485084ms"}
2024-11-27T17:34:58.527+0300	INFO	persisted to disk	{"blocks":
 16, "keys": 11109, "headerHeight": 216770, "blockHeight": 216769,
 "took": "52.43925ms"}
```
Average block persistence speed: 610.33 block/s
Average blocks number for each persist log: 752.61

Close #3699

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:37:50 +03:00
Ekaterina Pavlova
c1ce6904c4 network: discard bFetcherQueue earlier
Discard `bFetcherQueue` to avoid persisting
new blocks during termination before service Shutdown.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:37:50 +03:00
Ekaterina Pavlova
df05cd2858 blockfetcher: change shutdown logic
Move the `isActive` check earlier to enable faster shutdown and prevent
the service from hanging.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:37:50 +03:00
Ekaterina Pavlova
8216f538c3 blockfetcher: check container and network Magic
Close #3644

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:37:11 +03:00
Ekaterina Pavlova
92ff8409a9 blockfetcher: add retry for GET and SEARCH operations
Close #3564

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-29 13:34:40 +03:00
Ekaterina Pavlova
9fa07d8d6d blockfetcher: fix invalid wallet test
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-28 16:14:25 +03:00
Ekaterina Pavlova
c321eed8ee blockfetcher: use pool for GET and SEARCH operations
Use NeoFS storage nodes pool during blocks fetching to spread the load.

Close #3568

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-28 16:14:25 +03:00
Ekaterina Pavlova
04516e7d26 neofs: add pool support for NeoFS operations
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-28 16:13:36 +03:00
Anna Shaleva
e89f9fe2a4
Merge pull request #3704 from nspcc-dev/interop-md-fixes
Native contract manifest build fixes
2024-11-26 17:03:31 +03:00
Anna Shaleva
f98169a762
Merge pull request #3689 from nspcc-dev/feat/filter-events-by-parameters
ws: allow filtering notification by parameters
2024-11-26 10:12:40 +03:00
Pavel Karpy
97bd73b38c rpcsrv/tests: simplify tests with parsed test contract hash
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2024-11-26 00:26:06 +03:00
Pavel Karpy
0ba16fe580 ws: allow filtering notification by parameters
`Any` type with nil/null value is treated as a parameter filter that allows
any notification value. Not more than 16 filter parameters are allowed.
Closes #3624.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2024-11-26 00:26:04 +03:00
Roman Khimov
9e7fd5180a interop: slightly rephrase BuildHFSpecificMD logic
Technically, we can always buildHFSpecificMD() if contract is active, but we
just optimize the build out in some cases. switch slightly obfuscates this
and requires having the call in two branches.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-25 14:45:26 +03:00
Roman Khimov
6b6878706a interop: fix ActiveFrom comparison when building HF-specific MD
These conditions are about filtering methods out. A method is excluded if its
ActiveFrom is strictly higher than the current HF, since if it's the same then
it should be present. Otherwise contract is broken at the height of this
particular HF.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-25 14:45:20 +03:00
Anna Shaleva
171e01be3c
Merge pull request #3691 from nspcc-dev/uploader-eq-search
upload-bin, NeoFSBlockFetcher: migrate to SEARCH with strict equality comparator
2024-11-25 13:43:12 +03:00
Ekaterina Pavlova
43609dd984 *: migrate to SEARCH with strict equality comparator
Close #3670

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-25 13:19:35 +03:00
Anna Shaleva
3d3f3f67e6
Merge pull request #3697 from nspcc-dev/logs-for-cash
core: fix restoring chain with `StateRootInHeader = true`
2024-11-25 12:52:11 +03:00
Ekaterina Pavlova
616c805ac5 cli: refactor index file search in upload-bin command
Align index file searching logic with block search.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-22 17:45:35 +03:00
Ekaterina Pavlova
5dab154582 cli: split searchBatchSize usage in upload-bin command
Split searchBatchSize into constant for searching and uploading.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-22 17:45:35 +03:00
Ekaterina Pavlova
9fd375d56d cli: fix nil error of fetchLatestMissingBlockIndex in upload-bin
Close #3653

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-22 17:45:35 +03:00
Roman Khimov
ff15e39363
Merge pull request #3695 from nspcc-dev/add-unwrap-errnull
unwrap: add ErrNull to handle Null returned, fix #2795
2024-11-22 14:19:46 +03:00
Ekaterina Pavlova
85c3b96f82 core: fix restoring chain with StateRootInHeader = true
Close #3597

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-22 13:33:50 +03:00
Ekaterina Pavlova
8fe2ae8437 config: add UnitTestNet configuration to embed configs
Tests should use embed config too.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-22 11:55:51 +03:00
Roman Khimov
3d00a19383 unwrap: add ErrNull to handle Null returned, fix #2795
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-21 13:23:32 +03:00
Anna Shaleva
cdbc026c27
Merge pull request #3692 from nspcc-dev/basicchain-doc
*: extend basic testing chain documentation
2024-11-20 18:35:30 +03:00
Anna Shaleva
40dda9f524 *: extend basic testing chain documentation
Save some time for developers who are not familiar with RPC server
unit-test structure.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-20 18:06:13 +03:00
Anna Shaleva
2b758c53eb
Merge pull request #3560 from nspcc-dev/nep24
manifest: support NEP-24
2024-11-20 15:37:53 +03:00
Ekaterina Pavlova
b63c7aad73 rpcsrv: add NEP24 test
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-20 14:45:28 +03:00
Ekaterina Pavlova
8e99ff65e1 rpcsrv: update testdata/testblocks.acc
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-20 14:45:28 +03:00
Ekaterina Pavlova
b2bd8e4a0a manifest: support NEP-24
Close #3451

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-20 14:45:28 +03:00
Ekaterina Pavlova
54e3708566 manifest: add Required field to Standard
`Required` contains standards that are required for this standard.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-20 14:45:28 +03:00
Anna Shaleva
57eec71101
Merge pull request #3690 from nspcc-dev/drop-compat-0.107.0
Drop deprecated code for 0.107.0
2024-11-20 11:03:46 +03:00
Roman Khimov
3f1ccdd2ce ROADMAP: fix missing trailing newline
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-19 22:23:05 +03:00
Roman Khimov
aba781c2de waiter: remove compat code for block-based waiting
A part of #3454 for 0.107.0 release.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-19 22:23:00 +03:00
Roman Khimov
aeee733479 neorpc: remove deprecated error codes (as scheduled)
A part of #3454.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-19 21:47:27 +03:00
Roman Khimov
632092b2cd network: drop deprecated serv_node_version metric
A part of #3454.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-19 21:43:08 +03:00
Roman Khimov
125f757988 state: drop compatibility code for NEOBalance
A part of #3454.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-19 21:40:01 +03:00
Roman Khimov
7683f4366d result: drop obsolete unmarshaling code
A part of #3454.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-19 21:35:50 +03:00
Anna Shaleva
2b1b9a4fca
Merge pull request #3685 from nspcc-dev/vm-map
vm: fix packmap operation
2024-11-19 17:56:28 +03:00
Ekaterina Pavlova
9082c6ea1a vm: fix PACKMAP operation
Close #3613

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-19 13:49:53 +03:00
Anna Shaleva
176593b31f
Merge pull request #3687 from nspcc-dev/extend-smartcontract-conversions
Extend smartcontract type conversions
2024-11-18 17:21:22 +03:00
Roman Khimov
db2956f1af smartcontract: add support for maps in NewParameterFromValue
It's just not possible to use maps in invokers/actors without this. And maps
have too many combinations to try pushing them into a type switch, that's
where reflection kicks in and solves it easily.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-18 15:20:39 +03:00
Roman Khimov
22c6ab4de9 smartcontract: process slices via reflection in NewParameterFromValue
Pros:
 * less code
 * handles more types
Cons:
 * slow

This code is not likely to be on the hot path and it is exactly the one used
by actors for making calls of various kinds. Supporting more types is more
important here than raw speed.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-18 15:20:39 +03:00
Anna Shaleva
990634a43a
Merge pull request #3688 from nspcc-dev/vm-fix-popitem-refcounting
vm: fix incorrect refcounting in POPITEM
2024-11-18 11:32:57 +03:00
Roman Khimov
270f0d2d7a vm: fix incorrect refcounting in POPITEM
We're popping an item (array) off the stack, OK, it triggers refs.Remove() for
it. Then we're pushing an inner item to the stack, OK, it triggers refs.Add()
for this element. Why are we removing it afterwards? Looks like something went
wrong in 324107b31e (and https://github.com/nspcc-dev/neo-go/pull/1670)
since a simple test shows zero counter after POPITEM and -1 after popping the
only item left on the stack.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-16 18:03:25 +03:00
Roman Khimov
a123b75fd9 io: no need to do ValueOf() again, it's known
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-15 22:29:06 +03:00
Anna Shaleva
4a96bd1dc1
Merge pull request #3686 from nspcc-dev/fix-mode
cli: fix `skip-blocks-uploading` mode
2024-11-15 17:39:00 +03:00
Ekaterina Pavlova
5f6284de05 cli: fix skip-blocks-uploading mode of upload-bin command
If uploadBlocks succeeded uploadIndexFiles should work based on the
currentBlockHeight.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-15 17:28:24 +03:00
Ekaterina Pavlova
58ed448f8d cli: add debug mode to upload-bin command
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-15 17:18:44 +03:00
Anna Shaleva
b97d0b2326
Merge pull request #3684 from nspcc-dev/last-block
cli: fix fetchLatestMissingBlockIndex and uploadIndexFiles in `upload-bin` command
2024-11-15 15:12:30 +03:00
Ekaterina Pavlova
375b095f78 cli: add details to the logs in upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-15 14:34:36 +03:00
Ekaterina Pavlova
380d112599 cli: fix stopping of uploadIndexFiles in upload-bin
The expected count of index files should be counted based on the already
uploaded object number.

Close #3669

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-15 14:34:36 +03:00
Ekaterina Pavlova
6e863e9a06 cli: fix fetchLatestMissingBlockIndex in upload-bin command
Close #3667

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-15 14:34:10 +03:00
Roman Khimov
895a0ae624
Merge pull request #3677 from ixje/expose-slots
vm: make slots public
2024-11-15 13:16:48 +03:00
ixje
7ec0c1155c vm: expose Context slots, hide Set/ClearRefs on Slot, deprecate Dump*Slot methods
Signed-off-by: ixje <erik@coz.io>
2024-11-15 11:05:42 +01:00
Anna Shaleva
5707bdd1d1
Merge pull request #3683 from nspcc-dev/improve-tx-doc
Improve tx and block doc
2024-11-15 12:39:36 +03:00
Roman Khimov
9cc16d73f2 core: a bit better field explanations for header/transaction
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-15 11:27:21 +03:00
Roman Khimov
933d522b82 block: explain protocol extensions better
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-15 11:27:21 +03:00
Roman Khimov
ccbb198a5b core: document trimmed transactions better
Eventually we can drop them, but they were present for a long long time.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-15 11:27:21 +03:00
Roman Khimov
bbec25de43 block: document hash caching better
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-15 11:27:21 +03:00
Roman Khimov
7af2ab92d2 transaction: specify hash/size behavior better
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-15 11:27:21 +03:00
Anna Shaleva
c2b4d32224
Merge pull request #3680 from nspcc-dev/fix-range
*: remove range usage from smartcontracts
2024-11-14 17:22:27 +03:00
Ekaterina Pavlova
71a89c230a *: remove range usage from smartcontracts
Reverting a part of 1b83dc2, because ranging over integers is not
supported by smart contract compiler, ref. #3525.

Close #3671

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-14 16:57:39 +03:00
Anna Shaleva
3ec06f316e
Merge pull request #3674 from ixje/remove-breakpoint
vm: add RemoveBreakPoint support
2024-11-14 12:42:57 +03:00
ixje
d8ea4103c7 cli: add delete and list break point commands
Close #3673

Signed-off-by: ixje <erik@coz.io>
2024-11-14 09:52:25 +01:00
Anna Shaleva
cb51eeb1b1
Merge pull request #3678 from nspcc-dev/dupword
*: fix some dupword warnings
2024-11-14 10:46:17 +03:00
Roman Khimov
8b12b3ac64 *: fix some dupword warnings
Duplicate words in comments.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-13 20:57:10 +03:00
Anna Shaleva
dda2cafdf8
Merge pull request #3665 from nspcc-dev/http-code-status-ok
rpcsrv: align default error code with C# implementation
2024-11-12 17:19:03 +03:00
Ekaterina Pavlova
0afd9ac0bb rpcsrv: align default error code with C# implementation
We had 400, 405 and 500 in getHTTPCodeForError, but none is explicitly
set in C#.

1. **405 code** (unknown method, `neorpc.MethodNotFoundCode`). The
resulting HTTP code returned by C# server is 200 OK:
```
anna@kiwi:~/Documents/GitProjects/bane-labs/go-ethereum$ curl -v -d '{
"jsonrpc": "2.0", "id": 1, "method": "unknown-method", "params": [] }'
http://seed1t5.neo.org:20332 | json_pp
  % Total    % Received % Xferd  Average Speed   Time    Time     Time
  Current
                                 Dload  Upload   Total   Spent    Left
                                 Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--
       0*   Trying 34.133.235.69:20332...
* Connected to seed1t5.neo.org (34.133.235.69) port 20332 (#0)
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--
       0> POST / HTTP/1.1
> Host: seed1t5.neo.org:20332
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Length: 71
> Content-Type: application/x-www-form-urlencoded
>
} [71 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Tue, 12 Nov 2024 13:45:51 GMT
< Server: Kestrel
< Transfer-Encoding: chunked
<
{ [461 bytes data]
100   520    0   449  100    71    729    115 --:--:-- --:--:-- --:--:--
   844
* Connection #0 to host seed1t5.neo.org left intact
{
   "error" : {
      "code" : -32601,
      "data" : "   at Neo.Plugins.Result.True_Or(Boolean result,
      RpcError err)\n   at Neo.Plugins.RpcServer.ProcessRequestAsync
      (HttpContext context, JObject request)",
      "message" : "Method not found - The method 'unknown-method'
      doesn't exists. -    at Neo.Plugins.Result.True_Or(Boolean result,
       RpcError err)\n   at Neo.Plugins.RpcServer.ProcessRequestAsync
       (HttpContext context, JObject request)"
   },
   "id" : 1,
   "jsonrpc" : "2.0"
}
```
2. **400 code** (malformed request, `neorpc.BadRequestCode`). The
resulting HTTP code returned by C# server is 200 OK:
```
anna@kiwi:~/Documents/GitProjects/bane-labs/go-ethereum$ curl -v -d '{
"jsonrpc": "2.0", "id": 1, "method": "getapplicationlog", "params": ["]
}' http://seed1t5.neo.org:20332 | json_pp
  % Total    % Received % Xferd  Average Speed   Time    Time     Time
  Current
                                 Dload  Upload   Total   Spent    Left
                                 Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--
       0*   Trying 34.133.235.69:20332...
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--
       0* Connected to seed1t5.neo.org (34.133.235.69) port 20332 (#0)
> POST / HTTP/1.1
> Host: seed1t5.neo.org:20332
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Length: 75
> Content-Type: application/x-www-form-urlencoded
>
} [75 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Tue, 12 Nov 2024 13:50:12 GMT
< Server: Kestrel
< Transfer-Encoding: chunked
<
{ [86 bytes data]
100   150    0    75  100    75     84     84 --:--:-- --:--:-- --:--:--
   169
* Connection #0 to host seed1t5.neo.org left intact
{
   "error" : {
      "code" : -32700,
      "message" : "Bad request"
   },
   "id" : null,
   "jsonrpc" : "2.0"
}
```
3. **500 code** (internal server error, `neorpc
.InternalServerErrorCode`). It's difficult to reproduce this error on
real network for C# server, but the resulting code is expected to be the
 same, 200 OK.

Close #3586

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-12 17:08:01 +03:00
Anna Shaleva
66fbcb2f00
Merge pull request #3642 from nspcc-dev/config-blockfetcher
config: add NeoFSBlockFetcher section
2024-11-11 11:57:33 +03:00
Anna Shaleva
b62c0b9118
Merge pull request #3666 from nspcc-dev/doc-improve
Tiny documentation improvements
2024-11-07 18:46:35 +03:00
Roman Khimov
9cca702ac2 emit: add some package doc
People use it more often than they should, discourage that a bit, it's rarely
really needed.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-07 18:44:55 +03:00
Roman Khimov
24577d320d rpcclient: improve GetNEPXXTransfers docs
No one cares about NeoGo 0.7X.smth, but timestamps are a bit unobvious here.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-07 18:02:53 +03:00
Anna Shaleva
7062ab9397
Merge pull request #3660 from nspcc-dev/gaps-upload-bin
cli: add check of successful upload for `upload-bin`
2024-11-06 11:27:23 +03:00
Ekaterina Pavlova
29e2d712f4 cli: add flag max-retries for upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-06 11:18:41 +03:00
Ekaterina Pavlova
18ed3bb3e8 cli: fix missing errors on closure of writer in upload-bin
Ref #3658

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-06 11:16:46 +03:00
Anna Shaleva
1a540d5883
Merge pull request #3663 from nspcc-dev/io-improve-bytes-doc
io: specify Bytes() behavior a bit better for BufBinWriter
2024-11-06 11:15:52 +03:00
Anna Shaleva
8c4d9432d7
Merge pull request #3662 from nspcc-dev/improve-uploadbin-log
cli: extend logs of index file construction for `upload-bin`
2024-11-06 11:13:44 +03:00
Anna Shaleva
b6516586d5
Merge pull request #3661 from alexey-savchuk/fix-panic-on-sighup
services/rpcsrv: Return a new server by pointer
2024-11-06 11:04:44 +03:00
Roman Khimov
23f9c5a43b io: specify Bytes() behavior a bit better for BufBinWriter
It's not immediately obvious.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-05 16:58:33 +03:00
Alexey Savchuk
df9247c00b services/rpcsrv: Return a new server by pointer
Before, a new server was returned by value which could cause
a panic `unlock of unlocked mutex` on SIGHUP handling. It's
because the new server overwrites a locked mutex of the already
existing server.

oct‚ 22 13:51:15 node1 neo-go[1183338]: fatal error: sync: Unlock of unlocked RWMutex
oct‚ 22 13:51:15 node1 neo-go[1183338]: goroutine 538 [running]:
oct‚ 22 13:51:15 node1 neo-go[1183338]: sync.fatal({0xf83d64?, 0xc001085880?})
oct‚ 22 13:51:15 node1 neo-go[1183338]:         runtime/panic.go:1007 +0x18
oct‚ 22 13:51:15 node1 neo-go[1183338]: sync.(*RWMutex).Unlock(0xc00019a4c8)
oct‚ 22 13:51:15 node1 neo-go[1183338]:         sync/rwmutex.go:208 +0x45
oct‚ 22 13:51:15 node1 neo-go[1183338]: github.com/nspcc-dev/neo-go/pkg/services/rpcsrv.(*Server).dropSubscriber(0xc00019a2c8, 0xc000a77740)
oct‚ 22 13:51:15 node1 neo-go[1183338]:         github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/server.go:825 +0xce
oct‚ 22 13:51:15 node1 neo-go[1183338]: github.com/nspcc-dev/neo-go/pkg/services/rpcsrv.(*Server).handleWsReads(0xc00019a2c8, 0xc0034478c0, 0xc000af5f80, 0xc000a7
7740)
oct‚ 22 13:51:15 node1 neo-go[1183338]:         github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/server.go:810 +0x266
oct‚ 22 13:51:15 node1 neo-go[1183338]: github.com/nspcc-dev/neo-go/pkg/services/rpcsrv.(*Server).handleHTTPRequest(0xc00019a2c8, {0x11c3900, 0xc003437dc0}, 0xc00
31945a0)
oct‚ 22 13:51:15 node1 neo-go[1183338]:         github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/server.go:582 +0x54a
oct‚ 22 13:51:15 node1 neo-go[1183338]: net/http.HandlerFunc.ServeHTTP(0x471779?, {0x11c3900?, 0xc003437dc0?}, 0xc000943b68?)
oct‚ 22 13:51:15 node1 neo-go[1183338]:         net/http/server.go:2171 +0x29
oct‚ 22 13:51:15 node1 neo-go[1183338]: net/http.serverHandler.ServeHTTP({0xc000a77680?}, {0x11c3900?, 0xc003437dc0?}, 0x6?)
oct‚ 22 13:51:15 node1 neo-go[1183338]:         net/http/server.go:3142 +0x8e
oct‚ 22 13:51:15 node1 neo-go[1183338]: net/http.(*conn).serve(0xc0032030e0, {0x11c5220, 0xc000a76960})
oct‚ 22 13:51:15 node1 neo-go[1183338]:         net/http/server.go:2044 +0x5e8
oct‚ 22 13:51:15 node1 neo-go[1183338]: created by net/http.(*Server).Serve in goroutine 534
oct‚ 22 13:51:15 node1 neo-go[1183338]:         net/http/server.go:3290 +0x4b4

Signed-off-by: Alexey Savchuk <alexey.a.savchuk@yandex.com>
2024-11-05 15:05:25 +03:00
Anna Shaleva
cee296eb92
Merge pull request #3659 from nspcc-dev/make-balance-work-without-wallet
cli: make nep1X balance commands work without a wallet, fix #3275
2024-11-05 13:24:20 +03:00
Roman Khimov
ddaf9c01ab cli: make nep1X balance commands work without a wallet, fix #3275
Checking the balance should be easy without any wallets involved, the data
is available anyway.

Tests are extended a bit as well.

Adding the command to "query nep1X" is not so trivial, so let's have it this
way for now.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-11-05 13:12:13 +03:00
Anna Shaleva
a85f3ce83f cli: rename uploader.go to upload_bin.go
Follow the style of other files in the `util` package and existing
`dump_bin.go`.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-05 12:43:20 +03:00
Anna Shaleva
22cb7feb1d cli: fix format
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-05 11:25:08 +03:00
Anna Shaleva
82b508e8f3 cli: extend logs of index file construction for upload-bin
These logs are needed to track the progress of uploaders.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-05 11:24:57 +03:00
Roman Khimov
95098d4b25
Merge pull request #3657 from nspcc-dev/drop-macos-12
workflows: remove macos-12 support
2024-11-02 13:30:21 +03:00
Anna Shaleva
5cffa90774 workflows: remove macos-12 support
Support for macos-12 will be deprecated 10/7/24 and the image will
be fully unsupported by 12/3/24. Ref.
https://github.com/actions/runner-images/issues/10721.

Get rid of the build warning:
```
A brownout will take place on November 4, 14:00 UTC - November 5, 00:00
UTC to raise awareness of the upcoming macOS-12 environment removal.
```

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-11-02 13:08:12 +03:00
Anna Shaleva
21d6887969
Merge pull request #3650 from nspcc-dev/index-files
cli: add retries if search results are not expected length in `upload-bin`
2024-11-02 12:28:26 +03:00
Ekaterina Pavlova
1c65f32a6d config: add NeoFSBlockFetcher section
NeoFSBlockFetcher is disabled by default.

Close #3632

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-02 12:26:57 +03:00
Ekaterina Pavlova
c955c1e4ea cli: use GET instead of HEAD in upload bin
Will be reverted when nspcc-dev/neofs-node#2988 is fixed. Currently HEAD
request can return incorrect attributes, which can lead to incorrect
index files.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-02 12:04:02 +03:00
Ekaterina Pavlova
35d12779d6 cli: adjust index file creation in upload-bin
In case of incomplete search result it will try to find each missed oid
and process it. In case of duplicates the first found will be in index
file.

Close #3647

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-01 19:51:01 +03:00
Ekaterina Pavlova
365bbe08ed cli: refactor errors and comments in upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-11-01 14:58:48 +03:00
Ekaterina Pavlova
6a93f70728 cli: add flag for skipping block uploading in upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-31 18:39:40 +03:00
Anna Shaleva
a4633ce2c7
Merge pull request #3649 from nspcc-dev/vm-modpow
vm: fix MODPOW operation
2024-10-31 17:50:40 +03:00
Ekaterina Pavlova
cccbe843be vm: fix MODPOW operation
Close #3612

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-31 17:49:02 +03:00
Anna Shaleva
e8b8c1a4b1
Merge pull request #3651 from nspcc-dev/tojson-test
stackitem: extend ToJSON test
2024-10-30 20:01:45 +03:00
Anna Shaleva
a0d2f95e42 stackitem: extend ToJSON test
Inspired by https://github.com/neo-project/neo/pull/3558, although this
PR fixes different implementation of stackitem serializatior that is
used outside of VM.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-29 12:07:04 +03:00
Anna Shaleva
b8a65d3c37
Merge pull request #3643 from nspcc-dev/uploader-workers
cli: add flag workers to `upload-bin` command
2024-10-24 12:26:51 +03:00
Ekaterina Pavlova
7b3eeb9061 cli: fix usage of upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-24 12:19:12 +03:00
Ekaterina Pavlova
36e855609d cli: add workers and searchers flags to upload-bin command
Number of workers to fetch, upload and search blocks concurrently is now
configurable.

Close #3641

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-24 12:19:10 +03:00
Anna Shaleva
4b10f23aec
Merge pull request #3638 from nspcc-dev/uploader-retry-errors
cli: fix `upload-bin` error handling
2024-10-23 16:00:38 +03:00
Ekaterina Pavlova
a5e9ab6979 cli: verify index file construction in upload-bin command
Verify that there are no empty OIDs in the constructed index file, as
empty payloads can result in improperly set attributes, leading to empty
OIDs being added to the index file.

Close #3628

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-23 15:30:12 +03:00
Ekaterina Pavlova
42c8e40eaa cli: add retry to all requests to NeoFS in upload-bin
Add retry to `NetworkInfo` and `uploadObj`.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-23 15:30:12 +03:00
Ekaterina Pavlova
8b43c33e44 cli: extend object attribute parsing error in upload-bin
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-23 15:30:12 +03:00
Ekaterina Pavlova
5b793bcf1b cli: fix process termination in upload-bin command
Add a return statement to properly handle errors, ensuring the process
terminates as expected.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-23 15:30:12 +03:00
Ekaterina Pavlova
e83b3e4839 cli: fix error handling in upload-bin command
Fix shared global error reuse.

Close #3634

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-23 15:30:12 +03:00
Anna Shaleva
0b79901b7f
Merge pull request #3637 from nspcc-dev/attributes
BlockFetcher/BlockUploader: fix and add additional attributes
2024-10-23 15:07:09 +03:00
Ekaterina Pavlova
c47d4e6c5b BlockFetcher/BlockUploader: fix and add additional attributes
Capitalize block objects and index file objects attributes.
Add network magic attribute to container.

Close #3631

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-23 13:52:06 +03:00
Anna Shaleva
e82d9a179c
Merge pull request #3636 from nspcc-dev/blockfetcher-errors
blockfetcher: add more details to errors
2024-10-22 16:29:28 +03:00
Ekaterina Pavlova
8a154d9189 blockfetcher: add more details to errors
Close #3629

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-22 14:11:27 +03:00
Anna Shaleva
a0def2e5d2
Merge pull request #3633 from nspcc-dev/fix-empty-obj-uploading
cli: fix empty block uploading in `util upload-bin`
2024-10-22 10:12:17 +03:00
Ekaterina Pavlova
e00d7fec7f cli: fix empty block uploading in util upload-bin
In case of uploading retry already drained bin buffer is returned in
`bw.Bytes`, which leads to empty block uploading.

Close #3630

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-21 22:44:06 +03:00
Roman Khimov
29bb3ff1cf
Merge pull request #3627 from nspcc-dev/fix-ntf
core: prove contract notifications count is not restricted
2024-10-18 21:16:56 +03:00
Anna Shaleva
c962edcf47
Merge pull request #3625 from nspcc-dev/uploader-timeouts
cli: increase NeoFS pool deadlines for upload-bin command
2024-10-18 20:02:24 +03:00
Ekaterina Pavlova
41873e35c6 cli: increase NeoFS pool deadlines for upload-bin command
`defaultDialTimeout` and `defaultStreamTimeout` increased because of
`code = DeadlineExceeded desc = context deadline exceeded`

Close #3620

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-18 19:59:27 +03:00
Anna Shaleva
7b09812069 core: refactor TestEngineLimits
Replace repetative hand-written code with generated one.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-18 19:10:47 +03:00
Anna Shaleva
b5b89881b7 core: prove contract notifications count is not restricted
A part of #3490.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-18 19:10:34 +03:00
Anna Shaleva
8e1fdd5d70
Merge pull request #3626 from nspcc-dev/uploader-errors
cli: adjust error of `upload-bin` command
2024-10-18 14:28:02 +03:00
Ekaterina Pavlova
87bbff9831 cli: adjust error of upload-bin command
Miscalculation leads to negative values in errors:
```
2024-10-17 11:26:56.790	failed to fetch the latest missing block index
 from container: search of index files failed for batch with indexes
 from -260000 to -250000: error during object IDs iteration: context
 deadline exceeded
```

Close #3621

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-18 14:26:17 +03:00
Anna Shaleva
14ea5a8d32
Merge pull request #3582 from nspcc-dev/block-fetcher-commands
NeoFS block storage: add uploading commands
2024-10-17 12:10:06 +03:00
Ekaterina Pavlova
6199240598 cli: add upload-bin
This command is used for keeping container with blocks for
blockfetcher updated.

Close #3578

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-17 11:56:07 +03:00
Ekaterina Pavlova
59fab5d654 blockfetcher: adjust index file numeration from 0
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-16 19:23:56 +03:00
Ekaterina Pavlova
852dcb0f64 config, docs: adjust NeoFSBlockFetcher index file and time attributes
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-16 19:23:56 +03:00
Ekaterina Pavlova
2435484dc4 neofs: add ObjectSearchInitter
With ObjectSearchInitter ObjectSearch can can be done by both NeoFS SDK
client and pool.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-16 19:23:56 +03:00
Roman Khimov
86ed214e8a
Merge pull request #3616 from nspcc-dev/coverage-enh
neotest: coverage collection polishing
2024-10-16 16:14:04 +03:00
Anna Shaleva
c1444d45a4 nns: update neo-go dependency
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-16 16:04:54 +03:00
Anna Shaleva
d8e945978a neotest: sort coverage blocks within a test scope
Make the behaviour similar to the `go test` output. It's not a problem
for the `go cover` tool, but the sorted file is easier to debug and analize.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-16 16:03:46 +03:00
Anna Shaleva
c747bb8ff7 neotest: preallocate coverage blocks list
And always use pointers for coverage block processing, dereference is
excessive in this context.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-15 15:44:54 +03:00
Anna Shaleva
49f2e1dc64 neotest: gracefully report about coverage setup error
Don't use panic when we can use t.Fatal.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-15 15:44:54 +03:00
Anna Shaleva
2dc588ea95 neotest: distinguish coverage modes
Use calls frequency calculated by executor in the final coverage
profile for `atomic` cover mode. Support only `set` cover mode for now
due to #3587.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-15 15:44:53 +03:00
Anna Shaleva
a9242535db
Merge pull request #3605 from nspcc-dev/fix-linter
Enable linter for every module
2024-10-11 14:16:44 +03:00
Anna Shaleva
8bdb8afaf5 Makefile: adjust lint target
Enable linter for examples, scripts and interop packages.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-11 12:41:06 +03:00
Anna Shaleva
c5c64f5f07 interop: fix linter issues
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-11 12:41:06 +03:00
Anna Shaleva
25b353c9f8 workflows: enable lint for interop package
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-11 12:41:06 +03:00
Anna Shaleva
9fb291d5df workflows: add linter for scripts
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-11 12:40:55 +03:00
Anna Shaleva
524ba5fd1b examples: fix linter issues
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-11 12:24:02 +03:00
Anna Shaleva
949ed4f2ea workflows: enable linter for example contracts
Avoid situations like 9f93123301.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-10 11:36:45 +03:00
Anna Shaleva
8ee13cc35e scripts: keep go.mod tidy
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-10 11:36:45 +03:00
Anna Shaleva
8ed3ab8c82
Merge pull request #3604 from nspcc-dev/fix-nns-test-with-coverage
Fix nns test with coverage
2024-10-09 18:49:00 +03:00
Roman Khimov
ad41d0f9c7 neotest: add a warning to CompileSource, it's bad for coverage
$ go tool cover -html=coverage.txt -o coverage.html
cover: can't read "/home/rik/dev/neo-go/examples/nft-nd-nns/contract.go": open /home/rik/dev/neo-go/examples/nft-nd-nns/contract.go: no such file or directory

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-10-09 15:53:01 +03:00
Roman Khimov
594612bbdf nft-nd-nns: update to coverage-enabled NeoGo
It's to be updated to the final release eventually, but it's a good contract
to showcase the coverage feature, quite a lot of code with good overall
percentage.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-10-09 15:52:06 +03:00
Roman Khimov
9f93123301 nft-nd-nns: fix test runs
./nns_test.go:161:6: declared and not used: i

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-10-09 15:50:18 +03:00
Roman Khimov
0968c3a81f
Merge pull request #3600 from nspcc-dev/fix-cover-panic
neotest: don't collect coverage for contracts with empty DI
2024-10-07 19:15:39 +03:00
Anna Shaleva
7fac3bcd6f neotest: don't collect coverage for contracts with empty DI
Users are allowed to provide any contract to neotest, including those
contracts that don't have DebugInfo filled in. Neotest should filter
them out, otherwise panic may occur on attempt to write coverage profile:
```
--- FAIL: TestDeploys (0.01s)
panic: runtime error: invalid memory address or nil pointer dereference
	panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0xc6f5b5]

goroutine 1735 [running]:
testing.tRunner.func1.2({0xd7aa80, 0x1723f60})
	/usr/local/go/src/testing/testing.go:1631 +0x24a
testing.tRunner.func1()
	/usr/local/go/src/testing/testing.go:1634 +0x377
panic({0xd7aa80?, 0x1723f60?})
	/usr/local/go/src/runtime/panic.go:770 +0x132
github.com/nspcc-dev/neo-go/pkg/neotest.processCover()
	/home/anna/go/pkg/mod/github.com/nspcc-dev/neo-go@v0.106.4-0.20241007094345-11151938b9bd/pkg/neotest/coverage.go:143 +0xd5
github.com/nspcc-dev/neo-go/pkg/neotest.writeCoverageReport({0x10c6260, 0xc00033a378})
	/home/anna/go/pkg/mod/github.com/nspcc-dev/neo-go@v0.106.4-0.20241007094345-11151938b9bd/pkg/neotest/coverage.go:123 +0x4b
github.com/nspcc-dev/neo-go/pkg/neotest.reportCoverage({0x10db278, 0xc00021a000})
	/home/anna/go/pkg/mod/github.com/nspcc-dev/neo-go@v0.106.4-0.20241007094345-11151938b9bd/pkg/neotest/coverage.go:118 +0x165
github.com/nspcc-dev/neo-go/pkg/neotest.(*Executor).trackCoverage.func1()
	/home/anna/go/pkg/mod/github.com/nspcc-dev/neo-go@v0.106.4-0.20241007094345-11151938b9bd/pkg/neotest/basic.go:182 +0x1b
testing.(*common).Cleanup.func1()
	/usr/local/go/src/testing/testing.go:1175 +0x10f
testing.(*common).runCleanup(0xc00021a000, 0x58d7c0?)
	/usr/local/go/src/testing/testing.go:1353 +0xdb
testing.(*common).runCleanup.func2()
	/usr/local/go/src/testing/testing.go:1337 +0x47
panic({0xd7aa80?, 0x1723f60?})
```

An example of such partially-filled contract is:
a8d5e001a2/tests/deploys_test.go (L13)
a8d5e001a2/contracts/contracts.go (L96)

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-07 17:51:19 +03:00
Anna Shaleva
11151938b9
Merge pull request #3599 from Slava0135/fix-modmul
vm: fix modmul operation
2024-10-07 12:43:45 +03:00
slava0135
9aca090644 vm: fix modmul operation
Signed-off-by: Slava0135 <super.novalskiy_0135@inbox.ru>
2024-10-07 11:02:01 +03:00
Anna Shaleva
c960a7eb47
Merge pull request #3594 from nspcc-dev/unclaimed-gas
vm: fix unclaimedGas calculation
2024-10-04 17:03:21 +03:00
Ekaterina Pavlova
02727b14b7 vm: fix unclaimedGas calculation
Fix difference with C#:
```
(base) ekaterinapavlova@MacBook-Air-4 neo-go % curl -X POST
http://seed1t5.neo.org:20332 -H 'Content-Type: application/json' -d '{
  "jsonrpc": "2.0",
  "method": "getapplicationlog",
  "params":
  ["61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3f"],
  "id": 1
}' | json_pp
  % Total    % Received % Xferd  Average Speed   Time    Time     Time
  Current
                                 Dload  Upload   Total   Spent    Left
                                 Speed
100   429    0   269  100   160    582    346 --:--:-- --:--:-- --:--:--
   930
{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "executions" : [
         {
            "exception" : null,
            "gasconsumed" : "198754",
            "notifications" : [],
            "stack" : [
               {
                  "type" : "Integer",
                  "value" : "0"
               }
            ],
            "trigger" : "Application",
            "vmstate" : "HALT"
         }
      ],
      "txid" :
      "0x61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3
      f"
   }
}
```
(base) ekaterinapavlova@MacBook-Air-4 neo-go % curl -X POST  https://rpc
.t5.n3.nspcc.ru:20331 -H 'Content-Type: application/json' -d '{
  "jsonrpc": "2.0",
  "method": "getapplicationlog",
  "params":
  ["61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3f"],
  "id": 1
}' | json_pp
  % Total    % Received % Xferd  Average Speed   Time    Time     Time
  Current
                                 Dload  Upload   Total   Spent    Left
                                 Speed
100   583  100   423  100   160   1424    538 --:--:-- --:--:-- --:--:--
  1969
{
   "id" : 1,
   "jsonrpc" : "2.0",
   "result" : {
      "executions" : [
         {
            "exception" : "at instruction 120 (SYSCALL): can't calculate
             bonus of height unequal (BlockHeight + 1)",
            "gasconsumed" : "198754",
            "notifications" : [],
            "stack" : [
               {
                  "type" : "Integer",
                  "value" : "4704605"
               },
               {
                  "type" : "ByteString",
                  "value" : "KfYYlDe/fxqqqm1yr7o5XLnQ7uk="
               }
            ],
            "trigger" : "Application",
            "vmstate" : "FAULT"
         }
      ],
      "txid" :
      "0x61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3
      f"
   }
}

```
```
(base) ekaterinapavlova@MacBook-Air-4 neo-go % ./bin/neo-go contract
invokefunction -r https://rpc.t5.n3.nspcc.ru:20331 -w ./testnet_wallet
.json 1e6f88377a6c6bc4f683a5fc61eed5645ec5f123 unclaimedGas
e9eed0b95c39baaf726daaaa1a7fbf379418f629 4704605
Enter account NWtk9HYWsf1njtSzA3XNgwZXRtriACcJ9G password >
Warning: FAULT VM state returned from the RPC node: at instruction 120
(SYSCALL): can't calculate bonus of height unequal (BlockHeight + 1).
Use --force flag to send the transaction anyway.
```

Close #3589

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-10-04 16:55:12 +03:00
Roman Khimov
b1068b1ab9
Merge pull request #3596 from nspcc-dev/fix-test 2024-10-04 13:06:57 +03:00
Anna Shaleva
fadfac7041 native: fix formulae of unclaimed GAS calculation in test
The behaviour is the same, the result is also the same, but the meaning
is fixed.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-04 11:59:48 +03:00
Anna Shaleva
4a9003f551 native: fix typo in the variable documentation
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-10-04 11:56:02 +03:00
Roman Khimov
9a38360824
Merge pull request #3592 from nspcc-dev/org-linter
Linter: adapt org-wide linter
2024-09-26 15:06:45 +03:00
Roman Khimov
5847ca16ba
Merge pull request #3593 from nspcc-dev/vm-doc
docs: update vm loadhex example
2024-09-26 15:05:04 +03:00
Ekaterina Pavlova
0ae098bea2 docs: update vm loadhex example
Update example for Neo Legacy VM to N3.

Close #3588

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-26 14:51:23 +03:00
Ekaterina Pavlova
cf2e6296b7 *: fix linter gofmt errors
```
pkg/vm/stackitem/json_test.go:11                   gofmt       File is
not `gofmt`-ed with `-s` `-r 'interface{} -> any'`
pkg/core/native/native_test/cryptolib_test.go:471  gofmt       File is
not `gofmt`-ed with `-s` `-r 'interface{} -> any'`
pkg/rpcclient/nns/contract_test.go:585             gofmt       File is
not `gofmt`-ed with `-s` `-r 'interface{} -> any'`

```

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-26 13:29:04 +03:00
Ekaterina Pavlova
cf4d3b25d7 *: fix linter exhaustive errors
```
pkg/smartcontract/rpcbinding/binding.go:523:2      exhaustive  missing
cases in switch of type smartcontract.ParamType: smartcontract
.UnknownType
pkg/smartcontract/rpcbinding/binding.go:572:2      exhaustive  missing
cases in switch of type smartcontract.ParamType: smartcontract
.UnknownType
pkg/smartcontract/rpcbinding/binding.go:862:2      exhaustive  missing
cases in switch of type smartcontract.ParamType: smartcontract
.UnknownType, smartcontract.AnyType, smartcontract.BoolType,
smartcontract.IntegerType, smartcontract.ByteArrayType, smartcontract
.Hash160Type, smartcontract.Hash256Type, smartcontract.SignatureType,
smartcontract.InteropInterfaceType, smartcontract.VoidType
pkg/smartcontract/param_type.go:165:2              exhaustive  missing
cases in switch of type smartcontract.ParamType: smartcontract
.UnknownType
pkg/smartcontract/manifest/permission.go:103:2     exhaustive  missing
cases in switch of type manifest.PermissionType: manifest
.PermissionWildcard
pkg/services/notary/core_test.go:223:4             exhaustive  missing
cases in switch of type notary.RequestType: notary.Contract
pkg/services/notary/core_test.go:292:4             exhaustive  missing
cases in switch of type notary.RequestType: notary.Contract
pkg/services/oracle/jsonpath/jsonpath.go:62:3      exhaustive  missing
cases in switch of type jsonpath.pathTokenType: jsonpath.pathInvalid,
jsonpath.pathRoot, jsonpath.pathRightBracket, jsonpath.pathAsterisk,
jsonpath.pathComma, jsonpath.pathColon, jsonpath.pathIdentifier,
jsonpath.pathString, jsonpath.pathNumber
pkg/services/rpcsrv/server.go:2740:3               exhaustive  missing
cases in switch of type neorpc.EventID: neorpc.InvalidEventID, neorpc
.MissedEventID
pkg/services/rpcsrv/server.go:2804:2               exhaustive  missing
cases in switch of type neorpc.EventID: neorpc.InvalidEventID, neorpc
.MissedEventID
pkg/services/rpcsrv/server.go:2864:2               exhaustive  missing
cases in switch of type neorpc.EventID: neorpc.InvalidEventID, neorpc
.MissedEventID

pkg/vm/contract_checks.go:153:3                    exhaustive  missing
cases in switch of type opcode.Opcode: opcode.PUSHINT8, opcode
.PUSHINT16, opcode.PUSHINT32, opcode.PUSHINT64, opcode.PUSHINT128,
opcode.PUSHINT256, opcode.PUSHT, opcode.PUSHF, opcode.PUSHNULL, opcode
.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4, opcode.PUSHM1, opcode
.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, opcode.PUSH4, opcode
.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode
.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14,
opcode.PUSH15, opcode.PUSH16, opcode.NOP, opcode.CALLA, opcode.CALLT,
opcode.ABORT, opcode.ASSERT, opcode.THROW, opcode.ENDFINALLY, opcode
.RET, opcode.SYSCALL, opcode.DEPTH, opcode.DROP, opcode.NIP, opcode
.XDROP, opcode.CLEAR, opcode.DUP, opcode.OVER, opcode.PICK, opcode.TUCK,
 opcode.SWAP, opcode.ROT, opcode.ROLL, opcode.REVERSE3, opcode.REVERSE4,
  opcode.REVERSEN, opcode.INITSSLOT, opcode.INITSLOT, opcode.LDSFLD0,
  opcode.LDSFLD1, opcode.LDSFLD2, opcode.LDSFLD3, opcode.LDSFLD4, opcode
  .LDSFLD5, opcode.LDSFLD6, opcode.LDSFLD, opcode.STSFLD0, opcode
  .STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode
  .STSFLD5, opcode.STSFLD6, opcode.STSFLD, opcode.LDLOC0, opcode.LDLOC1,
   opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode
   .LDLOC6, opcode.LDLOC, opcode.STLOC0, opcode.STLOC1, opcode.STLOC2,
   opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode.STLOC6, opcode
   .STLOC, opcode.LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3,
   opcode.LDARG4, opcode.LDARG5, opcode.LDARG6, opcode.LDARG, opcode
   .STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4,
   opcode.STARG5, opcode.STARG6, opcode.STARG, opcode.NEWBUFFER, opcode
   .MEMCPY, opcode.CAT, opcode.SUBSTR, opcode.LEFT, opcode.RIGHT, opcode
   .INVERT, opcode.AND, opcode.OR, opcode.XOR, opcode.EQUAL, opcode
   .NOTEQUAL, opcode.SIGN, opcode.ABS, opcode.NEGATE, opcode.INC, opcode
   .DEC, opcode.ADD, opcode.SUB, opcode.MUL, opcode.DIV, opcode.MOD,
   opcode.POW, opcode.SQRT, opcode.MODMUL, opcode.MODPOW, opcode.SHL,
   opcode.SHR, opcode.NOT, opcode.BOOLAND, opcode.BOOLOR, opcode.NZ,
   opcode.NUMEQUAL, opcode.NUMNOTEQUAL, opcode.LT, opcode.LE, opcode.GT,
    opcode.GE, opcode.MIN, opcode.MAX, opcode.WITHIN, opcode.PACKMAP,
    opcode.PACKSTRUCT, opcode.PACK, opcode.UNPACK, opcode.NEWARRAY0,
    opcode.NEWARRAY, opcode.NEWSTRUCT0, opcode.NEWSTRUCT, opcode.NEWMAP,
     opcode.SIZE, opcode.HASKEY, opcode.KEYS, opcode.VALUES, opcode
     .PICKITEM, opcode.APPEND, opcode.SETITEM, opcode.REVERSEITEMS,
     opcode.REMOVE, opcode.CLEARITEMS, opcode.POPITEM, opcode.ISNULL,
     opcode.ABORTMSG, opcode.ASSERTMSG
pkg/vm/vm.go:912:3                                 exhaustive  missing
cases in switch of type opcode.Opcode: opcode.PUSHINT8, opcode
.PUSHINT16, opcode.PUSHINT32, opcode.PUSHINT64, opcode.PUSHINT128,
opcode.PUSHINT256, opcode.PUSHT, opcode.PUSHF, opcode.PUSHA, opcode
.PUSHNULL, opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4, opcode
.PUSHM1, opcode.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, opcode
.PUSH4, opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode
.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13,
opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.NOP, opcode.JMP,
opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode
.JMPIFNOTL, opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL,
opcode.JMPGT, opcode.JMPGTL, opcode.JMPGE, opcode.JMPGEL, opcode.JMPLT,
opcode.JMPLTL, opcode.JMPLE, opcode.JMPLEL, opcode.CALL, opcode.CALLL,
opcode.CALLA, opcode.CALLT, opcode.ABORT, opcode.ASSERT, opcode.THROW,
opcode.TRY, opcode.TRYL, opcode.ENDTRY, opcode.ENDTRYL, opcode
.ENDFINALLY, opcode.RET, opcode.SYSCALL, opcode.DEPTH, opcode.DROP,
opcode.NIP, opcode.XDROP, opcode.CLEAR, opcode.DUP, opcode.OVER, opcode
.PICK, opcode.TUCK, opcode.SWAP, opcode.ROT, opcode.ROLL, opcode
.REVERSE3, opcode.INITSSLOT, opcode.INITSLOT, opcode.LDSFLD0, opcode
.LDSFLD1, opcode.LDSFLD2, opcode.LDSFLD3, opcode.LDSFLD4, opcode
.LDSFLD5, opcode.LDSFLD6, opcode.LDSFLD, opcode.STSFLD0, opcode.STSFLD1,
 opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode.STSFLD5, opcode
 .STSFLD6, opcode.STSFLD, opcode.LDLOC0, opcode.LDLOC1, opcode.LDLOC2,
 opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode.LDLOC6, opcode
 .LDLOC, opcode.STLOC0, opcode.STLOC1, opcode.STLOC2, opcode.STLOC3,
 opcode.STLOC4, opcode.STLOC5, opcode.STLOC6, opcode.STLOC, opcode
 .LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3, opcode.LDARG4,
 opcode.LDARG5, opcode.LDARG6, opcode.LDARG, opcode.STARG0, opcode
 .STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4, opcode.STARG5,
 opcode.STARG6, opcode.STARG, opcode.NEWBUFFER, opcode.MEMCPY, opcode
 .CAT, opcode.SUBSTR, opcode.LEFT, opcode.RIGHT, opcode.INVERT, opcode
 .AND, opcode.OR, opcode.XOR, opcode.EQUAL, opcode.NOTEQUAL, opcode
 .SIGN, opcode.ABS, opcode.NEGATE, opcode.INC, opcode.DEC, opcode.ADD,
 opcode.SUB, opcode.MUL, opcode.DIV, opcode.MOD, opcode.POW, opcode
 .SQRT, opcode.MODMUL, opcode.MODPOW, opcode.SHL, opcode.SHR, opcode
 .NOT, opcode.BOOLAND, opcode.BOOLOR, opcode.NZ, opcode.NUMEQUAL, opcode
 .NUMNOTEQUAL, opcode.LT, opcode.LE, opcode.GT, opcode.GE, opcode.MIN,
 opcode.MAX, opcode.WITHIN, opcode.PACKMAP, opcode.PACKSTRUCT, opcode
 .PACK, opcode.UNPACK, opcode.NEWARRAY0, opcode.NEWARRAY, opcode
 .NEWARRAYT, opcode.NEWSTRUCT0, opcode.NEWSTRUCT, opcode.NEWMAP, opcode
 .SIZE, opcode.HASKEY, opcode.KEYS, opcode.VALUES, opcode.PICKITEM,
 opcode.APPEND, opcode.SETITEM, opcode.REVERSEITEMS, opcode.REMOVE,
 opcode.CLEARITEMS, opcode.POPITEM, opcode.ISNULL, opcode.ISTYPE, opcode
 .CONVERT, opcode.ABORTMSG, opcode.ASSERTMSG
pkg/vm/vm.go:1116:4                                exhaustive  missing
cases in switch of type opcode.Opcode: opcode.PUSHINT8, opcode
.PUSHINT16, opcode.PUSHINT32, opcode.PUSHINT64, opcode.PUSHINT128,
opcode.PUSHINT256, opcode.PUSHT, opcode.PUSHF, opcode.PUSHA, opcode
.PUSHNULL, opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4, opcode
.PUSHM1, opcode.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, opcode
.PUSH4, opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode
.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13,
opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.NOP, opcode.JMP,
opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode
.JMPIFNOTL, opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL,
opcode.JMPGT, opcode.JMPGTL, opcode.JMPGE, opcode.JMPGEL, opcode.JMPLT,
opcode.JMPLTL, opcode.JMPLE, opcode.JMPLEL, opcode.CALL, opcode.CALLL,
opcode.CALLA, opcode.CALLT, opcode.ABORT, opcode.ASSERT, opcode.THROW,
opcode.TRY, opcode.TRYL, opcode.ENDTRY, opcode.ENDTRYL, opcode
.ENDFINALLY, opcode.RET, opcode.SYSCALL, opcode.DEPTH, opcode.DROP,
opcode.NIP, opcode.XDROP, opcode.CLEAR, opcode.DUP, opcode.OVER, opcode
.PICK, opcode.TUCK, opcode.SWAP, opcode.ROT, opcode.ROLL, opcode
.REVERSE3, opcode.REVERSE4, opcode.REVERSEN, opcode.INITSSLOT, opcode
.INITSLOT, opcode.LDSFLD0, opcode.LDSFLD1, opcode.LDSFLD2, opcode
.LDSFLD3, opcode.LDSFLD4, opcode.LDSFLD5, opcode.LDSFLD6, opcode.LDSFLD,
 opcode.STSFLD0, opcode.STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode
 .STSFLD4, opcode.STSFLD5, opcode.STSFLD6, opcode.STSFLD, opcode.LDLOC0,
  opcode.LDLOC1, opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode
  .LDLOC5, opcode.LDLOC6, opcode.LDLOC, opcode.STLOC0, opcode.STLOC1,
  opcode.STLOC2, opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode
  .STLOC6, opcode.STLOC, opcode.LDARG0, opcode.LDARG1, opcode.LDARG2,
  opcode.LDARG3, opcode.LDARG4, opcode.LDARG5, opcode.LDARG6, opcode
  .LDARG, opcode.STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3,
  opcode.STARG4, opcode.STARG5, opcode.STARG6, opcode.STARG, opcode
  .NEWBUFFER, opcode.MEMCPY, opcode.CAT, opcode.SUBSTR, opcode.LEFT,
  opcode.RIGHT, opcode.INVERT, opcode.AND, opcode.OR, opcode.XOR, opcode
  .EQUAL, opcode.NOTEQUAL, opcode.SIGN, opcode.ABS, opcode.NEGATE,
  opcode.INC, opcode.DEC, opcode.ADD, opcode.SUB, opcode.MUL, opcode
  .DIV, opcode.MOD, opcode.POW, opcode.SQRT, opcode.MODMUL, opcode
  .MODPOW, opcode.SHL, opcode.SHR, opcode.NOT, opcode.BOOLAND, opcode
  .BOOLOR, opcode.NZ, opcode.NUMEQUAL, opcode.NUMNOTEQUAL, opcode.MIN,
  opcode.MAX, opcode.WITHIN, opcode.PACKMAP, opcode.PACKSTRUCT, opcode
  .PACK, opcode.UNPACK, opcode.NEWARRAY0, opcode.NEWARRAY, opcode
  .NEWARRAYT, opcode.NEWSTRUCT0, opcode.NEWSTRUCT, opcode.NEWMAP, opcode
  .SIZE, opcode.HASKEY, opcode.KEYS, opcode.VALUES, opcode.PICKITEM,
  opcode.APPEND, opcode.SETITEM, opcode.REVERSEITEMS, opcode.REMOVE,
  opcode.CLEARITEMS, opcode.POPITEM, opcode.ISNULL, opcode.ISTYPE,
  opcode.CONVERT, opcode.ABORTMSG, opcode.ASSERTMSG

pkg/compiler/codegen.go:944:5                      exhaustive  missing
cases in switch of type smartcontract.ParamType: smartcontract
.UnknownType, smartcontract.AnyType, smartcontract.BoolType,
smartcontract.IntegerType, smartcontract.ByteArrayType, smartcontract
.StringType, smartcontract.PublicKeyType, smartcontract.SignatureType,
smartcontract.ArrayType, smartcontract.MapType, smartcontract
.InteropInterfaceType, smartcontract.VoidType
pkg/compiler/codegen.go:1221:3                     exhaustive  missing
cases in switch of type token.Token: token.ILLEGAL, token.EOF, token
.COMMENT, token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR,
token.STRING, token.ADD, token.SUB, token.MUL, token.QUO, token.REM,
token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT,
token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN,
token.REM_ASSIGN, token.AND_ASSIGN, token.OR_ASSIGN, token.XOR_ASSIGN,
token.SHL_ASSIGN, token.SHR_ASSIGN, token.AND_NOT_ASSIGN, token.LAND,
token.LOR, token.ARROW, token.INC, token.DEC, token.EQL, token.LSS,
token.GTR, token.ASSIGN, token.NOT, token.NEQ, token.LEQ, token.GEQ,
token.DEFINE, token.ELLIPSIS, token.LPAREN, token.LBRACK, token.LBRACE,
token.COMMA, token.PERIOD, token.RPAREN, token.RBRACK, token.RBRACE,
token.SEMICOLON, token.COLON, token.CASE, token.CHAN, token.CONST, token
.DEFAULT, token.DEFER, token.ELSE, token.FALLTHROUGH, token.FOR, token
.FUNC, token.GO, token.GOTO, token.IF, token.IMPORT, token.INTERFACE,
token.MAP, token.PACKAGE, token.RANGE, token.RETURN, token.SELECT, token
.STRUCT, token.SWITCH, token.TYPE, token.VAR, token.TILDE
pkg/compiler/codegen.go:1709:2                     exhaustive  missing
cases in switch of type token.Token: token.ILLEGAL, token.EOF, token
.COMMENT, token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR,
token.STRING, token.ADD, token.SUB, token.MUL, token.QUO, token.REM,
token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT,
token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN,
token.REM_ASSIGN, token.AND_ASSIGN, token.OR_ASSIGN, token.XOR_ASSIGN,
token.SHL_ASSIGN, token.SHR_ASSIGN, token.AND_NOT_ASSIGN, token.LAND,
token.LOR, token.ARROW, token.INC, token.DEC, token.ASSIGN, token.NOT,
token.DEFINE, token.ELLIPSIS, token.LPAREN, token.LBRACK, token.LBRACE,
token.COMMA, token.PERIOD, token.RPAREN, token.RBRACK, token.RBRACE,
token.SEMICOLON, token.COLON, token.BREAK, token.CASE, token.CHAN, token
.CONST, token.CONTINUE, token.DEFAULT, token.DEFER, token.ELSE, token
.FALLTHROUGH, token.FOR, token.FUNC, token.GO, token.GOTO, token.IF,
token.IMPORT, token.INTERFACE, token.MAP, token.PACKAGE, token.RANGE,
token.RETURN, token.SELECT, token.STRUCT, token.SWITCH, token.TYPE,
token.VAR, token.TILDE
pkg/compiler/codegen.go:2353:3                     exhaustive  missing
cases in switch of type opcode.Opcode: opcode.PUSHINT8, opcode
.PUSHINT16, opcode.PUSHINT32, opcode.PUSHINT64, opcode.PUSHINT128,
opcode.PUSHINT256, opcode.PUSHT, opcode.PUSHF, opcode.PUSHNULL, opcode
.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4, opcode.PUSHM1, opcode
.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, opcode.PUSH4, opcode
.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode
.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14,
opcode.PUSH15, opcode.PUSH16, opcode.NOP, opcode.CALLA, opcode.CALLT,
opcode.ABORT, opcode.ASSERT, opcode.THROW, opcode.TRY, opcode.ENDTRY,
opcode.ENDFINALLY, opcode.RET, opcode.SYSCALL, opcode.DEPTH, opcode
.DROP, opcode.NIP, opcode.XDROP, opcode.CLEAR, opcode.DUP, opcode.OVER,
opcode.PICK, opcode.TUCK, opcode.SWAP, opcode.ROT, opcode.ROLL, opcode
.REVERSE3, opcode.REVERSE4, opcode.REVERSEN, opcode.INITSSLOT, opcode
.LDSFLD0, opcode.LDSFLD1, opcode.LDSFLD2, opcode.LDSFLD3, opcode
.LDSFLD4, opcode.LDSFLD5, opcode.LDSFLD6, opcode.LDSFLD, opcode.STSFLD0,
 opcode.STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode
 .STSFLD5, opcode.STSFLD6, opcode.STSFLD, opcode.LDLOC0, opcode.LDLOC1,
 opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode
 .LDLOC6, opcode.LDLOC, opcode.STLOC0, opcode.STLOC1, opcode.STLOC2,
 opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode.STLOC6, opcode
 .STLOC, opcode.LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3,
 opcode.LDARG4, opcode.LDARG5, opcode.LDARG6, opcode.LDARG, opcode
 .STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4,
 opcode.STARG5, opcode.STARG6, opcode.STARG, opcode.NEWBUFFER, opcode
 .MEMCPY, opcode.CAT, opcode.SUBSTR, opcode.LEFT, opcode.RIGHT, opcode
 .INVERT, opcode.AND, opcode.OR, opcode.XOR, opcode.EQUAL, opcode
 .NOTEQUAL, opcode.SIGN, opcode.ABS, opcode.NEGATE, opcode.INC, opcode
 .DEC, opcode.ADD, opcode.SUB, opcode.MUL, opcode.DIV, opcode.MOD,
 opcode.POW, opcode.SQRT, opcode.MODMUL, opcode.MODPOW, opcode.SHL,
 opcode.SHR, opcode.NOT, opcode.BOOLAND, opcode.BOOLOR, opcode.NZ,
 opcode.NUMEQUAL, opcode.NUMNOTEQUAL, opcode.LT, opcode.LE, opcode.GT,
 opcode.GE, opcode.MIN, opcode.MAX, opcode.WITHIN, opcode.PACKMAP,
 opcode.PACKSTRUCT, opcode.PACK, opcode.UNPACK, opcode.NEWARRAY0, opcode
 .NEWARRAY, opcode.NEWARRAYT, opcode.NEWSTRUCT0, opcode.NEWSTRUCT,
 opcode.NEWMAP, opcode.SIZE, opcode.HASKEY, opcode.KEYS, opcode.VALUES,
 opcode.PICKITEM, opcode.APPEND, opcode.SETITEM, opcode.REVERSEITEMS,
 opcode.REMOVE, opcode.CLEARITEMS, opcode.POPITEM, opcode.ISNULL, opcode
 .ISTYPE, opcode.CONVERT, opcode.ABORTMSG, opcode.ASSERTMSG
pkg/compiler/codegen.go:2474:3                     exhaustive  missing
cases in switch of type opcode.Opcode: opcode.PUSHINT8, opcode
.PUSHINT16, opcode.PUSHINT32, opcode.PUSHINT64, opcode.PUSHINT128,
opcode.PUSHINT256, opcode.PUSHT, opcode.PUSHF, opcode.PUSHNULL, opcode
.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4, opcode.PUSHM1, opcode
.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, opcode.PUSH4, opcode
.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode
.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14,
opcode.PUSH15, opcode.PUSH16, opcode.NOP, opcode.CALLA, opcode.CALLT,
opcode.ABORT, opcode.ASSERT, opcode.THROW, opcode.ENDFINALLY, opcode
.RET, opcode.SYSCALL, opcode.DEPTH, opcode.DROP, opcode.NIP, opcode
.XDROP, opcode.CLEAR, opcode.DUP, opcode.OVER, opcode.PICK, opcode.TUCK,
 opcode.SWAP, opcode.ROT, opcode.ROLL, opcode.REVERSE3, opcode.REVERSE4,
  opcode.REVERSEN, opcode.INITSSLOT, opcode.INITSLOT, opcode.LDSFLD0,
  opcode.LDSFLD1, opcode.LDSFLD2, opcode.LDSFLD3, opcode.LDSFLD4, opcode
  .LDSFLD5, opcode.LDSFLD6, opcode.LDSFLD, opcode.STSFLD0, opcode
  .STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode
  .STSFLD5, opcode.STSFLD6, opcode.STSFLD, opcode.LDLOC0, opcode.LDLOC1,
   opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode
   .LDLOC6, opcode.LDLOC, opcode.STLOC0, opcode.STLOC1, opcode.STLOC2,
   opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode.STLOC6, opcode
   .STLOC, opcode.LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3,
   opcode.LDARG4, opcode.LDARG5, opcode.LDARG6, opcode.LDARG, opcode
   .STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4,
   opcode.STARG5, opcode.STARG6, opcode.STARG, opcode.NEWBUFFER, opcode
   .MEMCPY, opcode.CAT, opcode.SUBSTR, opcode.LEFT, opcode.RIGHT, opcode
   .INVERT, opcode.AND, opcode.OR, opcode.XOR, opcode.EQUAL, opcode
   .NOTEQUAL, opcode.SIGN, opcode.ABS, opcode.NEGATE, opcode.INC, opcode
   .DEC, opcode.ADD, opcode.SUB, opcode.MUL, opcode.DIV, opcode.MOD,
   opcode.POW, opcode.SQRT, opcode.MODMUL, opcode.MODPOW, opcode.SHL,
   opcode.SHR, opcode.NOT, opcode.BOOLAND, opcode.BOOLOR, opcode.NZ,
   opcode.NUMEQUAL, opcode.NUMNOTEQUAL, opcode.LT, opcode.LE, opcode.GT,
    opcode.GE, opcode.MIN, opcode.MAX, opcode.WITHIN, opcode.PACKMAP,
    opcode.PACKSTRUCT, opcode.PACK, opcode.UNPACK, opcode.NEWARRAY0,
    opcode.NEWARRAY, opcode.NEWARRAYT, opcode.NEWSTRUCT0, opcode
    .NEWSTRUCT, opcode.NEWMAP, opcode.SIZE, opcode.HASKEY, opcode.KEYS,
    opcode.VALUES, opcode.PICKITEM, opcode.APPEND, opcode.SETITEM,
    opcode.REVERSEITEMS, opcode.REMOVE, opcode.CLEARITEMS, opcode
    .POPITEM, opcode.ISNULL, opcode.ISTYPE, opcode.CONVERT, opcode
    .ABORTMSG, opcode.ASSERTMSG
pkg/compiler/inline_test.go:34:3                   exhaustive  missing
cases in switch of type opcode.Opcode: opcode.PUSHINT8, opcode
.PUSHINT16, opcode.PUSHINT32, opcode.PUSHINT64, opcode.PUSHINT128,
opcode.PUSHINT256, opcode.PUSHT, opcode.PUSHF, opcode.PUSHA, opcode
.PUSHNULL, opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4, opcode
.PUSHM1, opcode.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, opcode
.PUSH4, opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode
.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13,
opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.NOP, opcode.JMP,
opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode
.JMPIFNOTL, opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL,
opcode.JMPGT, opcode.JMPGTL, opcode.JMPGE, opcode.JMPGEL, opcode.JMPLT,
opcode.JMPLTL, opcode.JMPLE, opcode.JMPLEL, opcode.CALLA, opcode.CALLT,
opcode.ABORT, opcode.ASSERT, opcode.THROW, opcode.TRY, opcode.TRYL,
opcode.ENDTRY, opcode.ENDTRYL, opcode.ENDFINALLY, opcode.RET, opcode
.SYSCALL, opcode.DEPTH, opcode.DROP, opcode.NIP, opcode.XDROP, opcode
.CLEAR, opcode.DUP, opcode.OVER, opcode.PICK, opcode.TUCK, opcode.SWAP,
opcode.ROT, opcode.ROLL, opcode.REVERSE3, opcode.REVERSE4, opcode
.REVERSEN, opcode.LDSFLD0, opcode.LDSFLD1, opcode.LDSFLD2, opcode
.LDSFLD3, opcode.LDSFLD4, opcode.LDSFLD5, opcode.LDSFLD6, opcode.LDSFLD,
 opcode.STSFLD0, opcode.STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode
 .STSFLD4, opcode.STSFLD5, opcode.STSFLD6, opcode.STSFLD, opcode.LDLOC0,
  opcode.LDLOC1, opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode
  .LDLOC5, opcode.LDLOC6, opcode.LDLOC, opcode.STLOC0, opcode.STLOC1,
  opcode.STLOC2, opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode
  .STLOC6, opcode.STLOC, opcode.LDARG0, opcode.LDARG1, opcode.LDARG2,
  opcode.LDARG3, opcode.LDARG4, opcode.LDARG5, opcode.LDARG6, opcode
  .LDARG, opcode.STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3,
  opcode.STARG4, opcode.STARG5, opcode.STARG6, opcode.STARG, opcode
  .NEWBUFFER, opcode.MEMCPY, opcode.CAT, opcode.SUBSTR, opcode.LEFT,
  opcode.RIGHT, opcode.INVERT, opcode.AND, opcode.OR, opcode.XOR, opcode
  .EQUAL, opcode.NOTEQUAL, opcode.SIGN, opcode.ABS, opcode.NEGATE,
  opcode.INC, opcode.DEC, opcode.ADD, opcode.SUB, opcode.MUL, opcode
  .DIV, opcode.MOD, opcode.POW, opcode.SQRT, opcode.MODMUL, opcode
  .MODPOW, opcode.SHL, opcode.SHR, opcode.NOT, opcode.BOOLAND, opcode
  .BOOLOR, opcode.NZ, opcode.NUMEQUAL, opcode.NUMNOTEQUAL, opcode.LT,
  opcode.LE, opcode.GT, opcode.GE, opcode.MIN, opcode.MAX, opcode
  .WITHIN, opcode.PACKMAP, opcode.PACKSTRUCT, opcode.PACK, opcode
  .UNPACK, opcode.NEWARRAY0, opcode.NEWARRAY, opcode.NEWARRAYT, opcode
  .NEWSTRUCT0, opcode.NEWSTRUCT, opcode.NEWMAP, opcode.SIZE, opcode
  .HASKEY, opcode.KEYS, opcode.VALUES, opcode.PICKITEM, opcode.APPEND,
  opcode.SETITEM, opcode.REVERSEITEMS, opcode.REMOVE, opcode.CLEARITEMS,
   opcode.POPITEM, opcode.ISNULL, opcode.ISTYPE, opcode.CONVERT, opcode
   .ABORTMSG, opcode.ASSERTMSG

pkg/network/server.go:1395:3                       exhaustive  missing
cases in switch of type network.CommandType: network.CMDNotFound,
network.CMDReject, network.CMDFilterLoad, network.CMDFilterAdd, network
.CMDFilterClear, network.CMDMerkleBlock, network.CMDAlert
pkg/network/server_test.go:532:3                   exhaustive  missing
cases in switch of type network.CommandType: network.CMDVersion, network
.CMDVerack, network.CMDGetAddr, network.CMDAddr, network.CMDPing,
network.CMDPong, network.CMDGetHeaders, network.CMDHeaders, network
.CMDGetBlocks, network.CMDMempool, network.CMDInv, network.CMDGetData,
network.CMDGetBlockByIndex, network.CMDGetMPTData, network.CMDMPTData,
network.CMDReject, network.CMDFilterLoad, network.CMDFilterAdd, network
.CMDFilterClear, network.CMDMerkleBlock, network.CMDAlert
pkg/network/server_test.go:817:4                   exhaustive  missing
cases in switch of type network.CommandType: network.CMDVersion, network
.CMDVerack, network.CMDGetAddr, network.CMDAddr, network.CMDPing,
network.CMDPong, network.CMDGetHeaders, network.CMDHeaders, network
.CMDGetBlocks, network.CMDMempool, network.CMDInv, network.CMDGetData,
network.CMDGetBlockByIndex, network.CMDNotFound, network.CMDTX, network
.CMDBlock, network.CMDExtensible, network.CMDP2PNotaryRequest, network
.CMDGetMPTData, network.CMDReject, network.CMDFilterLoad, network
.CMDFilterAdd, network.CMDFilterClear, network.CMDMerkleBlock, network
.CMDAlert

pkg/core/native/designate.go:262:2                 exhaustive  missing
cases in switch of type noderoles.Role: noderoles.NeoFSAlphabet

pkg/neorpc/rpcevent/filter.go:36:2  exhaustive  missing cases in switch
of type neorpc.EventID: neorpc.InvalidEventID, neorpc.MissedEventID

pkg/consensus/recovery_message.go:145:2            exhaustive  missing
cases in switch of type dbft.MessageType: dbft.PreCommitType, dbft
.RecoveryRequestType, dbft.RecoveryMessageType

cli/cmdargs/parser.go:202:3  exhaustive  missing cases in switch of type
 transaction.WitnessScope: transaction.None, transaction.CalledByEntry,
 transaction.Rules, transaction.Global

```

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-26 13:29:03 +03:00
Ekaterina Pavlova
379ca4b4e6 linter: use org-wide version of linter
Close #3591,
Close #3417.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-25 21:47:45 +03:00
Anna Shaleva
e1d5ac8557
Merge pull request #3515 from nspcc-dev/block-fetcher
services: add new block fetching from NeoFS service
2024-09-12 11:45:31 +05:00
Roman Khimov
4445a0c42c
Merge pull request #3584 from nspcc-dev/allow-null-structs
rpcbinding: handle NULL results for structures, fix #3581
2024-09-12 09:02:08 +03:00
Roman Khimov
80dd6359a5
Merge pull request #3583 from nspcc-dev/extend-accepted-parameters
Extend accepted parameters
2024-09-12 08:57:04 +03:00
Roman Khimov
4e3f17d06f rpcbinding: handle NULL results for structures, fix #3581
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-11 18:54:16 +03:00
Roman Khimov
8469f97d09 smartcontract: extend slice types accepted by NewParameterFromValue
It's _very_ annoying to not be able to use []string types properly:
    test invocation failed: unsupported parameter []string

But the same thing can happen to any other slice, so accept slices of every
basic type we accept above. Reorder tests to match implementation switch as
well.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-11 17:08:01 +03:00
Roman Khimov
f80f453933 smartcontract: use generics to simplify slice handling
It's all the same in its essence.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-11 16:22:46 +03:00
Ekaterina Pavlova
0b31a29f39 services: add new service for fetching blocks from NeoFS
Close #3496

Co-authored-by: Anna Shaleva <shaleva.ann@nspcc.ru>
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-11 08:50:31 +04:00
Ekaterina Pavlova
5dff3fc13d neofs: add GetSDKClient, ObjectSearch and GetWithClient
Could be used for operation with neofs.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-10 13:29:54 +04:00
Ekaterina Pavlova
8846f648c3 cli: add dump-bin command
Dump blocks (starting with the genesis or specified block) to the
directory in binary format.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-10 13:27:59 +04:00
Anna Shaleva
6bbf15f305
Merge pull request #3579 from nspcc-dev/dependabot/go_modules/github.com/consensys/gnark-0.11.0
build(deps): bump github.com/consensys/gnark from 0.10.0 to 0.11.0
2024-09-09 15:37:08 +05:00
dependabot[bot]
5108121c0f
build(deps): bump github.com/consensys/gnark from 0.10.0 to 0.11.0
Bumps [github.com/consensys/gnark](https://github.com/consensys/gnark) from 0.10.0 to 0.11.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.10.0...v0.11.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 10:18:54 +00:00
Anna Shaleva
44a87769e5
Merge pull request #3576 from nspcc-dev/dependabot/go_modules/examples/zkp/cubic_circuit/github.com/consensys/gnark-0.11.0
build(deps): bump github.com/consensys/gnark from 0.10.0 to 0.11.0 in /examples/zkp/cubic_circuit
2024-09-09 15:18:20 +05:00
Ekaterina Pavlova
69b655ec7a queue: add Blocking OperationMode
If Blocking mode is on PutBlock will block until there is enough space
in the queue.

Co-authored-by: Anna Shaleva <shaleva.ann@nspcc.ru>
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-09 10:28:15 +04:00
dependabot[bot]
e5eef2fbc7
build(deps): bump github.com/consensys/gnark
Bumps [github.com/consensys/gnark](https://github.com/consensys/gnark) from 0.10.0 to 0.11.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.10.0...v0.11.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-06 19:40:53 +00:00
Ekaterina Pavlova
6f2712ee55 network: make cash size of bqueue configurable
Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-09-06 19:17:20 +04:00
Anna Shaleva
d9a6a7cd3f
Merge pull request #3572 from nspcc-dev/feat/add-for-range-linter
.golangci.yml: add `intrange` linter
2024-09-05 18:02:10 +05:00
Anna Shaleva
585af7e596
Merge pull request #3570 from nspcc-dev/dep-upgrade
Dependency upgrade
2024-09-05 18:01:46 +05:00
Pavel Karpy
f8549a4fb8 mpt: refactor lcp to be possible to use range
It took some time to understand why changing a regular `for` to a `range`
one leads to behavior changes; let it be more clear and explicit. Also, a
correct code is always better than a correct code with `nolint`.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2024-09-04 14:15:44 +03:00
Pavel Karpy
32f91dd726 .golangci.yml: add intrange linter
It checks that go 1.22's range-over-numbers feature is not skipped. Also,
fix some warnings it found.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2024-09-04 14:15:32 +03:00
Roman Khimov
09921bb3a4 check_deps: fix the check for stable NeoGo revisions used in nft-nd-nns
vX.Y.Z-date-commit is very much different from vX.Y.Z and we can have any of
them for NeoGo (vX.Y.Z is even preferable). Previous code ended up this way
for v0.106.3:

++ sed -E -n -e 's/.*neo-go\s.+-.+-(\w+)/\1/ p' examples/nft-nd-nns//go.mod
+ NEO_GO_COMMIT=
+ git merge-base --is-ancestor '' HEAD
fatal: Not a valid object name
+ die 'examples/nft-nd-nns/: neo-go commit  was not found in git'
+ echo 'examples/nft-nd-nns/: neo-go commit  was not found in git'
examples/nft-nd-nns/: neo-go commit  was not found in git
+ exit 1

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 11:03:01 +03:00
Roman Khimov
f614cc3d45 scripts: move 'em to a separate Go module
Which allows to drop two direct dependencies specific to scripts from the main
go.mod.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
f0ecc9764d examples: update neo-go dependency to v0.106.3
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
d96f2db6dd examples: update github.com/stretchr/testify dependency to 1.9.0
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
9ebf04400e go.mod: upgrade golang.org/x/tools to v0.24.0
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
894e53e697 go.mod: upgrade golang.org/x/crypto to v0.26.0
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
a8e73a632d go.mod: update go.etcd.io/bbolt to v1.3.11
Minor fixes and Go 1.22.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
7da0c900ca go.mod: upgrade github.com/urfave/cli/v2 to v2.27.4
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
702ab131e3 go.mod: upgrade github.com/prometheus/client_golang to v1.20.2
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
50657ddf32 go.mod: update our internal dependencies
go-ordered-json and rfc6979, nothing has really changed there.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
da4500d8f1 go.mod: upgrade github.com/holiman/uint256 to v1.3.1
Heavily optimized one.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
fed47df11a go.mod: upgrade github.com/gorilla/websocket to v1.5.3
It's maintained now again! A lot of fixes went in, but it seems to be ok
in tests.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
4249fddc36 go.mod: update github.com/decred/dcrd/dcrec/secp256k1/v4 to v4.3.0
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
7c356163e2 go.mod: upgrade github.com/consensys/gnark-crypto to v0.13.0
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-09-04 10:49:58 +03:00
Roman Khimov
d47fe392fb
Merge pull request #3567 from nspcc-dev/go-1.22
Go 1.22
2024-09-02 22:32:40 +03:00
Roman Khimov
565f8cfb7a *: fix copyloopvar warnings
Go 1.22+ allows to drop these:

  The copy of the 'for' variable "i" can be deleted (Go 1.22+) (copyloopvar)

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 22:02:57 +03:00
Roman Khimov
74cf5cbeae vm: drop *vm.VM parameter of CheckMultisigPar()
It's unused. Maybe we can move it out of VM completely decoupling VM from
crypto.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 21:48:27 +03:00
Roman Khimov
1b83dc2476 *: improve for loop syntax
Mostly it's about Go 1.22+ syntax with ranging over integers, but it also
prefers ranging over slices where possible (it makes code a little better to
read).

Notice that we have a number of dangerous loops where slices are mutated
during loop execution, many of these can't be converted since we need proper
length evalutation at every iteration.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 21:45:18 +03:00
Roman Khimov
133cd1dcf8 rpcbinding: use slices.Delete* for element filtering
It's a bit easier this way, loops that change slices aren't fun.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 21:35:27 +03:00
Roman Khimov
0fec17d7c0 bqueue: simplify queue flush on Discard()
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 21:30:00 +03:00
Roman Khimov
8f45d57612 *: stop using math/rand
Mostly this switches to math/rand/v2, but sometimes randomness is not really needed.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 17:00:11 +03:00
Roman Khimov
a50723ff72 *: use cmp.Or where appropriate
It's slightly less efficient (all comparisons are always made), but for
strings/ints it's negligible performance difference, while the code looks a
tiny bit better.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 17:00:11 +03:00
Roman Khimov
357bc76882 *: use slices.Concat where appropriate
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 17:00:11 +03:00
Roman Khimov
d5b7fc54e7 manifest: deduplicate test logic a bit with slices.Concat
LIkely it's more readable this way.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 17:00:11 +03:00
Roman Khimov
dfcff64acb go.mod: update to Go 1.22+
This also changes workflows to test 1.22 and 1.23 only (refs. https://github.com/nspcc-dev/.github/issues/30).
Fixes #3310.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-30 17:00:11 +03:00
Anna Shaleva
ff979e7ad2
Merge pull request #3563 from nspcc-dev/go-1.21
Go 1.21
2024-08-30 16:24:11 +05:00
Roman Khimov
8bececb511 go.mod: update github.com/consensys/gnark to v0.10.0
Fixes a security issue, https://github.com/Consensys/gnark/issues/897

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 15:40:49 +03:00
Roman Khimov
c07604fbf3 notary: simplify key presence check with slices
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:51:12 +03:00
Roman Khimov
8925b42250 golangci.yml: replace outdated linter
level=warning msg="The linter 'exportloopref' is deprecated (since v1.60.2) due to: Since Go1.22 (loopvar) this linter is no longer relevant. Replaced by copyloopvar."

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:51:12 +03:00
Roman Khimov
6d4ebdcef3 *: return errors.ErrUnsupported where appropriate
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:51:12 +03:00
Roman Khimov
97506fb48d keys: rework nep-2/wif encoding without bytes.Buffer
It doesn't make any sense here and the length check was just a dead code.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
9bc67f9da6 keys: mute elliptic.Marshal deprecation warning
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
b4e4567c2b *: don't use CompareTo name for three-way comparison functions
"Compare" is almost a standard one now, although math/big uses Cmp for historic
reasons (keys.PublicKey does so too). This also fixes Fixed8 since int64 to int
conversion is lossy.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
a1a7e3d708 *: use slices package for sorting and searching
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
6581bd47fc interop: simplify code with slices.Insert
It's not performance-critical for sure.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
a0553f740d statesync: simplify Pool management code
* use slices.BinarySearchFunc with its boolean status
* use slices.Insert/slices.Delete, tnis can be a little less efficient, but it
  frees the memory faster and this code is more I/O (networking) bound to care
  about 1-3%

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
5431b31d84 core: fix extensible sender check
Looks like it was wrong since 9592f3e052 because
sort.Search can return an index that is not equal to the target.
slices.BinarySearchFunc can do that too, but it also return a very convenient
boolean status that can be used.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
74acfe5288 network: rework insertion into sorting in tryInitStateSync
Overall this'd be less operations and less allocations.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 12:29:44 +03:00
Roman Khimov
49438798b5 manifest: deduplicate duplicate-checking code
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
db820cb0dc manifest: rework method duplicate check
Don't use an additional type, it's inconvenient and this method is not
performance-critical.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
7f1690a840 manifest: use slices.SortFunc, deduplicate PermissionDesc code
Simplify Less and Equals, fix Equals as well for PermissionWildcard (it was
reporting false erroneously).

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
e0cf47e6d0 consensus: don't do useless key sorting
They're already sorted out of smartcontract.CreateMultiSigRedeemScript().

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
32e3310205 smartcontract: improve CreateMultiSigRedeemScript documentation
It was always like that, but this important aspect was never documented.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
de665b5567 *: use slices.Sort() where appropriate
It's always faster for simple types.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
d40ccb3e57 util: add Compare to Uint160
It's useful for slices-based sorting/finding.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
dda3d8b284 mempool: add some comments on potential slices.* use
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
a30792dbb6 *: use slices.Index/slices.Contains where appropriate
This doesn't touch performance-sensitive parts, but simplifies a lot of code.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
9cce148d83 *: use maps.Equal/slices.Equal where appropriate
Testing for equality has never been easier.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
93ecd61079 stackitem: microoptimize memory management of Make()
Preallocate as much as needed.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
e8a86e617b transaction: microoptimize multiattribute check
We can't have more than 256 attribute types, so allocate and use 32 bytes
instead of a whole map.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
f15a163cdf rpcsrv: deduplicate RunForErrors code
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
d22bdd8369 rpcsrv: microoptimize result allocation
We know the size.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-27 08:24:52 +03:00
Roman Khimov
aefd0da181 payload: deduplicate test code slightly
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:48 +03:00
Roman Khimov
35d5495d39 wallet: simplify signature handling
Don't use an additional buffer, drop one branch.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:48 +03:00
Roman Khimov
9e112fc024 *: use slices.Clone instead of make/copy
It's much easier this way.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:48 +03:00
Roman Khimov
7243754a0d *: drop open-coded slice reversing
Less code, same performance.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:48 +03:00
Roman Khimov
c7b623cc38 *: drop pkg/util/slice, use slices.Reverse
This also removes bigint.FromBytesUnsigned(), it's not used very often and
it's somewhat misleading in the bigint package (which is supposed to use a
very specific enconding).

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:48 +03:00
Roman Khimov
963e22ea95 *: replace slice.Clean() with clear()
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:48 +03:00
Roman Khimov
d9ee31fb52 *: use clear() to clear maps
Supposedly more efficient since we can avoid some memory management dances.

Memory pool agrees:

goos: linux
goarch: amd64
pkg: github.com/nspcc-dev/neo-go/pkg/core/mempool
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
                       │  pool.old   │              pool.new               │
                       │   sec/op    │   sec/op     vs base                │
Pool/one,_incr_fee-16    12.44m ± 1%   12.51m ± 1%   +0.55% (p=0.029 n=10)
Pool/many,_same_fee-16   4.960m ± 2%   3.100m ± 1%  -37.50% (p=0.000 n=10)
Pool/many,_incr_fee-16   16.03m ± 2%   14.11m ± 1%  -12.00% (p=0.000 n=10)
Pool/one,_same_fee-16                  1.742m ± 1%
geomean                  9.964m        5.556m       -17.92%

                       │    pool.old     │                pool.new                │
                       │      B/op       │      B/op       vs base                │
Pool/one,_incr_fee-16     8.117Ki ± 120%   7.101Ki ± 128%  -12.52% (p=0.022 n=10)
Pool/many,_same_fee-16   3941.2Ki ±   0%   805.4Ki ±   0%  -79.56% (p=0.000 n=10)
Pool/many,_incr_fee-16   3936.2Ki ±   0%   829.8Ki ±   0%  -78.92% (p=0.000 n=10)
Pool/one,_same_fee-16                      12.98Ki ±  10%
geomean                   501.2Ki          88.59Ki         -66.47%

                       │   pool.old   │               pool.new               │
                       │  allocs/op   │  allocs/op    vs base                │
Pool/one,_incr_fee-16     28.00 ± 21%    24.00 ± 21%  -14.29% (p=0.002 n=10)
Pool/many,_same_fee-16   40.38k ±  0%   40.03k ±  0%   -0.86% (p=0.000 n=10)
Pool/many,_incr_fee-16   40.38k ±  0%   40.04k ±  0%   -0.85% (p=0.000 n=10)
Pool/one,_same_fee-16                    23.00 ±  4%
geomean                  3.574k          969.8         -5.55%

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:48 +03:00
Roman Khimov
c2a374541f mempool: add a simple benchmark
Can't believe we never had any in code.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-24 22:41:05 +03:00
Roman Khimov
1c1d77c9b8 *: make use of min/max where appropriate
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-23 22:11:29 +03:00
Roman Khimov
f21edef43b *: bump min Go to 1.21, use 1.23 by default
Fixes #3089.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-23 19:11:06 +03:00
Anna Shaleva
dc6c195637
Merge pull request #3462 from NeoGoBros/add-coverage-support
Add initial coverage support to neotest
2024-08-21 11:40:16 +03:00
Roman Khimov
dfd4566a04
Merge pull request #3556 from nspcc-dev/adjust-waiter
Make PollingBased waiter more flexible
2024-08-20 12:32:51 +03:00
Anna Shaleva
42555668da rpcclient: add Waiter.Config
Include Waiter.PollConfig into Waiter.Config and use extended Waiter
configuration where needed.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-20 10:13:20 +03:00
Anna Shaleva
92c6361be8 rpcclient: integrate customizable Waiter with Actor
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-20 10:13:20 +03:00
Anna Shaleva
afbb51e78c rpcsrv: add test for Waiter constructor
Ensure that WSClient-based Actor is able to create EventBased waiter.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-20 10:13:20 +03:00
Anna Shaleva
027d726b65 rpcclient: allow to tune PollingBased waiter
Some clients need more flexible awaiting options (e.g. for short-blocks
networks). The default behaviour is not changed, all exported APIs are
compatible. Ref. https://github.com/nspcc-dev/neofs-node/issues/2864.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-20 10:13:19 +03:00
Anna Shaleva
3e3991cef8 rpcclient: remove default PolingBased poll interval
pollTime is never 0 since MillisecondsPerBlock protocol configuration value
is present in `getversion` RPC response since 0.97.3 release. We don't have such
old RPC servers in the network anymore, thus this fallback code may be
safely removed.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-20 10:10:59 +03:00
Slava0135
d0c45477f5 neotest: implement coverage collection
Test coverage is automatically enabled when go test is running with coverage
enabled. It can be disabled for any Executor by using relevant methods.
Coverage is gathered by capturing VM OPs during test contract execution and
mapping them to the contract source code using the DebugInfo information.

Signed-off-by: Slava0135 <super.novalskiy_0135@inbox.ru>
2024-08-19 14:39:18 +03:00
Roman Khimov
7766168c19
Merge pull request #3557 from nspcc-dev/fix-lint
*: fix linter issues
2024-08-14 15:02:20 +03:00
Anna Shaleva
c06543cf50 *: fix linter issues
Linter is updated up to v1.60.1, the following issue is fixed:
```
predeclared  variable max has same name as predeclared identifier
```

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-14 12:36:15 +03:00
Roman Khimov
8ea0bc6e58
Merge pull request #3554 from nspcc-dev/add-echidna 2024-08-12 13:48:20 +03:00
Anna Shaleva
2c24cb342e core: introduce Echidna hardfork
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-12 11:10:26 +03:00
Anna Shaleva
79e78980c4
Merge pull request #3552 from nspcc-dev/fix-wsclient-limit
rpcsrv: fix failing TestWSClientsLimit
2024-08-09 16:44:27 +03:00
Anna Shaleva
b27e4f4309
Merge pull request #3540 from nspcc-dev/rpc-getversion
rpcsrv: add seedlist and standbycommittee to `getversion`
2024-08-09 15:18:12 +03:00
Anna Shaleva
9ba6db491e rpcsrv: fix failing TestWSClientsLimit
64 connections is too much for slow GA runners, we have to keep all of
them alive to avoid failures in the test, but it's impossible since
runners are too slow to iterate through all of them in time.

Close #3442.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-09 15:14:52 +03:00
Roman Khimov
f0266a9973
Merge pull request #3551 from nspcc-dev/neotest-fixes 2024-08-09 15:09:30 +03:00
Ekaterina Pavlova
9fb6d3266e rpcsrv: add seedlist and standbycommittee to getversion
Port neo-project/neo#3443.

Close #3538

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-08-09 12:53:31 +03:00
Anna Shaleva
72d1427109 neotest: calculate system fee for deployment transactions
1. Bind NewDeployTxBy to Executor to be able to use
   (*Executor).AddSystemFee.
2. Replace pre-defined constant deployment fee by calculated one.

This change is needed to be able to properly collect coverage for
_deploy method of a contract via neotest coverage. Ref.
https://github.com/nspcc-dev/neo-go/pull/3462#pullrequestreview-2229601870.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-09 11:55:22 +03:00
Anna Shaleva
5799397034 neotest: bind AddSystemFee and TestInvoke to Executor
These methods need Executor's context to properly process coverage, thus
these methods are not independent anymore. Ref.
https://github.com/nspcc-dev/neo-go/pull/3462#pullrequestreview-2219332436.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-09 11:49:31 +03:00
Roman Khimov
ffcbe6a10d
Merge pull request #3547 from nspcc-dev/docker-upd 2024-08-07 08:24:46 +03:00
Anna Shaleva
cbc2f73a0c *: migrate to Docker Compose V2
Docker Compose V1 is deprecated since June 2023, ref.
https://docs.docker.com/compose/migrate/ and
https://github.com/nspcc-dev/neo-bench/issues/166.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-06 15:51:17 +03:00
Anna Shaleva
7e8fda6b6d
Merge pull request #3541 from nspcc-dev/up-dbft
dbft: update to AMEV-enabled version
2024-08-06 15:44:56 +03:00
Roman Khimov
39e0d60221
Merge pull request #3544 from nspcc-dev/fix-permission-extension 2024-08-05 16:17:19 +03:00
Anna Shaleva
2c7430583c smartcontrct: take care of manifest permission descriptor wildcardness
Starting from b10af1ed31
(*WildPermissionDescs).Add method's call is not enough to construct a
proper restricted permission descriptor, because Wildcard field should
be set properly at the same time. Ref. #3523.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-05 16:05:59 +03:00
Anna Shaleva
c46dfee214
Merge pull request #3537 from nspcc-dev/fix-mempool-fees
Fix mempool fees
2024-08-05 15:59:11 +03:00
Roman Khimov
5d1d7b104e mempool: properly remove fees when removing tx during Add
Fixes #3488.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-05 15:36:14 +03:00
Anna Shaleva
6651b683a0
Merge pull request #3539 from nspcc-dev/netmode
netmode: add constant MainNetNeoFS and TestNetNeoFS
2024-08-05 15:30:32 +03:00
Roman Khimov
c950891298
Merge pull request #3545 from nspcc-dev/fix-cfg-doc
docs: adjust DB compatibility notes
2024-08-05 15:14:29 +03:00
Anna Shaleva
7e277fa948 docs: adjust DB compatibility notes
It's dangerous to change `Max*` ProtocolConfiguration settings:

* Changes in MaxBlockSize, MaxBlockSystemFee and MaxTransactionsPerBlock
  may lead to the fact that accepted block or transaction becomes invalid.
  I agree that these settings are not written in the DB, but at the same
  time it's not correct to compare databases that have these settings
  mismatched.
* Changes in MaxTraceableBlocks may lead to the fact that some
  transaction will be processed differently, it's a possible contract
  state mismatch.
* Changes in MaxValidUntilBlockIncrement may lead to the fact that
  `setMaxNotValidBeforeDelta` method of native Notary contract may be
  processed in a different way which is also a possible contract state
  mismatch.

Ref. 5d29a3fdab.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-08-05 12:33:11 +03:00
Roman Khimov
ee0d92c6d2 dbft: update to AMEV-enabled version
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-01 22:29:51 +03:00
Ekaterina Pavlova
ef20ba3701 netmode: add constant MainNetNeoFS and TestNetNeoFS
Extend netmode package with NeoFS chains IDs.

Close #3503

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-31 11:02:17 +08:00
Roman Khimov
a11e433754 mempool: move metrics out of removeInternal, simplify it
Metrics should be updated once per action, currently removeInternal is
used by Add and Remove, the first one updates them in the end anyway and
remove should do the same.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-30 17:43:14 +03:00
Roman Khimov
6334192a95 mempool: remove Feer from Remove()
It's not used and not needed for removal.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-30 17:37:18 +03:00
Roman Khimov
d8e3e57f88
Merge pull request #3536 from nspcc-dev/rel-0.106.3
CHANGELOG: release 0.106.3
2024-07-29 19:01:16 +03:00
Roman Khimov
7fe176ac27 CHANGELOG: release 0.106.3
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-29 18:35:16 +03:00
Roman Khimov
c2a1c4af53
Merge pull request #3535 from nspcc-dev/fix/possible-WS-deadlock-on-connection-loss 2024-07-29 17:56:39 +03:00
Pavel Karpy
9fadfef0d7 rpcclient/WS: do not deadlock on connection loss
`makeWsRequest` creates a channel for response and waits for it. If between
creating the channel and starting the reading `select` connection is lost
(`writerDone` channel is closed), nothing reads from the channel and a
deadlock appears. Looking at "done" channels when transferring RPC data
solves the issue. Closes #3530.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2024-07-29 17:39:11 +03:00
Roman Khimov
8d4e8b6047
Merge pull request #3527 from nspcc-dev/dump-fixes
Dump help/error fixes
2024-07-29 15:08:07 +03:00
Roman Khimov
07da75c254
Merge pull request #3526 from nspcc-dev/improve-doc 2024-07-29 15:07:54 +03:00
Roman Khimov
54fd70fc32
Merge pull request #3523 from nspcc-dev/fix-null-handling-in-manifest
Fix null handling in manifest
2024-07-29 13:11:20 +03:00
Roman Khimov
5d29a3fdab docs: document DB incompatibilities, fix #3456
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-29 13:09:16 +03:00
Roman Khimov
8bc06eceb8
Merge pull request #3534 from nspcc-dev/fix-missing-block-field
Fix missing block field
2024-07-29 10:08:11 +03:00
Roman Khimov
19aad75242 *: update interops
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-27 12:56:56 +03:00
Roman Khimov
1a48f1ce43 block: add PrimaryIndex to the stack item of block
This is a bad one.

$ ./bin/neo-go contract testinvokefunction -r https://rpc10.n3.nspcc.ru:10331 0xda65b600f7124ce6c79950c1772a36403104f2be getBlock 5762000
{
  "state": "HALT",
  "gasconsumed": "202812",
  "script": "AtDrVwARwB8MCGdldEJsb2NrDBS+8gQxQDYqd8FQmcfmTBL3ALZl2kFifVtS",
  "stack": [
    {
      "type": "Array",
      "value": [
        {
          "type": "ByteString",
          "value": "vq5IPTPEDRhz0JA4cQKIa6/o97pnJt/HfVkDRknd1rg="
        },
        {
          "type": "Integer",
          "value": "0"
        },
        {
          "type": "ByteString",
          "value": "zFYF3LGaTKdbqVX99shaBUzTq9YjXb0jaPMjk2jdSP4="
        },
        {
          "type": "ByteString",
          "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
        },
        {
          "type": "Integer",
          "value": "1722060076994"
        },
        {
          "type": "Integer",
          "value": "5293295626238767595"
        },
        {
          "type": "Integer",
          "value": "5762000"
        },
        {
          "type": "ByteString",
          "value": "LIt05Fpxhl/kXMX3EAGIASyOSQs="
        },
        {
          "type": "Integer",
          "value": "0"
        }
      ]
    }
  ],
  "exception": null,
  "notifications": []
}

$ ./bin/neo-go contract testinvokefunction -r http://seed3.neo.org:10332 0xda65b600f7124ce6c79950c1772a36403104f2be getBlock 5762000
{
  "state": "HALT",
  "gasconsumed": "202812",
  "script": "AtDrVwARwB8MCGdldEJsb2NrDBS+8gQxQDYqd8FQmcfmTBL3ALZl2kFifVtS",
  "stack": [
    {
      "type": "Array",
      "value": [
        {
          "type": "ByteString",
          "value": "vq5IPTPEDRhz0JA4cQKIa6/o97pnJt/HfVkDRknd1rg="
        },
        {
          "type": "Integer",
          "value": "0"
        },
        {
          "type": "ByteString",
          "value": "zFYF3LGaTKdbqVX99shaBUzTq9YjXb0jaPMjk2jdSP4="
        },
        {
          "type": "ByteString",
          "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
        },
        {
          "type": "Integer",
          "value": "1722060076994"
        },
        {
          "type": "Integer",
          "value": "5293295626238767595"
        },
        {
          "type": "Integer",
          "value": "5762000"
        },
        {
          "type": "Integer",
          "value": "6"
        },
        {
          "type": "ByteString",
          "value": "LIt05Fpxhl/kXMX3EAGIASyOSQs="
        },
        {
          "type": "Integer",
          "value": "0"
        }
      ]
    }
  ],
  "exception": null,
  "notifications": []
}

9 fields vs 10, notice the primary index right after the block number.

Back when ac527650eb initially added Ledger I've
used https://github.com/neo-project/neo/pull/2215 as a reference and it was
correct (no primary index). But then https://github.com/neo-project/neo/pull/2296
came into the C# codebase and while it looked like a pure refactoring it
actually did add the primary index as well and this wasn't noticed. It wasn't
noticed even when 3a4e0caeb8 had touched some
nearby code. In short, we had a completely wrong implementation of this call
for more than three years. But looks like it's not a very popular one.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-27 12:35:19 +03:00
Roman Khimov
c6ed92a169
Merge pull request #3532 from nspcc-dev/fix/ws-client-subscription-panic
rpcclient/WS: fix data race on concurrent (un)subscription
2024-07-26 19:29:38 +03:00
Pavel Karpy
d6eaf6efc2 rpcclient/WS: fix data race on concurrent (un)subscription
Every client's (Un)Subscription call does two things: an RPC call and a
subscription map lock (two of maps currently). If we imagine that there is
one routine that tries to subscribe (A) and one routine that tries to
unsubscribe (B), the following sequence can happen:

0. Current number of subscriptions is X
1. B does an RPC and makes number of subscriptions X-1
2. A does an RPC and makes number of subscriptions X again
3. A holds subscription locks and rewrites client's subscription state
   (subscription with ID X now points to a different channel; channel that
   was registered by B is lost and is not related to any real subscription
   but is still included in the `receivers` map)
4. B holds subscription locks and drops subscription X (first, it is an
   error and we have just lost a subscription that we think was made
   successfully second, we have lost a channel in the `receivers` map, and
   no corresponding subscription points to it)
5. X subscription is received by the WS client (in practice it is a new
   block, 100ms, quite often to be sure this issue happens every hour), we
   range through the receivers, see no corresponding subscription, and
   panic.

Closes #3093.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2024-07-26 18:19:50 +03:00
Roman Khimov
4fe9597dd5
Merge pull request #3529 from nspcc-dev/fix-sessionexpiration-default 2024-07-26 17:24:14 +03:00
Roman Khimov
f4b61b5a77 rpcsrv: set minimal default SessionExpirationTime to 5s, fix #3509
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-26 17:17:37 +03:00
Roman Khimov
e5dd2b2ad6 cli: extend error message for restore failure, fix #3499
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-26 17:07:27 +03:00
Roman Khimov
c4453a2a3f cli: fix dump command help, fix #3498
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-26 16:52:24 +03:00
Roman Khimov
d8cf424e0a docs: add more on compiler limitations, fix #3023
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-26 16:03:05 +03:00
Roman Khimov
b10af1ed31 manifest: make default trusts invalid
Refs. #3522. The core problem is the same as for groups/features: we can't
allow empty trusts when they're unmarshalled from JSON. But unlike others we
can't easily differentiate missing any value with other cases because the
default value for WildPermissionDescs is a valid thing. Adding an additional
field makes it invalid and we can build around it. Other options are
implementing custom UnmarshalJSON for Manifest (too much for this) or making
Trusts a pointer (an option, but can fail in too many ways).

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-26 15:18:25 +03:00
Roman Khimov
58ab24efdb manifest: don't accept manifests with invalid features
Refs. #3522.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-26 15:18:25 +03:00
Roman Khimov
e861aeec2e manifest: disallow null groups, fix #3522
IsValid() is used by both compiler and ContractManagement then.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-26 11:22:44 +03:00
Roman Khimov
f0ae14e4db
Merge pull request #3520 from nspcc-dev/alias
cli: fix flag alias in compare-states command
2024-07-22 13:03:36 +03:00
Ekaterina Pavlova
37302d5995 cli: fix flag alias in compare-states command
Close #3517

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-22 11:47:01 +03:00
Anna Shaleva
d78ed4e8f4
Merge pull request #3518 from nspcc-dev/tune-neofs-mtb
config: drop MTB value for NeoFS networks to three days
2024-07-18 17:48:41 +03:00
Roman Khimov
19d5e05d60 config: drop MTB value for NeoFS networks to three days
We want to cut the tail more aggressively, what matters is state, but not
old blocks and transactions that contracts don't use in any way. #3493
suggests that it's safe to limit the tail to one day, but let's be a bit
more conservative for now.

As a sidenote, EVM only allows to fetch things from the recent 256 blocks.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-17 19:30:07 +03:00
Anna Shaleva
c207b9b194
Merge pull request #3514 from nspcc-dev/microoptimize-chaindump
chaindump: microoptimize memory management
2024-07-16 16:54:41 +03:00
Roman Khimov
f60d5ee1b3
Merge pull request #3500 from nspcc-dev/optimize-ext-computations 2024-07-16 15:32:07 +03:00
Roman Khimov
1aefd7d16a chaindump: microoptimize memory management
Reuse buffers.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-07-16 15:03:17 +03:00
Anna Shaleva
c65d9f40e3
Merge pull request #3495 from nspcc-dev/cli
cli: upgrade urfave lib to v2
2024-07-12 12:49:50 +03:00
Ekaterina Pavlova
d4a3c912fb cli: fix errors format
Error string should not be capitalized or end with punctuation mark.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-12 12:35:10 +03:00
Ekaterina Pavlova
fc2798f9d1 cli: fix canceltx usage text
We do not have `--account` flag.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-12 12:16:24 +03:00
Ekaterina Pavlova
7b199af3b7 cli: change StringFlag to AddressFlag
For passing address or hash AddressFlag is more suitable.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-12 11:58:12 +03:00
Ekaterina Pavlova
39559b90e2 cli: add Required field for flags
Close #2861

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-12 11:58:12 +03:00
Ekaterina Pavlova
acde7bd0de cli: upgrade urfave lib to v2
Close #3097

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-12 11:58:12 +03:00
Ekaterina Pavlova
b32e568d21 cli: don't panic when handling error
Close #2886

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-12 11:58:12 +03:00
Ekaterina Pavlova
2f5c26f14b cli: use capital letter for Usage
To unified cli help only capital letter is used in Usage field.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-12 11:58:12 +03:00
Anna Shaleva
25dddc33cd
Merge pull request #3511 from nspcc-dev/lint-config
Upgrade `.golangci.yml` config
2024-07-12 11:30:35 +03:00
Anna Shaleva
aff66a23dc
Merge pull request #3508 from nspcc-dev/witness-check-hint
rpcsrv: improve witness verification error
2024-07-12 11:29:22 +03:00
Leonard Lyubich
d03fd02f79 lint: Drop no-op govet config
According to https://golangci-lint.run/usage/linters/#govet,
`check-shadowing` no longer exists. Now it should be
```yaml
govet:
  disable: [shadow]
```
but `shadow` is disabled by default. Thus, whole `govet` section is not
needed anymore.

```
$ golangci-lint --version
golangci-lint has version 1.59.1 built with go1.22.3 from 1a55854a on 2024-06-09T18:08:33Z
```

Signed-off-by: Leonard Lyubich <leonard@morphbits.io>
2024-07-11 21:22:10 +04:00
Leonard Lyubich
f37fa6f98c lint: Refresh deprecated config
This fixes
```
$ golangci-lint --version
golangci-lint has version 1.59.1 built with go1.22.3 from 1a55854a on 2024-06-09T18:08:33Z
$ golangci-lint run ./...
WARN [config_reader] The configuration option `output.format` is deprecated, please use `output.formats`
```

The configuration file is updated according to
https://golangci-lint.run/usage/configuration/.

Signed-off-by: Leonard Lyubich <leonard@morphbits.io>
2024-07-11 21:21:21 +04:00
Anna Shaleva
434f94800a rpcsrv: improve witness verification error
It's needed to give user a hint about what's wrong with the witness
during `calculatenetworkfee` RPC request processing.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-07-11 12:03:34 +03:00
Anna Shaleva
7304b2c7fb
Merge pull request #3505 from nspcc-dev/array-cp
*: Convert slices to arrays instead of `copy` where possible
2024-07-08 11:00:31 +03:00
Anna Shaleva
da40f2de14
Merge pull request #3504 from nspcc-dev/fix-loadcfg
config: get back default node configuration values
2024-07-05 19:49:27 +03:00
Leonard Lyubich
c975d728e8 *: Convert slices to arrays instead of copy where possible
Became possible with Go 1.20.

Signed-off-by: Leonard Lyubich <leonard@morphbits.io>
2024-07-05 20:36:22 +04:00
Anna Shaleva
ed9817d35b config: get back default node configuration values
This code was accidentally removed by
https://github.com/nspcc-dev/neo-go/pull/3477, it's important to have
these fields set by default.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-07-05 13:31:25 +03:00
Anna Shaleva
5566e354d4
Merge pull request #3501 from NeoGoBros/add-max-array-size-test
Add a missing `mptdata` test
2024-07-03 19:34:14 +03:00
Furetur
3456d92220 mptdata: add test for MaxArraySize
This commit adds a single test that covers the
previously uncovered branch in the mptdata
decoding algorithm.

Signed-off-by: Furetur <furetur@gmail.com>
2024-07-03 13:36:53 +03:00
Anna Shaleva
6f77195ce3
Merge pull request #3460 from NeoGoBros/add-onexec-hook
Implement OnExecHook VM API
2024-07-03 13:21:46 +03:00
Anna Shaleva
17de1bf7fe core: avoid extra call to GetNextBlockValidatorsInternal
It should be sufficient to retrieve next block validators once per
updateExtensibleWhitelist call and then reuse this value. `nextVals`
copy intentionally omitted since the only change that
smartcontract.CreateDefaultMultiSigRedeemScript performs over the
`nextVals` list is sorting.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-07-03 13:03:10 +03:00
Furetur
f5794e91a2 vm: implement OnExecHook
Refs #3415

This commit introduces a small new change
that implements the Hooks API and more
specifically the OnExecHook. This feature
can be used to implement test coverage
collection, tracing, breakpoints, and etc.

To be more specific, this commit:

1. adds a new `hooks` field to the `VM`
   (this field contains the OnExecHook
    function)

2. sets the default value of this hook
   to be a NOP function

3. adds the `VM.SetOnExecHook` method

Signed-off-by: Furetur <furetur@gmail.com>
2024-07-03 12:49:09 +03:00
Anna Shaleva
0f2229d6a5
Merge pull request #3477 from nspcc-dev/vm-default-config
cli: add embedded node config
2024-07-01 11:14:43 +03:00
Ekaterina Pavlova
a9abd84cc4 cli: add embedded node config
If `config-path` is not passed, default configs are used according to
the set network. In VM CLI the default privnet config with InMemory db
is used.

Close #3450
Close #3459

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-07-01 10:59:11 +03:00
Anna Shaleva
ce1edda3f2
Merge pull request #3481 from EdgeDLT/getPeerHeights
Extend getpeers RPC method to provide useragent and last known height
2024-06-28 11:07:09 +03:00
edgedlt
ba4ebbd107 rpcclient: fix getpeers test
Signed-off-by: edgedlt <edgedlt@protonmail.com>
2024-06-27 14:00:08 +01:00
edgedlt
aab2620548 neorpc: extend getpeers method
Signed-off-by: edgedlt <edgedlt@protonmail.com>
2024-06-27 10:41:59 +01:00
Anna Shaleva
d9d9d00775
Merge pull request #3492 from nspcc-dev/actor-signers
invoker: add Signers() API
2024-06-21 23:30:45 +03:00
Anna Shaleva
3889224c3e
Merge pull request #3491 from nspcc-dev/wait-success
actor: add a new WaitSuccess API
2024-06-21 23:29:00 +03:00
Roman Khimov
cc3f528eb6 actor: add SignerAccounts() API
Allow to retrieve the list easily.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-06-21 18:45:15 +03:00
Roman Khimov
8336b1b518 invoker: add Signers() API
Signers are very important for notary checks and keeping/passing an additional
copy of them is very inconvenient. Exposing them from invoker makes them
available in actors too.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-06-21 18:45:15 +03:00
Roman Khimov
a327a82085 actor: add a new WaitSuccess API
Most of the time people are interested in successful executions. Unfortunately,
unwrap package can't help here because of a different result structure (some
interface abstract can help, but it's still mostly stack-oriented and sessions
can be a problem), so this additional interface is needed.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-06-21 18:39:02 +03:00
Roman Khimov
4ff2063539
Merge pull request #3489 from nspcc-dev/rel-0.106.2 2024-06-13 14:39:36 +03:00
Anna Shaleva
0acdb44c14 CHANGELOG: release 0.106.2
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-13 14:34:32 +03:00
Roman Khimov
05d602b0d8
Merge pull request #3487 from nspcc-dev/domovoi-schedule
config: port Domovoi schedule
2024-06-13 11:14:56 +03:00
Anna Shaleva
8c2c75d92d config: port Domovoi schedule
Ref. https://github.com/neo-project/neo/pull/3330.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-11 20:40:56 +03:00
Roman Khimov
fc82feb982
Merge pull request #3486 from nspcc-dev/extend-domovoi 2024-06-11 19:42:35 +03:00
Anna Shaleva
8f70d05f85 docs: add a note about System.Runtime.GetNotifications refcounting
Add a note about System.Runtime.GetNotifications refcounting to Domovoi
hardfork. Ref. https://github.com/neo-project/neo/pull/3301 and
https://github.com/nspcc-dev/neo-go/pull/3485.

Although NeoGo doesn't have anything to be updated, there's a
behaviour difference between C# and Go nodes before Domovoi hardfork, it
deserves a comment.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-11 19:26:11 +03:00
Roman Khimov
21aaadc4b7
Merge pull request #3473 from nspcc-dev/fix-call-check 2024-06-11 19:21:44 +03:00
Anna Shaleva
a7aceca74a interop: use currently executing contract state for permissions check
It's not correct to use an updated contract state got from Management to
check for the allowed method call. We need to use manifest from the
currently executing context for that. It may be critical for cases when
executing contract is being updated firstly, and after that calls
another contract. So we need an old (executing) contract manifest for
this check.

This change likely does not affect the mainnet's state since it's hard
to meet the trigger criteria, but I'd put it under the hardfork anyway.

Ref. https://github.com/neo-project/neo/pull/3290.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-11 19:13:17 +03:00
Roman Khimov
02627e948f
Merge pull request #3485 from nspcc-dev/test-maxstacksize 2024-06-11 19:04:26 +03:00
Roman Khimov
cffef71be7
Merge pull request #3476 from nspcc-dev/D-hardfork 2024-06-11 19:01:40 +03:00
Anna Shaleva
d156cea24d vm: improve stack size related errors
No functional changes, just add more details to the error.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-11 18:34:22 +03:00
Anna Shaleva
effba1fa47 core: ensure System.Runtime.GetNotifications can't break MaxStackSize
This test ensures that NeoGo node doesn't have the DeepCopy problem
described in https://github.com/neo-project/neo/issues/3300 and fixed in
https://github.com/neo-project/neo/pull/3301. This problem leads to the
fact that Notifications items are not being properly refcounted by C#
node which leads to possibility to build an enormously large object on
stack. Go node doesn't have this problem.

The reason (at least, as I understand it) is in the fact that C# node
performs objects refcounting inside the DeepCopy even if the object
itself is not yet on stack. I.e. System.Runtime.Notify handler
immediately adds references to the notification argumetns inside
DeepCopy:
b1d27f0189/src/Neo.VM/Types/Array.cs (L108)
b1d27f0189/src/Neo.VM/Types/Array.cs (L75)

Whereas Go node just performs the honest DeepCopy without references counting:
b66cea5ccc/pkg/vm/stackitem/item.go (L1223)

Going further, C# node clears refs for notification arguments (for array
and underlying array items). System.Runtime.GetNotifications pushes the
notificaiton args array back on stack and increments counter only for
the external array, not for its arguments. Which results in negative
refcounter once notificaiton is removed from the stack. The fix itself
(f471c0542d/src/Neo/SmartContract/NotifyEventArgs.cs (L84))
doesn't need to be ported to NeoGo because Go node adds object to the
refcounter only at the moment when it's being pushed to stack by
System.Runtime.GetNotifications handler. This object is treated as new
object since it was deepcopied earlier by System.Runtime.Notify handler:
b66cea5ccc/pkg/vm/stack.go (L178).

Thus, no functoinal changes from the NeoGo side. And we won't
intentionally break our node to follow C# pre-Domovoi invalid behaviour.

Close #3484, close #3482.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-11 17:51:41 +03:00
Anna Shaleva
55fa12355e stackitem: extend Make() with []string
Allow []string to be converted to stackitem.Item.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-11 17:50:53 +03:00
Anna Shaleva
b66cea5ccc
Merge pull request #3483 from nspcc-dev/bump-neofs-sdk
go.mod: update NeoFS SDK to RC12
2024-06-07 16:37:04 +03:00
Roman Khimov
e5eb20bbae go.mod: update NeoFS SDK to RC12
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-06-07 15:08:25 +03:00
Anna Shaleva
e5a6e9ca32 core: introduce D hardfork
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-05 19:20:04 +03:00
Anna Shaleva
cf4d4a2611
Merge pull request #3469 from nspcc-dev/warn
logs: distinguish WARN and INFO for `peer disconnected`
2024-06-05 15:39:52 +03:00
Anna Shaleva
7a7a5d0322
Merge pull request #3470 from nspcc-dev/contract-acc
wallet: do not store deployed contract script inside `Contract` account field
2024-06-05 15:35:04 +03:00
Ekaterina Pavlova
4d6333866d network: extend errInvalidInvType error
Add type to the errInvalidInvType.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-06-05 15:32:43 +03:00
Ekaterina Pavlova
4a5e8f8592 logs: distinguish WARN and INFO for peer disconnected
Peer disconnections are not warnings in some cases.

Close #3182

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-06-05 15:32:42 +03:00
Ekaterina Pavlova
b4fdf8c3c9 wallet: do not store deployed contract script inside Contract account
`Contract` account field should not contain deployed contract script.

Close #3348

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-06-05 15:27:20 +03:00
Anna Shaleva
3c471f0b7e
Merge pull request #3455 from nspcc-dev/sast
Adjust minor SAST warnings
2024-06-05 12:21:42 +03:00
Ekaterina Pavlova
41109f442a oracle: add length check o.MainCfg.NeoFS.Nodes
Prevent the risk of a division by zero error when accessing the
`o.MainCfg.NeoFS.Nodes[index]` array.

Close #3419

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-06-05 11:53:52 +03:00
Ekaterina Pavlova
5c408f7fe4 core: adjust prevHeader nil check
prevHeader is never nil.

Refs #3419

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-06-05 11:53:52 +03:00
Anna Shaleva
cd525a1df5
Merge pull request #3468 from nspcc-dev/logs
logs: hide timestamp if the program is run not in TTY
2024-06-05 10:46:13 +03:00
Roman Khimov
836183ecb6
Merge pull request #3475 from nspcc-dev/rel-0.106.1 2024-06-03 17:15:44 +03:00
Anna Shaleva
3c1c650ddf CHANGELOG: release 0.106.1
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-03 16:12:36 +03:00
Anna Shaleva
45b8af359d
Merge pull request #3472 from nspcc-dev/pick-proper-exe-context
interop: use executing contract state for permissions checks
2024-06-03 16:10:39 +03:00
Anna Shaleva
4945145b09 interop: use executing contract state for permissions checks
Do not use the updated contract state from native Management to perform
permissions checks. We need to use the currently executing state
instead got from the currently executing VM context until context is
unloaded.

Close #3471.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-03 12:32:10 +03:00
Ekaterina Pavlova
f48e992a78 logs: hide timestamp if the program is run not in TTY
If the program is run in TTY then logger adds timestamp to its entries.

Close #3358

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
2024-06-03 11:49:54 +03:00
Roman Khimov
0b136c1c9c
Merge pull request #3463 from nspcc-dev/fool-protection 2024-05-25 00:59:23 +03:00
Anna Shaleva
f4aeaa6387 workflows: protect go.mod from unexpected imports
Avoid situations like #3458.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-05-24 21:11:39 +03:00
Roman Khimov
0ae5e7ea83
Merge pull request #3458 from nspcc-dev/remove-contract-deps
go.mod: remove unused dependency
2024-05-21 21:46:15 +03:00
Anna Shaleva
228052360e go.mod: remove unused dependency
Was accidentally added in
aa5a0cb49b.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-05-21 20:40:38 +03:00
443 changed files with 16996 additions and 5376 deletions

View file

@ -1,5 +1,3 @@
version: '2.4'
networks:
default:
name: neo_go_network

View file

@ -37,7 +37,7 @@ jobs:
runs-on: ${{matrix.os.name}}
strategy:
matrix:
os: [{ name: ubuntu-22.04, bin-name: linux }, { name: windows-2022, bin-name: windows }, { name: macos-12, bin-name: darwin }]
os: [{ name: ubuntu-22.04, bin-name: linux }, { name: windows-2022, bin-name: windows }, { name: macos-14, bin-name: darwin }]
arch: [amd64, arm64]
exclude:
- os: { name: windows-2022, bin-name: windows }
@ -53,7 +53,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version: '1.23'
- name: Build CLI
run: make build
@ -135,7 +135,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version: '1.23'
- name: Login to DockerHub
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}

View file

@ -14,19 +14,32 @@ on:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
name: 'Lint: NeoGo'
uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
skip-pkg-cache: true # golangci-lint can't work with this cache enabled, ref. https://github.com/golangci/golangci-lint-action/issues/135.
lint_examples:
name: 'Lint: examples (${{ matrix.contract }})'
uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master
strategy:
fail-fast: false
matrix:
contract: [ 'engine', 'events', 'iterator', 'nft-d', 'nft-nd', 'nft-nd-nns', 'oracle',
'runtime', 'storage', 'timer', 'token', 'zkp/cubic_circuit', 'zkp/xor_compat']
with:
workdir: examples/${{ matrix.contract }}
lint_scripts:
name: 'Lint: scripts'
uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master
with:
workdir: scripts
lint_interops:
name: 'Lint: interop'
uses: nspcc-dev/.github/.github/workflows/go-linter.yml@master
with:
workdir: pkg/interop
gomodcheck:
name: Check internal dependencies
@ -39,6 +52,14 @@ jobs:
- name: Check dependencies
run: |
./scripts/check_deps.sh
- name: Check go.mod is tidy
run: |
go mod tidy
if [[ $(git diff --name-only go.* | grep '' -c) != 0 ]]; then
echo "go mod tidy should be executed before the merge, following packages are unused or out of date:";
git diff go.*;
exit 1;
fi
codegencheck:
name: Check code generated with 'go generate' is up-to-date
@ -127,11 +148,11 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version: '1.23'
cache: true
- name: Write coverage profile
run: go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
run: DISABLE_NEOTEST_COVER=1 go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v4
@ -147,25 +168,17 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-2022, macos-12, macos-14]
go_versions: [ '1.20', '1.21', '1.22' ]
os: [ubuntu-22.04, windows-2022, macos-14]
go_versions: [ '1.22', '1.23' ]
exclude:
# Only latest Go version for Windows and MacOS.
- os: windows-2022
go_versions: '1.20'
- os: windows-2022
go_versions: '1.21'
- os: macos-12
go_versions: '1.20'
- os: macos-12
go_versions: '1.21'
go_versions: '1.22'
- os: macos-14
go_versions: '1.20'
- os: macos-14
go_versions: '1.21'
go_versions: '1.22'
# Exclude latest Go version for Ubuntu as Coverage uses it.
- os: ubuntu-22.04
go_versions: '1.22'
go_versions: '1.23'
fail-fast: false
steps:
- uses: actions/checkout@v4

3
.gitignore vendored
View file

@ -55,3 +55,6 @@ testdata/
pkg/vm/testdata/fuzz
!pkg/vm/testdata
!pkg/wallet/testdata
# Linter
.golangci.yml

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

@ -1,75 +0,0 @@
# This file contains all available configuration options
# with their default values.
# options for analysis running
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
timeout: 5m
# include test files or not, default is true
tests: true
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
format: tab
# all available settings of specific linters
linters-settings:
exhaustive:
# indicates that switch statements are to be considered exhaustive if a
# 'default' case is present, even if all enum members aren't listed in the
# switch
default-signifies-exhaustive: true
govet:
# report about shadowed variables
check-shadowing: false
linters:
enable:
# mandatory linters
- govet
- revive
# some default golangci-lint linters
- errcheck
- gosimple
- godot
- ineffassign
- staticcheck
- typecheck
- unused
# extra linters
# - exhaustive
# - goconst
# - goerr113
# - gomnd
# - nonamedreturns
# - unparam
- bidichk
- bodyclose
- contextcheck
- decorder
- durationcheck
- errorlint
- exportloopref
- gofmt
- misspell
- predeclared
- reassign
- whitespace
- goimports
disable-all: true
fast: false
issues:
include:
- EXC0002 # should have a comment
- EXC0003 # test/Test ... consider calling this
- EXC0004 # govet
- EXC0005 # C-style breaks
exclude-rules:
- linters:
- revive
text: "unused-parameter"

View file

@ -2,6 +2,306 @@
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`
application-level extension allowing to remove untraceable block headers from the DB.
This feature significantly reduces the database size, but for now it is supported in
an experimental mode, use it with care. Other than that, this release includes a fix
of BlockFetcher service that may hang on retry of NeoFS requests preventing the node
from syncing. Also, an improved algorithm of blocks uploading and extended list of
block and index file attributes are supported for `upload-bin` CLI command.
No configuration update or DB resync is required. However, starting from this release
`NeoFSBlockFetcher` application configuration section is backed by default values for
every parameter except `Addresses` and `ContainerID`, hence if you don't like too
chatty configuration files, feel free to remove all optional parameters.
New features:
* untraceable headers removal (#3750)
Behavior changes:
* add `BlockTime` attribute to block objects stored in NeoFS block storage (#3749)
* use `Timestamp` attribute to hold object creation time for block and index objects
stored in NeoFS block storage (#3749)
* extended debug logs for `upload-bin` CLI command (#3751)
Improvements:
* embed default UnitTestNet node configuration (#3696)
* NeoFS SDK dependency upgrade (#3725, #3756)
* dependent packages updates (#3746, #3747, #3748)
* refactor and speed up `upload-bin` CLI command (#3735)
* backup NeoFS BlockFetcher configuration with default values (#3742)
* reuse more of built-in NeoFS SDK functionality in `upload-bin` CLI handler (#3749)
Bugs fixed:
* NeoFS BlockFetcher sometimes is hanging during the node startup (#3736)
* RPC server timers are improperly drained (#3737)
* basic unit test chain restore configuration (#3696)
## 0.107.1 "Narrativization" (06 Dec 2024)
An urgent version that fixes the problem of intensive CPU usage caused by improper
NeoFS BlockFetcher shutdown on the node's sync process completion and magnified by
additional bug at the peer discovery level.
No configuration changes or DB resync is required. It is highly recommended to update
from 0.107.0 as soon as possible since described problems affect the speed of blocks
processing and the overall node functionality.
Behavior changes:
* explicitly enable the list of stable hardforks in default NeoFS testnet
configuration (#3722)
Improvements:
* decrease NeoFS storage nodes deal timeout for NeoFS BlockFetcher (#3723)
* adjust optimal number of peers for networks with small peer count (#3727)
* don't enable unstable hardforks by default (#3724)
Bugs fixed:
* "unexpected empty payload: CMDVersion" error on peer disconnection (#3726)
* NeoFS BlockFetcher shutdown (#3728)
* connected peers count is not respected on attempt to gather more node addresses
(#3730)
## 0.107.0 "Mongrelization" (03 Dec 2024)
A large update that introduces a major node extension: NeoFS BlockFetcher service and
`util upload-bin` CLI command implemented as a part of [NeoFS snapshot storage
proposal](https://github.com/neo-project/neo/issues/3463). BlockFetcher service, as
an alternative to P2P synchronization mechanism, allows to download blocks and sync
chain from block dumps stored in NeoFS. Starting from this release, NeoSPCC team
maintains chain dumps in NeoFS for N3 and NeoFS public networks. The default NeoGo
node configuration for these networks has been changed to use NeoFS BlockFetcher as
a synchronization mechanism prior to P2P synchronization. Other than that, this
release includes NEP-24 standard support at both compiler and SC bindings generator
levels. A large number of tiny user-facing enhancements is rolled out for RPC
actor/ivoker, `neotest` and `unwrap` packages as far as for CLI utilities. Also,
this release contains a set of NeoGo VM bug fixes inspired by differential VM fuzzing
study conducted by our external contributor @Slava0135 and a set of tiny VM CLI
enhancements introduced by @ixje.
Some deprecated functionality has been removed according to the schedule, see more
details in the `Behaviour changes` section. Also, for those node operators who would
like to check out our new NeoFS BlockFetcher node extension, we'd recommend to add
corresponding section to the node's configuration (see the default node configuration
for N3/NeoFS networks for example).
This release is fully compatible with 3.7.5 version of C# node, no DB resync is
needed.
New features:
* new NeoFS BlockFetcher service that allows to sync node from NeoFS chain dump
(#3515, #3636, #3637, #3632, #3691, #3706, #3668, #3713)
* new `util upload-bin` CLI command that allows to upload blocks from Neo chains to
NeoFS (#3578, #3625, #3626, #3633, #3637, #3638, #3643, #3650, #3662, #3684,
#3686, #3691)
* new `delete` and `ib` VM CLI commands for brealpoints management (#3674)
* NEP-24 standard support (#3560)
* Echidna hardfork introduced, but not yet enabled (please, note that this is only
a preview that includes a part of scheduled changes and will be changed in an
incompatible way before the full support, hence, this hardfork may be enabled for
experimental purpose only) (#3554)
Behavior changes:
* neotest's AddSystemFee and TestInvoke are bound to Executor state (#3551)
* getversion RPC response is extended with seed list and standby committee (#3540)
* support only two latest versions of Go instead of three (#3567)
* some deprecated functionality is dropped: unmarshalling code for the old
`getpeers` RPC response, `NEOBalance` stackitem deserializer compatibility code,
`serv_node_version` Prometheus gauge metric, outdated RPC error codes support,
block-based web-socket transaction awaiting (#3690)
Improvements:
* documentation updates (#3545, #3663, #3666, #3678, #3683, #3708)
* netmode package is extended with public NeoFS chain IDs (#3539)
* dBFT library upgrades (#3541, #3711)
* migration to Docker Compose V2 (#3547)
* system fee required for contract deployment in neotest is precisely calculated
(#3551)
* ability to customize awaiting options for PollingBased RPC waiter (#3556)
* Go 1.23 support, bump minimum required Go version up to Go 1.22 (#3567)
* a set of dependent libraries upgrades (#3570)
* extend the list of supported SC parameters for RPC actor/invoker (#3583)
* Null stackitem result handling for autogenerated RPC bindings (#3584)
* macos-12 support is removed, macos-14 support is added (#3657)
* allow to get NEP-11/NEP-17 balances via CLI using account address only (#3659)
* expose VM slot getters (#3677)
* add `unwrap.ErrNull` error to handle Null stackitem returned (#3695)
* extend web-socket notification subsystem with notification parameter filters
(#3689)
* explicitly prohibit unknown configuration fields for `contract generate-*` CLI
commands (#3708)
* extend compiled smart contract identifier in neotest cache (#3709)
* move `neogo_version` metric out of `network` package (#3712)
Bugs fixed:
* fees of ditched transaction are not cleared from mempool (#3537)
* extension of SC permission descriptor doesn't clear wildcard status (#3544)
* example of NeoGo VM script fails VM execution (#3593)
* a call to `getunclaimedGas` of native Neo contract for account with zero balance
results in VM failure (#3589)
* `MODMUL` VM opcode handler returns wrong results for negative arguments (#3599)
* neotest coverage extension panics on attempt to collect coverage for contract with
missing debug information (#3600)
* `MODPOW`VM opcode handler returns wrong results for negative base (#3649)
* node panic on SIGHUP (#3661)
* HTTP return code of RPC requests diverges from C# RPC server's behaviour (#3665)
* a set of bugs in unit tests (#3442, #3680)
* `POPITEM` VM opcode handler counts stack references improperly (#3688)
* `PACKMAP` VM opcode handler ignores duplicating map keys (#3685)
* chain restore from genesis block fails with StateRootInHeader extension enabled
(#3697)
* hardfork-dependent methods are not included into native contract metadata starting
from the hardfork height (#3704)
* outdated keyword usage in Dockerfiles (#3710)
## 0.106.3 "Lyophilization" (29 Jul 2024)
This 3.7.5-compatible version includes a number of important fixes, so please
upgrade your nodes. Some minor extensions were also added.
Resynchronization (or state reset) is required for testnet (because of a bug
leading to state difference since 4368840), but not required for mainnet.
New features:
* embedded mainnet/testnet/NeoFS node configuration files (#3477, #3504)
Behavior changes:
* CLI no longer panics if error occurs (#3495)
* MaxTraceableBlocks is 17280 now for NeoFS networks (#3518)
* minimal default RPC `SessionExpirationTime` is 5s now (#3529)
Improvements:
* RPC actor interface extension with WaitSuccess method (#3491)
* Signers() API for RPC invokers (#3492)
* SignerAccounts() API for RPC actors (#3492)
* getpeers RPC extension with the user agent and last known block height data
(#3481)
* OnExecHook() API for VM (#3460)
* more details in witness verification error message (#3508)
* CLI help and error string format unification (#3495, #3520)
* CLI library (github.com/urfave/cli) upgrade to 2.27.2 (from v1 API, #3495)
* microoptimization of extensible sender list calculation (#3500)
* microoptimization of chain dump code (#3514)
* documentation and error messages (#3526, #3527)
Bugs fixed:
* RPC `SessionExpirationTime` could be zero in some configurations (#3529)
* panic in WSClient unsubscription code in some multithreaded cases (#3532)
* missing PrimaryIndex in Ledger's getBlock() result (#3534)
* contract manifests with null groups were accepted (#3523)
* contract manifests with invalid features were accepted (#3523)
* contract manifests with null trusts were accepted (#3523)
* WSClient deadlock in some disconnection cases (#3535)
## 0.106.2 "Keratinization" (13 Jun 2024)
A 3.7.5-compatible version introducing new Domovoi hardfork that brings two fixes to
the protocol: using executing contract state to check contract call permissions
(included into this NeoGo release) and proper VM items refcounting for
System.Runtime.GetNotifications handler (not included into this NeoGo release because
we've never had this bug). Since the second bug is C#-specific and does not lead to
the state differences in mainnet/testnet, we've decided not to break the NeoGo node
to follow pre-Domovoi C# node implementation. Thus, differences in application logs
for several T5 transactions before Domovoi hardfork are expected and won't be fixed.
Please, ensure your node configuration includes the Domovoi hardfork. No DB
resynchronisation is required.
New features:
* Domovoi hardfork scheduled for 5570000 block of mainnet and 4144000 block of T5
testnet (#3476, #3473, #3486, #3487)
Behavior changes:
* hide node logs timestamp if the node is running not in TTY (#3468)
* distinguish log level for various node peer disconnection reasons (#3469)
Improvements:
* ensure NeoFS nodes are configured when processing NeoFS oracle requests (#3455)
* NeoFS SDK dependency upgrade (#3483)
* ensure System.Runtime.GetNotifications handler can't break the MaxStackSize
constraint before and after Domovoi hardfork (#3485)
Bugs fixed:
* deployed contract script is included into wallet's account (#3470)
* updated contract state is used to verify contract call permissions before the
Domovoi hardfork (#3473)
## 0.106.1 "Implication" (3 Jun 2024)
An urgent release that fixes mainnet state difference at block 5462944 which halts
blocks processing starting from the height 5468658. This release requires full node
DB resynchronization for mainnet nodes. T5 testnet DB state is not affected by this
bug (at least up to the current 4087361 height). Thus, DB resynchronisation may be
skipped for testnet nodes. No configuration changes implied.
Bugs fixed:
* mainnet state difference at block 5462944 caused by runtime notification
permissions check against the updated contract state instead of executing state
(#3472)
* unused neofs-contract dependency in the node modules (#3458)
## 0.106.0 "Zephyranthes" (21 May 2024)
We're rolling out a large set of updates including all of Neo 3.7.4 protocol changes:

View file

@ -1,6 +1,6 @@
# Builder image
# Keep go version in sync with Build GA job.
FROM golang:1.22-alpine as builder
FROM golang:1.23-alpine AS builder
# Display go version for information purposes.
RUN go version

View file

@ -1,6 +1,6 @@
# Builder image
# Keep go version in sync with Build GA job.
FROM golang:1.22.0-windowsservercore-ltsc2022 as builder
FROM golang:1.23.0-windowsservercore-ltsc2022 AS builder
COPY . /neo-go

View file

@ -3,7 +3,7 @@ REPONAME = "neo-go"
NETMODE ?= "privnet"
BINARY=neo-go
BINARY_PATH=./bin/$(BINARY)$(shell go env GOEXE)
GO_VERSION ?= 1.20
GO_VERSION ?= 1.23
DESTDIR = ""
SYSCONFIGDIR = "/etc"
BINDIR = "/usr/bin"
@ -22,6 +22,11 @@ BUILD_FLAGS = "-X '$(REPO)/pkg/config.Version=$(VERSION)' -X '$(REPO)/cli/smartc
IMAGE_REPO=nspccdev/neo-go
DISABLE_NEOTEST_COVER=1
ROOT_DIR:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
GOMODDIRS=$(dir $(shell find $(ROOT_DIR) -name go.mod))
# All of the targets are phony here because we don't really use make dependency
# tracking for files
.PHONY: build $(BINARY) deps image docker/$(BINARY) image-latest image-push image-push-latest clean-cluster \
@ -107,8 +112,13 @@ test:
vet:
@go vet ./...
lint:
@golangci-lint run
.golangci.yml:
curl -L -o $@ https://github.com/nspcc-dev/.github/raw/master/.golangci.yml
lint: .golangci.yml
@for dir in $(GOMODDIRS); do \
(cd "$$dir" && golangci-lint run --config $(ROOT_DIR)/$< | sed -r "s,^,$$dir," | sed -r "s,^$(ROOT_DIR),,") \
done
fmt:
@gofmt -l -w -s $$(find . -type f -name '*.go'| grep -v "/vendor/")
@ -131,19 +141,19 @@ env_image:
env_up:
@echo "=> Bootup environment"
@echo " Docker-compose file: $(DC_FILE)"
@docker-compose -f $(DC_FILE) up -d node_one node_two node_three node_four
@docker compose -f $(DC_FILE) up -d node_one node_two node_three node_four
env_single:
@echo "=> Bootup environment"
@docker-compose -f $(DC_FILE) up -d node_single
@docker compose -f $(DC_FILE) up -d node_single
env_down:
@echo "=> Stop environment"
@docker-compose -f $(DC_FILE) down
@docker compose -f $(DC_FILE) down
env_restart:
@echo "=> Stop and start environment"
@docker-compose -f $(DC_FILE) restart
@docker compose -f $(DC_FILE) restart
env_clean: env_down
@echo "=> Cleanup environment"

View file

@ -51,7 +51,7 @@ NeoGo, `:latest` points to the latest release) or build yourself.
### Building
Building NeoGo requires Go 1.20+ and `make`:
Building NeoGo requires Go 1.22+ and `make`:
```
make

View file

@ -7,11 +7,10 @@ functionality.
## Versions 0.7X.Y (as needed)
* Neo 2.0 support (bug fixes, minor functionality additions)
## Version 0.107.0 (~Jun-Jul 2024)
## Version 0.109.0 (~April 2025)
* protocol updates
* bug fixes
* node resynchronisation from local DB
* CLI library upgrade
* NeoFS-based synchronization
## Version 1.0 (2024, TBD)
* stable version
@ -25,47 +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.
## GetPeers RPC server response type changes and RPC client support
GetPeers RPC command returns a list of Peers where the port type has changed from
string to uint16 to match C#. The RPC client currently supports unmarshalling both
formats.
Removal of Peer unmarshalling with string based ports is scheduled for Jun-Jul 2024
(~0.107.0 release).
## `NEOBalance` from stack item
We check struct items count before convert LastGasPerVote to let RPC client be compatible with
old versions.
Removal of this compatiblility code is scheduled for Jun-Jul 2024.
## `serv_node_version` Prometheus gauge metric
This metric is replaced by the new `neogo_version` and `server_id` Prometheus gauge
metrics with proper version formatting. `neogo_version` contains NeoGo version
hidden under `version` label and `server_id` contains network server ID hidden
under `server_id` label.
Removal of `serv_node_version` is scheduled for Jun-Jul 2024 (~0.107.0 release).
## RPC error codes returned by old versions and C#-nodes
NeoGo retains certain deprecated error codes: `neorpc.ErrCompatGeneric`,
`neorpc.ErrCompatNoOpenedWallet`. They returned by nodes not compliant with the
neo-project/proposals#156 (NeoGo pre-0.102.0 and all known C# versions).
Removal of the deprecated RPC error codes is planned for Jun-Jul 2024 (~0.107.0
release).
## Block based web-socket waiter transaction awaiting
Web-socket RPC based `waiter.EventWaiter` uses `header_of_added_block` notifications
subscription to manage transaction awaiting. To support old NeoGo RPC servers
(older than 0.105.0) that do not have block headers subscription ability,
event-based waiter fallbacks to the old way of block monitoring with
`block_added` notifications subscription.
Removal of stale RPC server compatibility code from `waiter.EventWaiter` is
scheduled for Jun-Jul 2024 (~0.107.0 release).

View file

@ -12,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/cli/vm"
"github.com/nspcc-dev/neo-go/cli/wallet"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func versionPrinter(c *cli.Context) {

View file

@ -12,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
@ -138,16 +138,16 @@ const (
// GetSignersFromContext returns signers parsed from context args starting
// from the specified offset.
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) {
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, cli.ExitCoder) {
args := ctx.Args()
var (
signers []transaction.Signer
err error
)
if args.Present() && len(args) > offset {
signers, err = ParseSigners(args[offset:])
if args.Present() && args.Len() > offset {
signers, err = ParseSigners(args.Slice()[offset:])
if err != nil {
return nil, cli.NewExitError(err, 1)
return nil, cli.Exit(err, 1)
}
}
return signers, nil
@ -224,13 +224,14 @@ func parseCosigner(c string) (transaction.Signer, error) {
res.AllowedGroups = append(res.AllowedGroups, pub)
}
default:
}
}
return res, nil
}
// GetDataFromContext returns data parameter from context args.
func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
func GetDataFromContext(ctx *cli.Context) (int, any, cli.ExitCoder) {
var (
data any
offset int
@ -239,17 +240,17 @@ func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
)
args := ctx.Args()
if args.Present() {
offset, params, err = ParseParams(args, true)
offset, params, err = ParseParams(args.Slice(), true)
if err != nil {
return offset, nil, cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
return offset, nil, cli.Exit(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
}
if len(params) > 1 {
return offset, nil, cli.NewExitError("'data' should be represented as a single parameter", 1)
return offset, nil, cli.Exit("'data' should be represented as a single parameter", 1)
}
if len(params) != 0 {
data, err = smartcontract.ExpandParameterToEmitable(params[0])
if err != nil {
return offset, nil, cli.NewExitError(fmt.Sprintf("failed to convert 'data' to emitable type: %s", err.Error()), 1)
return offset, nil, cli.Exit(fmt.Sprintf("failed to convert 'data' to emitable type: %s", err.Error()), 1)
}
}
}
@ -258,9 +259,9 @@ func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
// EnsureNone returns an error if there are any positional arguments present.
// It can be used to check for them in commands that don't accept arguments.
func EnsureNone(ctx *cli.Context) *cli.ExitError {
func EnsureNone(ctx *cli.Context) cli.ExitCoder {
if ctx.Args().Present() {
return cli.NewExitError("additional arguments given while this command expects none", 1)
return cli.Exit(fmt.Errorf("additional arguments given while this command expects none"), 1)
}
return nil
}
@ -348,3 +349,14 @@ func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers
}
return signersAccounts, nil
}
// EnsureNotEmpty returns a function that checks if the flag with the given name
// is not empty.
func EnsureNotEmpty(flagName string) func(*cli.Context, string) error {
return func(ctx *cli.Context, name string) error {
if ctx.String(flagName) == "" {
return cli.Exit(fmt.Errorf("required flag --%s is empty", flagName), 1)
}
return nil
}
}

View file

@ -7,7 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// Address is a wrapper for a Uint160 with flag.Value methods.
@ -16,11 +16,15 @@ type Address struct {
Value util.Uint160
}
// AddressFlag is a flag with type string.
// AddressFlag is a flag with type Uint160.
type AddressFlag struct {
Name string
Usage string
Value Address
Name string
Usage string
Value Address
Aliases []string
Required bool
Hidden bool
Action func(*cli.Context, string) error
}
var (
@ -37,7 +41,7 @@ func (a Address) String() string {
func (a *Address) Set(s string) error {
addr, err := ParseAddress(s)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
a.IsSet = true
a.Value = addr
@ -63,9 +67,9 @@ func (f AddressFlag) IsSet() bool {
// (for usage defaults).
func (f AddressFlag) String() string {
var names []string
eachName(f.Name, func(name string) {
for _, name := range f.Names() {
names = append(names, getNameHelp(name))
})
}
return strings.Join(names, ", ") + "\t" + f.Usage
}
@ -77,17 +81,57 @@ func getNameHelp(name string) string {
return fmt.Sprintf("--%s value", name)
}
// GetName returns the name of the flag.
func (f AddressFlag) GetName() string {
return f.Name
// Names returns the names of the flag.
func (f AddressFlag) Names() []string {
return cli.FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether the flag is required.
func (f AddressFlag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false.
func (f AddressFlag) IsVisible() bool {
return !f.Hidden
}
// TakesValue returns true of the flag takes a value, otherwise false.
func (f AddressFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag.
func (f AddressFlag) GetUsage() string {
return f.Usage
}
// Apply populates the flag given the flag set and environment.
// Ignores errors.
func (f AddressFlag) Apply(set *flag.FlagSet) {
eachName(f.Name, func(name string) {
func (f AddressFlag) Apply(set *flag.FlagSet) error {
for _, name := range f.Names() {
set.Var(&f.Value, name, f.Usage)
})
}
return nil
}
// RunAction executes flag action if set.
func (f AddressFlag) RunAction(c *cli.Context) error {
if f.Action != nil {
return f.Action(c, address.Uint160ToString(f.Value.Value))
}
return nil
}
// GetValue returns the flags value as string representation.
func (f AddressFlag) GetValue() string {
return address.Uint160ToString(f.Value.Value)
}
// Get returns the flags value in the given Context.
func (f AddressFlag) Get(ctx *cli.Context) Address {
adr := ctx.Generic(f.Name).(*Address)
return *adr
}
// ParseAddress parses a Uint160 from either an LE string or an address.

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
)
func TestParseAddress(t *testing.T) {
@ -109,22 +110,102 @@ func TestAddress_getNameHelp(t *testing.T) {
require.Equal(t, "--flag value", getNameHelp("flag"))
}
func TestAddressFlag_GetName(t *testing.T) {
func TestAddressFlag_Names(t *testing.T) {
flag := AddressFlag{
Name: "my flag",
Name: "flag",
Aliases: []string{"my"},
}
require.Equal(t, "my flag", flag.GetName())
require.Equal(t, []string{"flag", "my"}, flag.Names())
}
func TestAddress(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(io.Discard) // don't pollute test output
addr := AddressFlag{Name: "addr, a"}
addr.Apply(f)
addr := AddressFlag{Name: "addr", Aliases: []string{"a"}}
err := addr.Apply(f)
require.NoError(t, err)
require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))
require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String())
require.NoError(t, f.Parse([]string{"-a", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))
require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String())
require.Error(t, f.Parse([]string{"--addr", "kek"}))
}
func TestAddressFlag_IsRequired(t *testing.T) {
flag := AddressFlag{Required: true}
require.True(t, flag.IsRequired())
flag.Required = false
require.False(t, flag.IsRequired())
}
func TestAddressFlag_IsVisible(t *testing.T) {
flag := AddressFlag{Hidden: false}
require.True(t, flag.IsVisible())
flag.Hidden = true
require.False(t, flag.IsVisible())
}
func TestAddressFlag_TakesValue(t *testing.T) {
flag := AddressFlag{}
require.True(t, flag.TakesValue())
}
func TestAddressFlag_GetUsage(t *testing.T) {
flag := AddressFlag{Usage: "Specify the address"}
require.Equal(t, "Specify the address", flag.GetUsage())
}
func TestAddressFlag_GetValue(t *testing.T) {
addrValue := util.Uint160{1, 2, 3}
flag := AddressFlag{Value: Address{IsSet: true, Value: addrValue}}
expectedStr := address.Uint160ToString(addrValue)
require.Equal(t, expectedStr, flag.GetValue())
}
func TestAddressFlag_Get(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
flag := AddressFlag{
Name: "testAddress",
Value: Address{Value: util.Uint160{1, 2, 3}, IsSet: false},
}
set.Var(&flag.Value, "testAddress", "test usage")
require.NoError(t, set.Set("testAddress", address.Uint160ToString(util.Uint160{3, 2, 1})))
expected := flag.Get(ctx)
require.True(t, expected.IsSet)
require.Equal(t, util.Uint160{3, 2, 1}, expected.Value)
}
func TestAddressFlag_RunAction(t *testing.T) {
called := false
action := func(ctx *cli.Context, s string) error {
called = true
require.Equal(t, address.Uint160ToString(util.Uint160{1, 2, 3}), s)
return nil
}
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
flag := AddressFlag{
Action: action,
Value: Address{IsSet: true, Value: util.Uint160{4, 5, 6}},
}
expected := address.Uint160ToString(util.Uint160{1, 2, 3})
set.Var(&flag.Value, "testAddress", "test usage")
require.NoError(t, set.Set("testAddress", expected))
require.Equal(t, expected, flag.GetValue())
err := flag.RunAction(ctx)
require.NoError(t, err)
require.True(t, called)
}

View file

@ -5,7 +5,7 @@ import (
"strings"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// Fixed8 is a wrapper for a Uint160 with flag.Value methods.
@ -15,9 +15,13 @@ type Fixed8 struct {
// Fixed8Flag is a flag with type string.
type Fixed8Flag struct {
Name string
Usage string
Value Fixed8
Name string
Usage string
Value Fixed8
Aliases []string
Required bool
Hidden bool
Action func(*cli.Context, string) error
}
var (
@ -34,7 +38,7 @@ func (a Fixed8) String() string {
func (a *Fixed8) Set(s string) error {
f, err := fixedn.Fixed8FromString(s)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
a.Value = f
return nil
@ -45,31 +49,75 @@ func (a *Fixed8) Fixed8() fixedn.Fixed8 {
return a.Value
}
// IsSet checks if flag was set to a non-default value.
func (f Fixed8Flag) IsSet() bool {
return f.Value.Value != 0
}
// String returns a readable representation of this value
// (for usage defaults).
func (f Fixed8Flag) String() string {
var names []string
eachName(f.Name, func(name string) {
for _, name := range f.Names() {
names = append(names, getNameHelp(name))
})
}
return strings.Join(names, ", ") + "\t" + f.Usage
}
// GetName returns the name of the flag.
func (f Fixed8Flag) GetName() string {
return f.Name
// Names returns the names of the flag.
func (f Fixed8Flag) Names() []string {
return cli.FlagNames(f.Name, f.Aliases)
}
// IsRequired returns whether the flag is required.
func (f Fixed8Flag) IsRequired() bool {
return f.Required
}
// IsVisible returns true if the flag is not hidden, otherwise false.
func (f Fixed8Flag) IsVisible() bool {
return !f.Hidden
}
// TakesValue returns true if the flag takes a value, otherwise false.
func (f Fixed8Flag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag.
func (f Fixed8Flag) GetUsage() string {
return f.Usage
}
// Apply populates the flag given the flag set and environment.
// Ignores errors.
func (f Fixed8Flag) Apply(set *flag.FlagSet) {
eachName(f.Name, func(name string) {
func (f Fixed8Flag) Apply(set *flag.FlagSet) error {
for _, name := range f.Names() {
set.Var(&f.Value, name, f.Usage)
})
}
return nil
}
// Fixed8FromContext returns a parsed util.Fixed8 value provided flag name.
func Fixed8FromContext(ctx *cli.Context, name string) fixedn.Fixed8 {
return ctx.Generic(name).(*Fixed8).Value
}
// RunAction executes flag action if set.
func (f Fixed8Flag) RunAction(c *cli.Context) error {
if f.Action != nil {
return f.Action(c, f.Value.Value.String())
}
return nil
}
// GetValue returns the flags value as string representation.
func (f Fixed8Flag) GetValue() string {
return f.Value.Value.String()
}
// Get returns the flags value in the given Context.
func (f Fixed8Flag) Get(ctx *cli.Context) Fixed8 {
adr := ctx.Generic(f.Name).(*Fixed8)
return *adr
}

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
)
func TestFixed8_String(t *testing.T) {
@ -45,22 +46,83 @@ func TestFixed8Flag_String(t *testing.T) {
require.Equal(t, "--myFlag value\tGas amount", flag.String())
}
func TestFixed8Flag_GetName(t *testing.T) {
func TestFixed8Flag_Names(t *testing.T) {
flag := Fixed8Flag{
Name: "myFlag",
}
require.Equal(t, "myFlag", flag.GetName())
require.Equal(t, []string{"myFlag"}, flag.Names())
}
func TestFixed8(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(io.Discard) // don't pollute test output
gas := Fixed8Flag{Name: "gas, g"}
gas.Apply(f)
gas := Fixed8Flag{Name: "gas", Aliases: []string{"g"}, Usage: "Gas amount", Value: Fixed8{Value: 0}, Required: true, Hidden: false, Action: nil}
err := gas.Apply(f)
require.NoError(t, err)
require.NoError(t, f.Parse([]string{"--gas", "0.123"}))
require.Equal(t, "0.123", f.Lookup("g").Value.String())
require.NoError(t, f.Parse([]string{"-g", "0.456"}))
require.Equal(t, "0.456", f.Lookup("g").Value.String())
require.Error(t, f.Parse([]string{"--gas", "kek"}))
}
func TestFixed8Flag_Get(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
flag := Fixed8Flag{
Name: "testFlag",
}
fixedFlag := Fixed8{Value: fixedn.Fixed8(123)}
set.Var(&fixedFlag, "testFlag", "test usage")
require.NoError(t, set.Set("testFlag", "0.00000321"))
expected := flag.Get(ctx)
require.Equal(t, fixedn.Fixed8(321), expected.Value)
}
func TestFixed8Flag_GetValue(t *testing.T) {
f := Fixed8Flag{Value: Fixed8{Value: fixedn.Fixed8(123)}}
require.Equal(t, "0.00000123", f.GetValue())
require.True(t, f.TakesValue())
}
func TestFixed8Flag_RunAction(t *testing.T) {
called := false
action := func(ctx *cli.Context, s string) error {
called = true
require.Equal(t, "0.00000123", s)
return nil
}
app := cli.NewApp()
set := flag.NewFlagSet("test", flag.ContinueOnError)
ctx := cli.NewContext(app, set, nil)
f := Fixed8Flag{
Action: action,
Value: Fixed8{Value: fixedn.Fixed8(123)},
}
err := f.RunAction(ctx)
require.NoError(t, err)
require.True(t, called)
}
func TestFixed8Flag_GetUsage(t *testing.T) {
f := Fixed8Flag{Usage: "Use this flag to specify gas amount"}
require.Equal(t, "Use this flag to specify gas amount", f.GetUsage())
}
func TestFixed8Flag_IsVisible(t *testing.T) {
f := Fixed8Flag{Hidden: false}
require.True(t, f.IsVisible())
f.Hidden = true
require.False(t, f.IsVisible())
}
func TestFixed8Flag_IsRequired(t *testing.T) {
f := Fixed8Flag{Required: false}
require.False(t, f.IsRequired())
f.Required = true
require.True(t, f.IsRequired())
}

View file

@ -1,11 +0,0 @@
package flags
import "strings"
func eachName(longName string, fn func(string)) {
parts := strings.Split(longName, ",")
for _, name := range parts {
name = strings.Trim(name, " ")
fn(name)
}
}

View file

@ -1,17 +0,0 @@
package flags
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEachName(t *testing.T) {
expected := "*one*two*three"
actual := ""
eachName(" one,two ,three", func(s string) {
actual += "*" + s
})
require.Equal(t, expected, actual)
}

View file

@ -1,6 +1,7 @@
package main
import (
"fmt"
"os"
"github.com/nspcc-dev/neo-go/cli/app"
@ -10,6 +11,7 @@ func main() {
ctl := app.New()
if err := ctl.Run(os.Args); err != nil {
panic(err)
fmt.Fprintln(ctl.ErrWriter, err)
os.Exit(1)
}
}

View file

@ -55,11 +55,14 @@ func TestNEP11Import(t *testing.T) {
"--wallet", walletPath,
}
// missing token hash
e.RunWithError(t, args...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, args...)
// excessive parameters
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE(), "something")...)
// empty token hash
e.RunWithErrorCheck(t, `invalid value "" for flag -token: zero length string`, append(args, "--token", "")...)
// good: non-divisible
e.Run(t, append(args, "--token", nnsContractHash.StringLE())...)
@ -229,7 +232,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdOwnerOf...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
// ownerOf: missing token ID
@ -244,11 +247,11 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flags "token, address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flag "address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", nftOwnerAddr)
// tokensOf: good
@ -260,7 +263,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdProperties...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE())
// properties: no token ID
@ -286,7 +289,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokens...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: excessive parameters
@ -514,7 +517,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdOwnerOf...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
// ownerOfD: missing token ID
@ -529,11 +532,11 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flags "token, address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...)
e.RunWithErrorCheck(t, `Required flag "address" not set`, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", testcli.ValidatorAddr)
// tokensOf: good
@ -547,7 +550,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdProperties...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE())
// properties: no token ID
@ -580,7 +583,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokens...)
e.RunWithErrorCheck(t, `Required flag "token" not set`, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: good, several tokens

View file

@ -3,7 +3,9 @@ package nep_test
import (
"io"
"math/big"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"testing"
@ -21,52 +23,111 @@ func TestNEP17Balance(t *testing.T) {
e := testcli.NewExecutor(t, true)
args := []string{
"neo-go", "wallet", "nep17", "multitransfer",
"neo-go", "wallet", "nep17", "multitransfer", "--force",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--from", testcli.ValidatorAddr,
"GAS:" + testcli.TestWalletMultiAccount1 + ":1",
"NEO:" + testcli.TestWalletMultiAccount1 + ":10",
"GAS:" + testcli.TestWalletMultiAccount3 + ":3",
"--force",
}
e.In.WriteString("one\r")
e.Run(t, args...)
e.CheckTxPersisted(t)
cmdbalance := []string{"neo-go", "wallet", "nep17", "balance"}
cmdbase := append(cmdbalance,
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.TestWalletMultiPath,
)
cmd := append(cmdbase, "--address", testcli.TestWalletMultiAccount1)
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...)
})
t.Run("NEO", func(t *testing.T) {
b, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
checkResult := func(t *testing.T) {
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
e.CheckEOF(t)
var checkAcc1NEO = func(t *testing.T, e *testcli.Executor, line string) {
if line == "" {
line = e.GetNextLine(t)
}
t.Run("Alias", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "NEO")...)
checkResult(t)
})
t.Run("Hash", func(t *testing.T) {
e.Run(t, append(cmd, "--token", e.Chain.GoverningTokenHash().StringLE())...)
checkResult(t)
})
balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
}
var checkAcc1GAS = func(t *testing.T, e *testcli.Executor, line string) {
if line == "" {
line = e.GetNextLine(t)
}
e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:")
}
var checkAcc1Assets = func(t *testing.T, e *testcli.Executor) {
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1)
// The order of assets is undefined.
for range 2 {
line := e.GetNextLine(t)
if strings.Contains(line, "GAS") {
checkAcc1GAS(t, e, line)
} else {
checkAcc1NEO(t, e, line)
}
}
}
var (
cmdbase = []string{"neo-go", "wallet", "nep17", "balance", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
addrparams = []string{"--address", testcli.TestWalletMultiAccount1}
walletparams = []string{"--wallet", testcli.TestWalletMultiPath}
)
t.Run("Bad wallet", func(t *testing.T) {
e.RunWithError(t, append(cmdbase, "--wallet", "/dev/null")...)
})
t.Run("GAS", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "GAS")...)
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
b := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String()+"$")
t.Run("empty wallet", func(t *testing.T) {
tmpDir := t.TempDir()
walletPath := filepath.Join(tmpDir, "emptywallet.json")
require.NoError(t, os.WriteFile(walletPath, []byte("{}"), 0o644))
e.RunWithError(t, append(cmdbase, "--wallet", walletPath)...)
})
t.Run("no wallet or address", func(t *testing.T) {
e.RunWithError(t, cmdbase...)
})
for name, params := range map[string][]string{
"address only": addrparams,
"address with wallet": slices.Concat(walletparams, addrparams),
} {
var cmd = append(cmdbase, params...)
t.Run(name, func(t *testing.T) {
t.Run("all tokens", func(t *testing.T) {
e.Run(t, cmd...)
checkAcc1Assets(t, e)
e.CheckEOF(t)
})
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--token", "NEO", "gas")...)
})
})
t.Run("NEO", func(t *testing.T) {
checkResult := func(t *testing.T) {
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
checkAcc1NEO(t, e, "")
e.CheckEOF(t)
}
t.Run("Alias", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "NEO")...)
checkResult(t)
})
t.Run("Hash", func(t *testing.T) {
e.Run(t, append(cmd, "--token", e.Chain.GoverningTokenHash().StringLE())...)
checkResult(t)
})
})
t.Run("GAS", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "GAS")...)
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
checkAcc1GAS(t, e, "")
})
t.Run("Bad token", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "kek")...)
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
e.CheckNextLine(t, `^\s*Can't find data for "kek" token\s*`)
e.CheckEOF(t)
})
}
t.Run("inexistent wallet account", func(t *testing.T) {
var cmd = append(cmdbase, walletparams...)
e.RunWithError(t, append(cmd, "--address", "NSPCCpw8YmgNDYWiBfXJHRfz38NDjv6WW3")...)
})
t.Run("zero balance of known token", func(t *testing.T) {
e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...)
@ -77,24 +138,9 @@ func TestNEP17Balance(t *testing.T) {
e.CheckEOF(t)
})
t.Run("all accounts", func(t *testing.T) {
e.Run(t, cmdbase...)
e.Run(t, append(cmdbase, walletparams...)...)
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount1)
// The order of assets is undefined.
for i := 0; i < 2; i++ {
line := e.GetNextLine(t)
if strings.Contains(line, "GAS") {
e.CheckLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:")
} else {
balance, index := e.Chain.GetGoverningTokenBalance(testcli.TestWalletMultiAccount1Hash)
e.CheckLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String()+"$")
e.CheckNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
}
}
checkAcc1Assets(t, e)
e.CheckNextLine(t, "^\\s*$")
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2)
@ -107,15 +153,6 @@ func TestNEP17Balance(t *testing.T) {
e.CheckNextLine(t, "^\\s*Updated:")
e.CheckEOF(t)
})
t.Run("Bad token", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "kek")...)
e.CheckNextLine(t, "^\\s*Account\\s+"+testcli.TestWalletMultiAccount1)
e.CheckNextLine(t, `^\s*Can't find data for "kek" token\s*`)
e.CheckEOF(t)
})
t.Run("Bad wallet", func(t *testing.T) {
e.RunWithError(t, append(cmdbalance, "--wallet", "/dev/null")...)
})
}
func TestNEP17Transfer(t *testing.T) {
@ -134,10 +171,9 @@ func TestNEP17Transfer(t *testing.T) {
}
t.Run("missing receiver", func(t *testing.T) {
as := append([]string{}, args[:8]...)
as = append(as, args[10:]...)
as := slices.Concat(args[:8], args[10:])
e.In.WriteString("one\r")
e.RunWithError(t, as...)
e.RunWithErrorCheck(t, `Required flag "to" not set`, as...)
e.In.Reset()
})
@ -327,7 +363,7 @@ func TestNEP17ImportToken(t *testing.T) {
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
// missing token hash
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
e.RunWithErrorCheck(t, `Required flag "token" not set`, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath)

View file

@ -8,7 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func TestGetRPCClient(t *testing.T) {

View file

@ -14,6 +14,7 @@ import (
"strings"
"time"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/config"
@ -24,11 +25,15 @@ 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/urfave/cli"
"github.com/nspcc-dev/neofs-sdk-go/pool"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/term"
"gopkg.in/yaml.v3"
)
@ -41,74 +46,117 @@ 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{cli.StringFlag{
Name: "wallet, w",
Usage: "wallet to use to get the key for transaction signing; conflicts with --wallet-config flag",
}, cli.StringFlag{
Name: "wallet-config",
Usage: "path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag"},
var Wallet = []cli.Flag{
&cli.StringFlag{
Name: "wallet",
Aliases: []string{"w"},
Usage: "Wallet to use to get the key for transaction signing; conflicts with --wallet-config flag",
},
&cli.StringFlag{
Name: "wallet-config",
Usage: "Path to wallet config to use to get the key for transaction signing; conflicts with --wallet flag",
},
}
// Network is a set of flags for choosing the network to operate on
// (privnet/mainnet/testnet).
var Network = []cli.Flag{
cli.BoolFlag{Name: "privnet, p", Usage: "use private network configuration (if --config-file option is not specified)"},
cli.BoolFlag{Name: "mainnet, m", Usage: "use mainnet network configuration (if --config-file option is not specified)"},
cli.BoolFlag{Name: "testnet, t", Usage: "use testnet network configuration (if --config-file option is not specified)"},
cli.BoolFlag{Name: "unittest", Hidden: true},
&cli.BoolFlag{
Name: "privnet",
Aliases: []string{"p"},
Usage: "Use private network configuration (if --config-file option is not specified)",
},
&cli.BoolFlag{
Name: "mainnet",
Aliases: []string{"m"},
Usage: "Use mainnet network configuration (if --config-file option is not specified)",
},
&cli.BoolFlag{
Name: "testnet",
Aliases: []string{"t"},
Usage: "Use testnet network configuration (if --config-file option is not specified)",
},
&cli.BoolFlag{
Name: "unittest",
Hidden: true,
},
}
// RPC is a set of flags used for RPC connections (endpoint and timeout).
var RPC = []cli.Flag{
cli.StringFlag{
Name: RPCEndpointFlag + ", r",
Usage: "RPC node address",
&cli.StringFlag{
Name: RPCEndpointFlag,
Aliases: []string{"r"},
Usage: "RPC node address",
Required: true,
Action: cmdargs.EnsureNotEmpty("rpc-endpoint"),
},
cli.DurationFlag{
Name: "timeout, s",
Value: DefaultTimeout,
Usage: "Timeout for the operation",
&cli.DurationFlag{
Name: "timeout",
Aliases: []string{"s"},
Value: DefaultTimeout,
Usage: "Timeout for the operation",
},
}
// NeoFSRPC is a set of flags used for NeoFS RPC connections (endpoint).
var NeoFSRPC = []cli.Flag{&cli.StringSliceFlag{
Name: NeoFSRPCEndpointFlag,
Aliases: []string{"fsr"},
Usage: "List of NeoFS storage node RPC addresses (comma-separated or multiple --fs-rpc-endpoint flags)",
Required: true,
Action: func(ctx *cli.Context, fsRpcEndpoints []string) error {
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{
var Historic = &cli.StringFlag{
Name: "historic",
Usage: "Use historic state (height, block hash or state root hash)",
}
// Config is a flag for commands that use node configuration.
var Config = cli.StringFlag{
var Config = &cli.StringFlag{
Name: "config-path",
Usage: "path to directory with per-network configuration files (may be overridden by --config-file option for the configuration file)",
Usage: "Path to directory with per-network configuration files (may be overridden by --config-file option for the configuration file)",
}
// ConfigFile is a flag for commands that use node configuration and provide
// path to the specific config file instead of config path.
var ConfigFile = cli.StringFlag{
var ConfigFile = &cli.StringFlag{
Name: "config-file",
Usage: "path to the node configuration file (overrides --config-path option)",
Usage: "Path to the node configuration file (overrides --config-path option)",
}
// RelativePath is a flag for commands that use node configuration and provide
// a prefix to all relative paths in config files.
var RelativePath = cli.StringFlag{
var RelativePath = &cli.StringFlag{
Name: "relative-path",
Usage: "a prefix to all relative paths in the node configuration file",
Usage: "Prefix to all relative paths in the node configuration file",
}
// Debug is a flag for commands that allow node in debug mode usage.
var Debug = cli.BoolFlag{
Name: "debug, d",
Usage: "enable debug logging (LOTS of output, overrides configuration)",
var Debug = &cli.BoolFlag{
Name: "debug",
Aliases: []string{"d"},
Usage: "Enable debug logging (LOTS of output, overrides configuration)",
}
var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'")
var errInvalidHistoric = errors.New("invalid 'historic' parameter, neither a block number, nor a block/state hash")
var errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet' or '-w' flag or specify wallet config file with the '--wallet-config' flag")
var errConflictingWalletFlags = errors.New("--wallet flag conflicts with --wallet-config flag, please, provide one of them to specify wallet location")
@ -144,20 +192,37 @@ func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) {
// GetRPCClient returns an RPC client instance for the given Context.
func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cli.ExitCoder) {
endpoint := ctx.String(RPCEndpointFlag)
if len(endpoint) == 0 {
return nil, cli.NewExitError(errNoEndpoint, 1)
}
c, err := rpcclient.New(gctx, endpoint, rpcclient.Options{})
if err != nil {
return nil, cli.NewExitError(err, 1)
return nil, cli.Exit(err, 1)
}
err = c.Init()
if err != nil {
return nil, cli.NewExitError(err, 1)
return nil, cli.Exit(err, 1)
}
return c, nil
}
// GetNeoFSClientPool returns a NeoFS pool and a signer for the given Context.
func GetNeoFSClientPool(ctx *cli.Context, acc *wallet.Account) (user.Signer, 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) {
@ -172,7 +237,7 @@ func GetInvoker(c *rpcclient.Client, ctx *cli.Context, signers []transaction.Sig
// Might as well be a block hash, but it makes no practical difference.
return invoker.NewHistoricWithState(u256, c, signers), nil
}
return nil, cli.NewExitError(errInvalidHistoric, 1)
return nil, cli.Exit(errInvalidHistoric, 1)
}
// GetRPCWithInvoker combines GetRPCClient with GetInvoker for cases where it's
@ -200,7 +265,7 @@ func GetConfigFromContext(ctx *cli.Context) (config.Config, error) {
if len(configFile) != 0 {
return config.LoadFile(configFile, relativePath)
}
var configPath = "./config"
var configPath = config.DefaultConfigPath
if argCp := ctx.String("config-path"); argCp != "" {
configPath = argCp
}
@ -219,6 +284,7 @@ var (
// If logPath is configured -- function creates a dir and a file for logging.
// If logPath is configured on Windows -- function returns closer to be
// able to close sink for the opened log output file.
// If the program is run in TTY then logger adds timestamp to its entries.
func HandleLoggingParams(debug bool, cfg config.ApplicationConfiguration) (*zap.Logger, *zap.AtomicLevel, func() error, error) {
var (
level = zapcore.InfoLevel
@ -239,7 +305,11 @@ func HandleLoggingParams(debug bool, cfg config.ApplicationConfiguration) (*zap.
cc.DisableStacktrace = true
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
if term.IsTerminal(int(os.Stdout.Fd())) {
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
} else {
cc.EncoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {}
}
cc.Encoding = "console"
cc.Level = zap.NewAtomicLevelAt(level)
cc.Sampling = nil
@ -308,7 +378,7 @@ func GetRPCWithActor(gctx context.Context, ctx *cli.Context, signers []actor.Sig
a, actorErr := actor.New(c, signers)
if actorErr != nil {
c.Close()
return nil, nil, cli.NewExitError(fmt.Errorf("failed to create Actor: %w", actorErr), 1)
return nil, nil, cli.Exit(fmt.Errorf("failed to create Actor: %w", actorErr), 1)
}
return c, a, nil
}

View file

@ -8,7 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func TestGetNetwork(t *testing.T) {

View file

@ -2,9 +2,10 @@ package query
import (
"bytes"
"cmp"
"encoding/base64"
"fmt"
"sort"
"slices"
"strconv"
"strings"
"text/tabwriter"
@ -22,21 +23,22 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// NewCommands returns 'query' command.
func NewCommands() []cli.Command {
func NewCommands() []*cli.Command {
queryTxFlags := append([]cli.Flag{
cli.BoolFlag{
Name: "verbose, v",
Usage: "Output full tx info and execution logs",
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
Usage: "Output full tx info and execution logs",
},
}, options.RPC...)
return []cli.Command{{
return []*cli.Command{{
Name: "query",
Usage: "Query data from RPC node",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
{
Name: "candidates",
Usage: "Get candidates and votes",
@ -61,14 +63,14 @@ func NewCommands() []cli.Command {
{
Name: "tx",
Usage: "Query transaction status",
UsageText: "neo-go query tx <hash> -r endpoint [-s timeout] [-v]",
UsageText: "neo-go query tx -r endpoint [-s timeout] [-v] <hash>",
Action: queryTx,
Flags: queryTxFlags,
},
{
Name: "voter",
Usage: "Print NEO holder account state",
UsageText: "neo-go query voter <address> -r endpoint [-s timeout]",
UsageText: "neo-go query voter -r endpoint [-s timeout] <address>",
Action: queryVoter,
Flags: options.RPC,
},
@ -77,16 +79,16 @@ func NewCommands() []cli.Command {
}
func queryTx(ctx *cli.Context) error {
args := ctx.Args()
args := ctx.Args().Slice()
if len(args) == 0 {
return cli.NewExitError("Transaction hash is missing", 1)
return cli.Exit("transaction hash is missing", 1)
} else if len(args) > 1 {
return cli.NewExitError("only one transaction hash is accepted", 1)
return cli.Exit("only one transaction hash is accepted", 1)
}
txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x"))
if err != nil {
return cli.NewExitError(fmt.Sprintf("Invalid tx hash: %s", args[0]), 1)
return cli.Exit(fmt.Sprintf("invalid tx hash: %s", args[0]), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -94,25 +96,25 @@ func queryTx(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
txOut, err := c.GetRawTransactionVerbose(txHash)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
var res *result.ApplicationLog
if !txOut.Blockhash.Equals(util.Uint256{}) {
res, err = c.GetApplicationLog(txHash, nil)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
}
err = DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose"))
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return nil
}
@ -179,26 +181,29 @@ func queryCandidates(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
vals, err := c.GetCandidates()
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
comm, err := c.GetCommittee()
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
sort.Slice(vals, func(i, j int) bool {
if vals[i].Active != vals[j].Active {
return vals[i].Active
slices.SortFunc(vals, func(a, b result.Candidate) int {
if a.Active && !b.Active {
return 1
}
if vals[i].Votes != vals[j].Votes {
return vals[i].Votes > vals[j].Votes
if !a.Active && b.Active {
return -1
}
return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1
return cmp.Or(
cmp.Compare(a.Votes, b.Votes),
a.PublicKey.Cmp(&b.PublicKey),
)
})
var res []byte
res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n")
@ -225,12 +230,12 @@ func queryCommittee(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
comm, err := c.GetCommittee()
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
for _, k := range comm {
@ -251,12 +256,12 @@ func queryHeight(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
blockCount, err := c.GetBlockCount()
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
blockHeight := blockCount - 1 // GetBlockCount returns block count (including 0), not the highest block index.
@ -271,16 +276,16 @@ func queryHeight(ctx *cli.Context) error {
}
func queryVoter(ctx *cli.Context) error {
args := ctx.Args()
args := ctx.Args().Slice()
if len(args) == 0 {
return cli.NewExitError("No address specified", 1)
return cli.Exit("no address specified", 1)
} else if len(args) > 1 {
return cli.NewExitError("this command only accepts one address", 1)
return cli.Exit("this command only accepts one address", 1)
}
addr, err := flags.ParseAddress(args[0])
if err != nil {
return cli.NewExitError(fmt.Sprintf("wrong address: %s", args[0]), 1)
return cli.Exit(fmt.Sprintf("wrong address: %s", args[0]), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -294,14 +299,14 @@ func queryVoter(ctx *cli.Context) error {
st, err := neoToken.GetAccountState(addr)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if st == nil {
st = new(state.NEOBalance)
}
dec, err := neoToken.Decimals()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get decimals: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to get decimals: %w", err), 1)
}
voted := "null"
if st.VoteTo != nil {

87
cli/server/dump_bin.go Normal file
View file

@ -0,0 +1,87 @@
package server
import (
"fmt"
"os"
"path/filepath"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/urfave/cli/v2"
)
func dumpBin(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.Exit(err, 1)
}
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil {
return cli.Exit(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
}
count := uint32(ctx.Uint("count"))
start := uint32(ctx.Uint("start"))
chain, _, prometheus, pprof, err := InitBCWithMetrics(cfg, log)
if err != nil {
return err
}
defer func() {
pprof.ShutDown()
prometheus.ShutDown()
chain.Close()
}()
blocksCount := chain.BlockHeight() + 1
if start+count > blocksCount {
return cli.Exit(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", blocksCount-1, count, start), 1)
}
if count == 0 {
count = blocksCount - start
}
out := ctx.String("out")
if out == "" {
return cli.Exit("output directory is not specified", 1)
}
if _, err = os.Stat(out); os.IsNotExist(err) {
if err = os.MkdirAll(out, os.ModePerm); err != nil {
return cli.Exit(fmt.Sprintf("failed to create directory %s: %s", out, err), 1)
}
}
if err != nil {
return cli.Exit(fmt.Sprintf("failed to check directory %s: %s", out, err), 1)
}
for i := start; i < start+count; i++ {
blk, err := chain.GetBlock(chain.GetHeaderHash(i))
if err != nil {
return cli.Exit(fmt.Sprintf("failed to get block %d: %s", i, err), 1)
}
filePath := filepath.Join(out, fmt.Sprintf("block-%d.bin", i))
if err = saveBlockToFile(blk, filePath); err != nil {
return cli.Exit(fmt.Sprintf("failed to save block %d to file %s: %s", i, filePath, err), 1)
}
}
return nil
}
func saveBlockToFile(blk *block.Block, filePath string) error {
file, err := os.Create(filePath)
if err != nil {
return err
}
defer file.Close()
writer := io.NewBinWriterFromIO(file)
blk.EncodeBinary(writer)
return writer.Err
}

View file

@ -0,0 +1,91 @@
package server_test
import (
"os"
"path/filepath"
"strconv"
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
func TestDumpBin(t *testing.T) {
tmpDir := t.TempDir()
loadConfig := func(t *testing.T) config.Config {
chainPath := filepath.Join(tmpDir, "neogotestchain")
cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
return cfg
}
cfg := loadConfig(t)
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e := testcli.NewExecutor(t, false)
restoreArgs := []string{"neo-go", "db", "restore",
"--config-file", cfgPath, "--in", inDump}
e.Run(t, restoreArgs...)
t.Run("missing output directory", func(t *testing.T) {
args := []string{"neo-go", "db", "dump-bin",
"--config-file", cfgPath, "--out", ""}
e.RunWithErrorCheck(t, "output directory is not specified", args...)
})
t.Run("successful dump", func(t *testing.T) {
outDir := filepath.Join(tmpDir, "blocks")
args := []string{"neo-go", "db", "dump-bin",
"--config-file", cfgPath, "--out", outDir, "--count", "5", "--start", "0"}
e.Run(t, args...)
require.DirExists(t, outDir)
for i := range 5 {
blockFile := filepath.Join(outDir, "block-"+strconv.Itoa(i)+".bin")
require.FileExists(t, blockFile)
}
})
t.Run("invalid block range", func(t *testing.T) {
outDir := filepath.Join(tmpDir, "invalid-blocks")
args := []string{"neo-go", "db", "dump-bin",
"--config-file", cfgPath, "--out", outDir, "--count", "1000", "--start", "0"}
e.RunWithError(t, args...)
})
t.Run("zero blocks (full chain dump)", func(t *testing.T) {
outDir := filepath.Join(tmpDir, "full-dump")
args := []string{"neo-go", "db", "dump-bin",
"--config-file", cfgPath, "--out", outDir}
e.Run(t, args...)
require.DirExists(t, outDir)
for i := range 5 {
blockFile := filepath.Join(outDir, "block-"+strconv.Itoa(i)+".bin")
require.FileExists(t, blockFile)
}
})
t.Run("invalid config file", func(t *testing.T) {
outDir := filepath.Join(tmpDir, "blocks")
args := []string{"neo-go", "db", "dump-bin",
"--config-file", "invalid-config-path", "--out", outDir}
e.RunWithError(t, args...)
})
}

23
cli/server/metrics.go Normal file
View file

@ -0,0 +1,23 @@
package server
import (
"github.com/prometheus/client_golang/prometheus"
)
var neogoVersion = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Help: "NeoGo version",
Name: "version",
Namespace: "neogo",
},
[]string{"version"})
func setNeoGoVersion(nodeVer string) {
neogoVersion.WithLabelValues(nodeVer).Add(1)
}
func init() {
prometheus.MustRegister(
neogoVersion,
)
}

View file

@ -6,6 +6,7 @@ import (
"fmt"
"os"
"os/signal"
"slices"
"syscall"
"time"
@ -27,89 +28,97 @@ import (
"github.com/nspcc-dev/neo-go/pkg/services/oracle"
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv"
"github.com/nspcc-dev/neo-go/pkg/services/stateroot"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// NewCommands returns 'node' command.
func NewCommands() []cli.Command {
func NewCommands() []*cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
cfgFlags = append(cfgFlags, options.Network...)
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
copy(cfgWithCountFlags, cfgFlags)
cfgFlags = append(cfgFlags, options.Debug)
var cfgWithCountFlags = slices.Clone(cfgFlags)
cfgFlags = append(cfgFlags, options.Debug)
cfgWithCountFlags = append(cfgWithCountFlags,
cli.UintFlag{
Name: "count, c",
Usage: "number of blocks to be processed (default or 0: all chain)",
&cli.UintFlag{
Name: "count",
Aliases: []string{"c"},
Usage: "Number of blocks to be processed (default or 0: all chain)",
},
)
var cfgCountOutFlags = make([]cli.Flag, len(cfgWithCountFlags))
copy(cfgCountOutFlags, cfgWithCountFlags)
var cfgCountOutFlags = slices.Clone(cfgWithCountFlags)
cfgCountOutFlags = append(cfgCountOutFlags,
cli.UintFlag{
Name: "start, s",
Usage: "block number to start from (default: 0)",
&cli.UintFlag{
Name: "start",
Aliases: []string{"s"},
Usage: "Block number to start from",
},
cli.StringFlag{
Name: "out, o",
Usage: "Output file (stdout if not given)",
&cli.StringFlag{
Name: "out",
Aliases: []string{"o"},
Usage: "Output file (stdout if not given)",
},
)
var cfgCountInFlags = make([]cli.Flag, len(cfgWithCountFlags))
copy(cfgCountInFlags, cfgWithCountFlags)
var cfgCountInFlags = slices.Clone(cfgWithCountFlags)
cfgCountInFlags = append(cfgCountInFlags,
cli.StringFlag{
Name: "in, i",
Usage: "Input file (stdin if not given)",
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Usage: "Input file (stdin if not given)",
},
cli.StringFlag{
&cli.StringFlag{
Name: "dump",
Usage: "directory for storing JSON dumps",
Usage: "Directory for storing JSON dumps",
},
cli.BoolFlag{
Name: "incremental, n",
Usage: "use if dump is incremental",
&cli.BoolFlag{
Name: "incremental",
Aliases: []string{"n"},
Usage: "Use if dump is incremental",
},
)
var cfgHeightFlags = make([]cli.Flag, len(cfgFlags)+1)
copy(cfgHeightFlags, cfgFlags)
cfgHeightFlags[len(cfgHeightFlags)-1] = cli.UintFlag{
var cfgHeightFlags = slices.Clone(cfgFlags)
cfgHeightFlags = append(cfgHeightFlags, &cli.UintFlag{
Name: "height",
Usage: "Height of the state to reset DB to",
Required: true,
}
return []cli.Command{
})
return []*cli.Command{
{
Name: "node",
Usage: "start a NeoGo node",
Usage: "Start a NeoGo node",
UsageText: "neo-go node [--config-path path] [-d] [-p/-m/-t] [--config-file file]",
Action: startServer,
Flags: cfgFlags,
},
{
Name: "db",
Usage: "database manipulations",
Subcommands: []cli.Command{
Usage: "Database manipulations",
Subcommands: []*cli.Command{
{
Name: "dump",
Usage: "dump blocks (starting with block #1) to the file",
UsageText: "neo-go db dump -o file [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
Usage: "Dump blocks (starting with the genesis or specified block) to the file",
UsageText: "neo-go db dump [-o file] [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
Action: dumpDB,
Flags: cfgCountOutFlags,
},
{
Name: "dump-bin",
Usage: "Dump blocks (starting with the genesis or specified block) to the directory in binary format",
UsageText: "neo-go db dump-bin -o directory [-s start] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
Action: dumpBin,
Flags: cfgCountOutFlags,
},
{
Name: "restore",
Usage: "restore blocks from the file",
UsageText: "neo-go db restore -i file [--dump] [-n] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
Usage: "Restore blocks from the file",
UsageText: "neo-go db restore [-i file] [--dump] [-n] [-c count] [--config-path path] [-p/-m/-t] [--config-file file]",
Action: restoreDB,
Flags: cfgCountInFlags,
},
{
Name: "reset",
Usage: "reset database to the previous state",
Usage: "Reset database to the previous state",
UsageText: "neo-go db reset --height height [--config-path path] [-p/-m/-t] [--config-file file]",
Action: resetDB,
Flags: cfgHeightFlags,
@ -131,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.NewExitError(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)
@ -142,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.NewExitError(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.NewExitError(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 {
@ -158,11 +168,11 @@ func dumpDB(ctx *cli.Context) error {
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
@ -174,13 +184,13 @@ func dumpDB(ctx *cli.Context) error {
if out := ctx.String("out"); out != "" {
outStream, err = os.Create(out)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
}
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
}
@ -192,7 +202,7 @@ func dumpDB(ctx *cli.Context) error {
chainCount := chain.BlockHeight() + 1
if start+count > chainCount {
return cli.NewExitError(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", chainCount-1, count, start), 1)
return cli.Exit(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", chainCount-1, count, start), 1)
}
if count == 0 {
count = chainCount - start
@ -203,7 +213,7 @@ func dumpDB(ctx *cli.Context) error {
writer.WriteU32LE(count)
err = chaindump.Dump(chain, writer, start, count)
if err != nil {
return cli.NewExitError(err.Error(), 1)
return cli.Exit(err.Error(), 1)
}
return nil
}
@ -218,7 +228,7 @@ func restoreDB(ctx *cli.Context) error {
}
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
@ -229,7 +239,7 @@ func restoreDB(ctx *cli.Context) error {
if in := ctx.String("in"); in != "" {
inStream, err = os.Open(in)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
}
defer inStream.Close()
@ -240,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
}
@ -254,7 +264,7 @@ func restoreDB(ctx *cli.Context) error {
if ctx.Bool("incremental") {
start = reader.ReadU32LE()
if chain.BlockHeight()+1 < start {
return cli.NewExitError(fmt.Errorf("expected height: %d, dump starts at %d",
return cli.Exit(fmt.Errorf("expected height: %d, dump starts at %d",
chain.BlockHeight()+1, start), 1)
}
}
@ -266,10 +276,10 @@ func restoreDB(ctx *cli.Context) error {
var allBlocks = reader.ReadU32LE()
if reader.Err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if skip+count > allBlocks {
return cli.NewExitError(fmt.Errorf("input file has only %d blocks, can't read %d starting from %d", allBlocks, count, skip), 1)
return cli.Exit(fmt.Errorf("input file has only %d blocks, can't read %d starting from %d", allBlocks, count, skip), 1)
}
if count == 0 {
count = allBlocks - skip
@ -320,7 +330,7 @@ func restoreDB(ctx *cli.Context) error {
err = chaindump.Restore(chain, reader, skip, count, f)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(fmt.Errorf("wrong dump file or settings mismatch: %w", err), 1)
}
return nil
}
@ -331,29 +341,29 @@ func resetDB(ctx *cli.Context) error {
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
h := uint32(ctx.Uint("height"))
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
}
chain, store, err := initBlockChain(cfg, log)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create Blockchain instance: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to create Blockchain instance: %w", err), 1)
}
err = chain.Reset(h)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to reset chain state to height %d: %w", h, err), 1)
return cli.Exit(fmt.Errorf("failed to reset chain state to height %d: %w", h, err), 1)
}
err = store.Close()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to close the DB: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to close the DB: %w", err), 1)
}
return nil
}
@ -442,12 +452,12 @@ func startServer(ctx *cli.Context) error {
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
var logDebug = ctx.Bool("debug")
log, logLevel, logCloser, err := options.HandleLoggingParams(logDebug, cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
@ -458,12 +468,12 @@ func startServer(ctx *cli.Context) error {
serverConfig, err := network.NewServerConfig(cfg)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
chain, _, prometheus, pprof, err := InitBCWithMetrics(cfg, log)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer func() {
pprof.ShutDown()
@ -473,31 +483,31 @@ func startServer(ctx *cli.Context) error {
serv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), log)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create network server: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to create network server: %w", err), 1)
}
srMod := chain.GetStateModule().(*corestate.Module) // Take full responsibility here.
sr, err := stateroot.New(serverConfig.StateRootCfg, srMod, log, chain, serv.BroadcastExtensible)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't initialize StateRoot service: %w", err), 1)
return cli.Exit(fmt.Errorf("can't initialize StateRoot service: %w", err), 1)
}
serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload)
oracleSrv, err := mkOracle(cfg.ApplicationConfiguration.Oracle, cfg.ProtocolConfiguration.Magic, chain, serv, log)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
p2pNotary, err := mkP2PNotary(cfg.ApplicationConfiguration.P2PNotary, chain, serv, log)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
errChan := make(chan error)
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
serv.AddService(&rpcServer)
serv.AddService(rpcServer)
setNeoGoVersion(config.Version)
serv.Start()
if !cfg.ApplicationConfiguration.RPC.StartWhenSynchronized {
// Run RPC server in a separate routine. This is necessary to avoid a potential
@ -552,10 +562,10 @@ Main:
logLevel.SetLevel(newLogLevel)
log.Warn("using new logging level", zap.Stringer("level", newLogLevel))
}
serv.DelService(&rpcServer)
serv.DelService(rpcServer)
rpcServer.Shutdown()
rpcServer = rpcsrv.New(chain, cfgnew.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
serv.AddService(&rpcServer)
serv.AddService(rpcServer)
if !cfgnew.ApplicationConfiguration.RPC.StartWhenSynchronized || serv.IsInSync() {
// Here similar to the initial run (see above for-loop), so async.
go rpcServer.Start()
@ -640,7 +650,7 @@ Main:
}
if shutdownErr != nil {
return cli.NewExitError(shutdownErr, 1)
return cli.Exit(shutdownErr, 1)
}
return nil
@ -650,7 +660,7 @@ Main:
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, error) {
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
if err != nil {
return nil, nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1)
return nil, nil, cli.Exit(fmt.Errorf("could not initialize storage: %w", err), 1)
}
chain, err := core.NewBlockchain(store, cfg.Blockchain(), log)
@ -663,7 +673,7 @@ func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, stora
errArgs = append(errArgs, closeErr)
}
return nil, nil, cli.NewExitError(fmt.Errorf(errText, errArgs...), 1)
return nil, nil, cli.Exit(fmt.Errorf(errText, errArgs...), 1)
}
return chain, store, nil
}

View file

@ -15,7 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)
@ -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

@ -56,13 +56,13 @@ func TestCalcHash(t *testing.T) {
cmd := []string{"neo-go", "contract", "calc-hash"}
t.Run("no sender", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--in", nefPath, "--manifest", manifestPath)...)
e.RunWithErrorCheck(t, `Required flag "sender" not set`, append(cmd, "--in", nefPath, "--manifest", manifestPath)...)
})
t.Run("no nef file", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...)
e.RunWithErrorCheck(t, `Required flag "in" not set`, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...)
})
t.Run("no manifest file", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...)
e.RunWithErrorCheck(t, `Required flag "manifest" not set`, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...)
})
t.Run("invalid nef path", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(),
@ -289,7 +289,7 @@ func TestContractInitAndCompile(t *testing.T) {
e := testcli.NewExecutor(t, false)
t.Run("no path is provided", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "init")
e.RunWithErrorCheck(t, `Required flag "name" not set`, "neo-go", "contract", "init")
})
t.Run("invalid path", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "init", "--name", "\x00")
@ -313,7 +313,7 @@ func TestContractInitAndCompile(t *testing.T) {
manifestPath := filepath.Join(tmpDir, "testcontract.manifest.json")
cmd := []string{"neo-go", "contract", "compile"}
t.Run("missing source", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...)
})
cmd = append(cmd, "--in", srcPath, "--out", nefPath, "--manifest", manifestPath)
@ -487,10 +487,10 @@ func TestDeployWithSigners(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--in", "", "--manifest", manifestName)
"--in", nefName, "--manifest", manifestName)
})
t.Run("missing manifest", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
e.RunWithErrorCheck(t, "required flag --manifest is empty", "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--in", nefName, "--manifest", "")
@ -517,7 +517,7 @@ func TestDeployWithSigners(t *testing.T) {
"[", "str1", "str2", "]")
})
t.Run("missing RPC", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "deploy",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "deploy",
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr,
"--in", nefName, "--manifest", manifestName,
"[", "str1", "str2", "]")
@ -548,28 +548,29 @@ func TestContractManifestGroups(t *testing.T) {
"--out", nefName, "--manifest", manifestName)
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group")
e.RunWithErrorCheck(t, `Required flags "sender, address, nef, manifest" not set`, "neo-go", "contract", "manifest", "add-group")
})
t.Run("invalid wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", t.TempDir())
"--wallet", t.TempDir(), "--sender", testcli.TestWalletAccount, "--address", testcli.TestWalletAccount,
"--nef", nefName, "--manifest", manifestName)
})
t.Run("invalid sender", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
e.RunWithErrorCheck(t, `invalid value "not-a-sender" for flag -sender: invalid base58 digit ('-')`, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", "not-a-sender")
"--sender", "not-a-sender", "--nef", nefName, "--manifest", manifestName)
})
t.Run("invalid NEF file", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", tmpDir)
"--sender", testcli.TestWalletAccount, "--nef", tmpDir, "--manifest", manifestName)
})
t.Run("corrupted NEF file", func(t *testing.T) {
f := filepath.Join(tmpDir, "invalid.nef")
require.NoError(t, os.WriteFile(f, []byte{1, 2, 3}, os.ModePerm))
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
"--wallet", testcli.TestWalletPath, "--address", testcli.TestWalletAccount,
"--sender", testcli.TestWalletAccount, "--nef", f)
"--sender", testcli.TestWalletAccount, "--nef", f, "--manifest", manifestName)
})
t.Run("invalid manifest file", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "manifest", "add-group",
@ -630,9 +631,17 @@ func TestContract_TestInvokeScript(t *testing.T) {
"--out", goodNef, "--manifest", manifestName)
t.Run("missing in", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("empty in", func(t *testing.T) {
e.RunWithErrorCheck(t, "required flag --in is empty", "neo-go", "contract", "testinvokescript", "-i", "",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("empty rpc", func(t *testing.T) {
e.RunWithErrorCheck(t, "required flag --rpc-endpoint is empty", "neo-go", "contract", "testinvokescript", "-i", goodNef,
"--rpc-endpoint", "")
})
t.Run("unexisting in", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
@ -723,7 +732,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
t.Run("check calc hash", func(t *testing.T) {
// missing sender
e.RunWithError(t, "neo-go", "contract", "calc-hash",
e.RunWithErrorCheck(t, `Required flag "sender" not set`, "neo-go", "contract", "calc-hash",
"--in", nefName,
"--manifest", manifestName)
@ -755,7 +764,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.RunWithError(t, append(cmd, "--", "notahash")...)
})
t.Run("missing RPC address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "contract", "testinvokefunction",
h.StringLE(), "getValue")
})
@ -1038,7 +1047,7 @@ func TestContractInspect(t *testing.T) {
cmd := []string{"neo-go", "contract", "inspect"}
t.Run("missing input", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "in" not set`, cmd...)
})
t.Run("with raw '.go'", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--in", srcPath)...)

View file

@ -1,6 +1,7 @@
package smartcontract
import (
"bytes"
"fmt"
"os"
"strings"
@ -9,34 +10,40 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)
var generatorFlags = []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "Configuration file to use",
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: `Configuration bindings file to use (*.yml). Configuration file is
generated by 'contract compile' command with --bindings flag`,
},
cli.StringFlag{
Name: "manifest, m",
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Required: true,
Usage: "Read contract manifest (*.manifest.json) file",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
cli.StringFlag{
Name: "out, o",
&cli.StringFlag{
Name: "out",
Aliases: []string{"o"},
Required: true,
Usage: "Output of the compiled wrapper",
Action: cmdargs.EnsureNotEmpty("out"),
},
cli.StringFlag{
&cli.StringFlag{
Name: "hash",
Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage",
},
}
var generateWrapperCmd = cli.Command{
var generateWrapperCmd = &cli.Command{
Name: "generate-wrapper",
Usage: "generate wrapper to use in other contracts",
Usage: "Generate wrapper to use in other contracts",
UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
Description: `Generates a Go wrapper to use it in other smart contracts. If the
--hash flag is provided, CALLT instruction is used for the target contract
@ -48,9 +55,9 @@ var generateWrapperCmd = cli.Command{
Flags: generatorFlags,
}
var generateRPCWrapperCmd = cli.Command{
var generateRPCWrapperCmd = &cli.Command{
Name: "generate-rpcwrapper",
Usage: "generate RPC wrapper to use for data reads",
Usage: "Generate RPC wrapper to use for data reads",
UsageText: "neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
Action: contractGenerateRPCWrapper,
Flags: generatorFlags,
@ -76,23 +83,26 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
if hStr := ctx.String("hash"); len(hStr) != 0 {
h, err = util.Uint160DecodeStringLE(strings.TrimPrefix(hStr, "0x"))
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid contract hash: %w", err), 1)
}
}
m, _, err := readManifest(ctx.String("manifest"), h)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1)
return cli.Exit(fmt.Errorf("can't read contract manifest: %w", err), 1)
}
cfg := binding.NewConfig()
if cfgPath := ctx.String("config"); cfgPath != "" {
bs, err := os.ReadFile(cfgPath)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't read config file: %w", err), 1)
return cli.Exit(fmt.Errorf("can't read config file: %w", err), 1)
}
err = yaml.Unmarshal(bs, &cfg)
decoder := yaml.NewDecoder(bytes.NewReader(bs))
decoder.KnownFields(true)
err = decoder.Decode(&cfg)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't parse config file: %w", err), 1)
return cli.Exit(fmt.Errorf("can't parse config file: %w", err), 1)
}
}
cfg.Manifest = m
@ -100,7 +110,7 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
f, err := os.Create(ctx.String("out"))
if err != nil {
return cli.NewExitError(fmt.Errorf("can't create output file: %w", err), 1)
return cli.Exit(fmt.Errorf("can't create output file: %w", err), 1)
}
defer f.Close()
@ -108,7 +118,7 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error)
err = cb(cfg)
if err != nil {
return cli.NewExitError(fmt.Errorf("error during generation: %w", err), 1)
return cli.Exit(fmt.Errorf("error during generation: %w", err), 1)
}
return nil
}

View file

@ -1,4 +1,4 @@
package smartcontract
package smartcontract_test
import (
"bytes"
@ -6,14 +6,13 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestGenerate(t *testing.T) {
@ -124,8 +123,7 @@ func TestGenerate(t *testing.T) {
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd}
e := testcli.NewExecutor(t, false)
rawCfg := `package: wrapper
hash: ` + h.StringLE() + `
@ -144,12 +142,12 @@ callflags:
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
require.NoError(t, app.Run([]string{"", "generate-wrapper",
e.Run(t, []string{"", "contract", "generate-wrapper",
"--manifest", manifestFile,
"--config", cfgPath,
"--out", outFile,
"--hash", h.StringLE(),
}))
}...)
const expected = `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
@ -234,11 +232,11 @@ func MyFunc(in map[int]mycontract.Input) []mycontract.Output {
require.NoError(t, err)
require.Equal(t, expected, string(data))
require.NoError(t, app.Run([]string{"", "generate-wrapper",
e.Run(t, []string{"", "contract", "generate-wrapper",
"--manifest", manifestFile,
"--config", cfgPath,
"--out", outFile,
}))
}...)
expectedWithDynamicHash := `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package wrapper contains wrappers for MyContract contract.
@ -350,13 +348,12 @@ func TestGenerateValidPackageName(t *testing.T) {
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
require.NoError(t, app.Run([]string{"", "generate-wrapper",
e := testcli.NewExecutor(t, false)
e.Run(t, []string{"", "contract", "generate-wrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
}...)
data, err := os.ReadFile(outFile)
require.NoError(t, err)
@ -378,11 +375,11 @@ func Get() int {
return neogointernal.CallWithToken(Hash, "get", int(contract.ReadOnly)).(int)
}
`, string(data))
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
e.Run(t, []string{"", "contract", "generate-rpcwrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
}...)
data, err = os.ReadFile(outFile)
require.NoError(t, err)
@ -431,17 +428,16 @@ const rewriteExpectedOutputs = false
func TestGenerateRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
e := testcli.NewExecutor(t, false)
var checkBinding = func(manifest string, hash string, good string) {
t.Run(manifest, func(t *testing.T) {
outFile := filepath.Join(tmpDir, "out.go")
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
e.Run(t, []string{"", "contract", "generate-rpcwrapper",
"--manifest", manifest,
"--out", outFile,
"--hash", hash,
}))
}...)
data, err := os.ReadFile(outFile)
require.NoError(t, err)
@ -478,10 +474,9 @@ func TestGenerateRPCBindings(t *testing.T) {
func TestAssistedRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = NewCommands()
e := testcli.NewExecutor(t, false)
var checkBinding = func(source string, hasDefinedHash bool, guessEventTypes bool, suffix ...string) {
var checkBinding = func(source, configFile, expectedFile string, hasDefinedHash, guessEventTypes bool, suffix ...string) {
testName := source
if len(suffix) != 0 {
testName += suffix[0]
@ -489,13 +484,20 @@ func TestAssistedRPCBindings(t *testing.T) {
testName += fmt.Sprintf(", predefined hash: %t", hasDefinedHash)
t.Run(testName, func(t *testing.T) {
outFile := filepath.Join(tmpDir, "out.go")
configFile := filepath.Join(source, "config.yml")
expectedFile := filepath.Join(source, "rpcbindings.out")
if len(suffix) != 0 {
configFile = filepath.Join(source, "config"+suffix[0]+".yml")
expectedFile = filepath.Join(source, "rpcbindings"+suffix[0]+".out")
} else if !hasDefinedHash {
expectedFile = filepath.Join(source, "rpcbindings_dynamic_hash.out")
if configFile == "" {
if len(suffix) != 0 {
configFile = filepath.Join(source, "config"+suffix[0]+".yml")
} else {
configFile = filepath.Join(source, "config.yml")
}
}
if expectedFile == "" {
expectedFile = filepath.Join(source, "rpcbindings.out")
if len(suffix) != 0 {
expectedFile = filepath.Join(source, "rpcbindings"+suffix[0]+".out")
} else if !hasDefinedHash {
expectedFile = filepath.Join(source, "rpcbindings_dynamic_hash.out")
}
}
manifestF := filepath.Join(tmpDir, "manifest.json")
bindingF := filepath.Join(tmpDir, "binding.yml")
@ -510,7 +512,7 @@ func TestAssistedRPCBindings(t *testing.T) {
if guessEventTypes {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(cmd))
e.Run(t, cmd...)
cmds := []string{"", "contract", "generate-rpcwrapper",
"--config", bindingF,
@ -520,7 +522,7 @@ func TestAssistedRPCBindings(t *testing.T) {
if hasDefinedHash {
cmds = append(cmds, "--hash", "0x00112233445566778899aabbccddeeff00112233")
}
require.NoError(t, app.Run(cmds))
e.Run(t, cmds...)
data, err := os.ReadFile(outFile)
require.NoError(t, err)
@ -537,39 +539,39 @@ func TestAssistedRPCBindings(t *testing.T) {
}
for _, hasDefinedHash := range []bool{true, false} {
checkBinding(filepath.Join("testdata", "rpcbindings", "types"), hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "structs"), hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "types"), "", "", hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "structs"), "", "", hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "royalty"), "", "", hasDefinedHash, false)
}
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false, "_extended")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, true, "_guessed")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, false, "_extended")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, true, "_guessed")
checkBinding(filepath.Join("..", "..", "examples", "nft-d"), filepath.Join("..", "..", "examples", "nft-d", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-d", "rpcbindings_dynamic_hash.out"), false, false)
checkBinding(filepath.Join("..", "..", "examples", "nft-d"), filepath.Join("..", "..", "examples", "nft-d", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-d", "rpcbindings.out"), true, true)
checkBinding(filepath.Join("..", "..", "examples", "nft-nd"), filepath.Join("..", "..", "examples", "nft-nd", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-nd", "rpcbindings_dynamic_hash.out"), false, false)
checkBinding(filepath.Join("..", "..", "examples", "nft-nd"), filepath.Join("..", "..", "examples", "nft-nd", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-nd", "rpcbindings.out"), true, true)
require.False(t, rewriteExpectedOutputs)
}
func TestGenerate_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd}
app.ExitErrHandler = func(*cli.Context, error) {}
e := testcli.NewExecutor(t, false)
args := []string{"neo-go", "contract", "generate-wrapper"}
checkError := func(t *testing.T, msg string, args ...string) {
// cli.ExitError doesn't implement wraping properly, so we check for an error message.
err := app.Run(append([]string{"", "generate-wrapper"}, args...))
require.True(t, strings.Contains(err.Error(), msg), "got: %v", err)
}
t.Run("invalid hash", func(t *testing.T) {
checkError(t, "invalid contract hash", "--hash", "xxx", "--manifest", "yyy", "--out", "zzz")
e.RunWithErrorCheckExit(t, "invalid contract hash", append(args, "--hash", "xxx", "--manifest", "yyy", "--out", "zzz")...)
})
t.Run("missing manifest argument", func(t *testing.T) {
checkError(t, "Required flag \"manifest\" not set", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheck(t, `Required flag "manifest" not set`, append(args, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
t.Run("missing manifest file", func(t *testing.T) {
checkError(t, "can't read contract manifest", "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheckExit(t, "can't read contract manifest", append(args, "--manifest", "notexists", "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
t.Run("empty manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
require.NoError(t, os.WriteFile(manifestFile, []byte("[]"), os.ModePerm))
checkError(t, "json: cannot unmarshal array into Go value of type manifest.Manifest", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheckExit(t, "json: cannot unmarshal array into Go value of type manifest.Manifest", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
t.Run("invalid manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
@ -577,7 +579,7 @@ func TestGenerate_Errors(t *testing.T) {
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
checkError(t, "ABI: no methods", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
e.RunWithErrorCheckExit(t, "ABI: no methods", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")...)
})
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
@ -593,9 +595,8 @@ func TestGenerate_Errors(t *testing.T) {
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
t.Run("missing config", func(t *testing.T) {
checkError(t, "can't read config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", filepath.Join(t.TempDir(), "not.exists.yml"), "--out", "zzz")
e.RunWithErrorCheckExit(t, "can't read config file", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", filepath.Join(t.TempDir(), "not.exists.yml"), "--out", "zzz")...)
})
t.Run("invalid config", func(t *testing.T) {
rawCfg := `package: wrapper
@ -605,23 +606,13 @@ callflags:
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
checkError(t, "can't parse config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", cfgPath, "--out", "zzz")
e.RunWithErrorCheckExit(t, "can't parse config file", append(args, "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", cfgPath, "--out", "zzz")...)
})
}
func TestCompile_GuessEventTypes(t *testing.T) {
app := cli.NewApp()
app.Commands = NewCommands()
app.ExitErrHandler = func(*cli.Context, error) {}
checkError := func(t *testing.T, msg string, args ...string) {
// cli.ExitError doesn't implement wraping properly, so we check for an error message.
err := app.Run(args)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), msg), "got: %v", err)
}
e := testcli.NewExecutor(t, false)
check := func(t *testing.T, source string, expectedErrText string) {
tmpDir := t.TempDir()
configFile := filepath.Join(source, "invalid.yml")
@ -636,7 +627,7 @@ func TestCompile_GuessEventTypes(t *testing.T) {
"--out", nefF,
"--guess-eventtypes",
}
checkError(t, expectedErrText, cmd...)
e.RunWithErrorCheckExit(t, expectedErrText, cmd...)
}
t.Run("not declared in manifest", func(t *testing.T) {
@ -664,10 +655,7 @@ func TestCompile_GuessEventTypes(t *testing.T) {
}
func TestGenerateRPCBindings_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = NewCommands()
app.ExitErrHandler = func(*cli.Context, error) {}
e := testcli.NewExecutor(t, false)
t.Run("duplicating resulting fields", func(t *testing.T) {
check := func(t *testing.T, packageName string, autogen bool, expectedError string) {
tmpDir := t.TempDir()
@ -687,16 +675,14 @@ func TestGenerateRPCBindings_Errors(t *testing.T) {
if autogen {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(cmd))
e.Run(t, cmd...)
cmds := []string{"", "contract", "generate-rpcwrapper",
"--config", bindingF,
"--manifest", manifestF,
"--out", out,
}
err := app.Run(cmds)
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), expectedError), err.Error())
e.RunWithErrorCheckExit(t, expectedError, cmds...)
}
t.Run("event", func(t *testing.T) {

View file

@ -2,7 +2,6 @@ package smartcontract
import (
"encoding/json"
"errors"
"fmt"
"os"
@ -13,34 +12,30 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func manifestAddGroup(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
sender, err := flags.ParseAddress(ctx.String("sender"))
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid sender: %w", err), 1)
}
sender := ctx.Generic("sender").(*flags.Address)
nf, _, err := readNEFFile(ctx.String("nef"))
if err != nil {
return cli.NewExitError(fmt.Errorf("can't read NEF file: %w", err), 1)
return cli.Exit(fmt.Errorf("can't read NEF file: %w", err), 1)
}
mPath := ctx.String("manifest")
m, _, err := readManifest(mPath, util.Uint160{})
if err != nil {
return cli.NewExitError(fmt.Errorf("can't read contract manifest: %w", err), 1)
return cli.Exit(fmt.Errorf("can't read contract manifest: %w", err), 1)
}
h := state.CreateContractHash(sender, nf.Checksum, m.Name)
h := state.CreateContractHash(sender.Uint160(), nf.Checksum, m.Name)
gAcc, w, err := options.GetAccFromContext(ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't get account to sign group with: %w", err), 1)
return cli.Exit(fmt.Errorf("can't get account to sign group with: %w", err), 1)
}
defer w.Close()
@ -64,21 +59,17 @@ func manifestAddGroup(ctx *cli.Context) error {
rawM, err := json.Marshal(m)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't marshal manifest: %w", err), 1)
return cli.Exit(fmt.Errorf("can't marshal manifest: %w", err), 1)
}
err = os.WriteFile(mPath, rawM, os.ModePerm)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't write manifest file: %w", err), 1)
return cli.Exit(fmt.Errorf("can't write manifest file: %w", err), 1)
}
return nil
}
func readNEFFile(filename string) (*nef.File, []byte, error) {
if len(filename) == 0 {
return nil, nil, errors.New("no nef file was provided")
}
f, err := os.ReadFile(filename)
if err != nil {
return nil, nil, err
@ -96,10 +87,6 @@ func readNEFFile(filename string) (*nef.File, []byte, error) {
// it for validness against the provided contract hash. If empty hash is specified
// then no hash-related manifest groups check is performed.
func readManifest(filename string, hash util.Uint160) (*manifest.Manifest, []byte, error) {
if len(filename) == 0 {
return nil, nil, errNoManifestFile
}
manifestBytes, err := os.ReadFile(filename)
if err != nil {
return nil, nil, err

View file

@ -27,25 +27,27 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)
// addressFlagName is a flag name used for address-related operations. It should be
// the same within the smartcontract package, thus, use this constant.
const addressFlagName = "address, a"
// addressFlagName and addressFlagAlias are a flag name and its alias
// used for address-related operations. It should be the same within
// the smartcontract package, thus, use this constant.
const (
addressFlagName = "address"
addressFlagAlias = "a"
)
var (
errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag")
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
errNoManifestFile = errors.New("no manifest file was found, specify manifest file with '--manifest' or '-m' flag")
errNoMethod = errors.New("no method specified for function invocation command")
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
errNoSmartContractName = errors.New("no name was provided, specify the '--name or -n' flag")
errFileExist = errors.New("A file with given smart-contract name already exists")
addressFlag = flags.AddressFlag{
Name: addressFlagName,
Usage: "address to use as transaction signee (and gas source)",
errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag")
errNoMethod = errors.New("no method specified for function invocation command")
errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument")
errFileExist = errors.New("A file with given smart-contract name already exists")
addressFlag = &flags.AddressFlag{
Name: addressFlagName,
Aliases: []string{addressFlagAlias},
Usage: "Address to use as transaction signee (and gas source)",
}
)
@ -74,11 +76,14 @@ func RuntimeNotify(args []any) {
)
// NewCommands returns 'contract' command.
func NewCommands() []cli.Command {
func NewCommands() []*cli.Command {
testInvokeScriptFlags := []cli.Flag{
cli.StringFlag{
Name: "in, i",
Usage: "Input location of the .nef file that needs to be invoked",
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input location of the .nef file that needs to be invoked",
Action: cmdargs.EnsureNotEmpty("in"),
},
options.Historic,
}
@ -96,40 +101,56 @@ func NewCommands() []cli.Command {
invokeFunctionFlags = append(invokeFunctionFlags, options.Wallet...)
invokeFunctionFlags = append(invokeFunctionFlags, options.RPC...)
deployFlags := append(invokeFunctionFlags, []cli.Flag{
cli.StringFlag{
Name: "in, i",
Usage: "Input file for the smart contract (*.nef)",
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input file for the smart contract (*.nef)",
Action: cmdargs.EnsureNotEmpty("in"),
},
cli.StringFlag{
Name: "manifest, m",
Usage: "Manifest input file (*.manifest.json)",
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Required: true,
Usage: "Manifest input file (*.manifest.json)",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
}...)
manifestAddGroupFlags := append([]cli.Flag{
cli.StringFlag{
Name: "sender, s",
Usage: "deploy transaction sender",
&flags.AddressFlag{
Name: "sender",
Aliases: []string{"s"},
Required: true,
Usage: "Deploy transaction sender",
},
flags.AddressFlag{
Name: addressFlagName, // use the same name for handler code unification.
Usage: "account to sign group with",
&flags.AddressFlag{
Name: addressFlagName, // use the same name for handler code unification.
Aliases: []string{addressFlagAlias},
Required: true,
Usage: "Account to sign group with",
},
cli.StringFlag{
Name: "nef, n",
Usage: "path to the NEF file",
&cli.StringFlag{
Name: "nef",
Aliases: []string{"n"},
Required: true,
Usage: "Path to the NEF file",
Action: cmdargs.EnsureNotEmpty("nef"),
},
cli.StringFlag{
Name: "manifest, m",
Usage: "path to the manifest",
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Required: true,
Usage: "Path to the manifest",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
}, options.Wallet...)
return []cli.Command{{
return []*cli.Command{{
Name: "contract",
Usage: "compile - debug - deploy smart contracts",
Subcommands: []cli.Command{
Usage: "Compile - debug - deploy smart contracts",
Subcommands: []*cli.Command{
{
Name: "compile",
Usage: "compile a smart contract to a .nef file",
Usage: "Compile a smart contract to a .nef file",
UsageText: "neo-go contract compile -i path [-o nef] [-v] [-d] [-m manifest] [-c yaml] [--bindings file] [--no-standards] [--no-events] [--no-permissions] [--guess-eventtypes]",
Description: `Compiles given smart contract to a .nef file and emits other associated
information (manifest, bindings configuration, debug information files) if
@ -141,55 +162,63 @@ func NewCommands() []cli.Command {
`,
Action: contractCompile,
Flags: []cli.Flag{
cli.StringFlag{
Name: "in, i",
Usage: "Input file for the smart contract to be compiled (*.go file or directory)",
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input file for the smart contract to be compiled (*.go file or directory)",
Action: cmdargs.EnsureNotEmpty("in"),
},
cli.StringFlag{
Name: "out, o",
Usage: "Output of the compiled contract",
&cli.StringFlag{
Name: "out",
Aliases: []string{"o"},
Usage: "Output of the compiled contract",
},
cli.BoolFlag{
Name: "verbose, v",
Usage: "Print out additional information after a compiling",
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
Usage: "Print out additional information after a compiling",
},
cli.StringFlag{
Name: "debug, d",
Usage: "Emit debug info in a separate file",
&cli.StringFlag{
Name: "debug",
Aliases: []string{"d"},
Usage: "Emit debug info in a separate file",
},
cli.StringFlag{
Name: "manifest, m",
Usage: "Emit contract manifest (*.manifest.json) file into separate file using configuration input file (*.yml)",
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Usage: "Emit contract manifest (*.manifest.json) file into separate file using configuration input file (*.yml)",
},
cli.StringFlag{
Name: "config, c",
Usage: "Configuration input file (*.yml)",
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Configuration input file (*.yml)",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-standards",
Usage: "do not check compliance with supported standards",
Usage: "Do not check compliance with supported standards",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-events",
Usage: "do not check emitted events with the manifest",
Usage: "Do not check emitted events with the manifest",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "no-permissions",
Usage: "do not check if invoked contracts are allowed in manifest",
Usage: "Do not check if invoked contracts are allowed in manifest",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "guess-eventtypes",
Usage: "guess event types for smart-contract bindings configuration from the code usages",
Usage: "Guess event types for smart-contract bindings configuration from the code usages",
},
cli.StringFlag{
&cli.StringFlag{
Name: "bindings",
Usage: "output file for smart-contract bindings configuration",
Usage: "Output file for smart-contract bindings configuration",
},
},
},
{
Name: "deploy",
Usage: "deploy a smart contract (.nef with description)",
Usage: "Deploy a smart contract (.nef with description)",
UsageText: "neo-go contract deploy -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] --in contract.nef --manifest contract.manifest.json [--out file] [--force] [--await] [data]",
Description: `Deploys given contract into the chain. The gas parameter is for additional
gas to be added as a network fee to prioritize the transaction. The data
@ -204,7 +233,7 @@ func NewCommands() []cli.Command {
generateRPCWrapperCmd,
{
Name: "invokefunction",
Usage: "invoke deployed contract on the blockchain",
Usage: "Invoke deployed contract on the blockchain",
UsageText: "neo-go contract invokefunction -r endpoint -w wallet [-a address] [-g gas] [-e sysgas] [--out file] [--force] [--await] scripthash [method] [arguments...] [--] [signers...]",
Description: `Executes given (as a script hash) deployed script with the given method,
arguments and signers. Sender is included in the list of signers by default
@ -219,7 +248,7 @@ func NewCommands() []cli.Command {
},
{
Name: "testinvokefunction",
Usage: "invoke deployed contract on the blockchain (test mode)",
Usage: "Invoke deployed contract on the blockchain (test mode)",
UsageText: "neo-go contract testinvokefunction -r endpoint [--historic index/hash] scripthash [method] [arguments...] [--] [signers...]",
Description: `Executes given (as a script hash) deployed script with the given method,
arguments and signers (sender is not included by default). If no method is given
@ -250,63 +279,78 @@ func NewCommands() []cli.Command {
},
{
Name: "init",
Usage: "initialize a new smart-contract in a directory with boiler plate code",
Usage: "Initialize a new smart-contract in a directory with boiler plate code",
UsageText: "neo-go contract init -n name [--skip-details]",
Action: initSmartContract,
Flags: []cli.Flag{
cli.StringFlag{
Name: "name, n",
Usage: "name of the smart-contract to be initialized",
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Required: true,
Usage: "Name of the smart-contract to be initialized",
Action: cmdargs.EnsureNotEmpty("name"),
},
cli.BoolFlag{
Name: "skip-details, skip",
Usage: "skip filling in the projects and contract details",
&cli.BoolFlag{
Name: "skip-details",
Aliases: []string{"skip"},
Usage: "Skip filling in the projects and contract details",
},
},
},
{
Name: "inspect",
Usage: "creates a user readable dump of the program instructions",
Usage: "Creates a user readable dump of the program instructions",
UsageText: "neo-go contract inspect -i file [-c]",
Action: inspect,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "compile, c",
Usage: "compile input file (it should be go code then)",
&cli.BoolFlag{
Name: "compile",
Aliases: []string{"c"},
Usage: "Compile input file (it should be go code then)",
},
cli.StringFlag{
Name: "in, i",
Usage: "input file of the program (either .go or .nef)",
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Required: true,
Usage: "Input file of the program (either .go or .nef)",
Action: cmdargs.EnsureNotEmpty("in"),
},
},
},
{
Name: "calc-hash",
Usage: "calculates hash of a contract after deployment",
Usage: "Calculates hash of a contract after deployment",
UsageText: "neo-go contract calc-hash -i nef -m manifest -s address",
Action: calcHash,
Flags: []cli.Flag{
flags.AddressFlag{
Name: "sender, s",
Usage: "sender script hash or address",
&flags.AddressFlag{
Name: "sender",
Aliases: []string{"s"},
Required: true,
Usage: "Sender script hash or address",
},
cli.StringFlag{
Name: "in",
Usage: "path to NEF file",
&cli.StringFlag{
Name: "in",
Required: true,
Usage: "Path to NEF file",
Action: cmdargs.EnsureNotEmpty("in"),
},
cli.StringFlag{
Name: "manifest, m",
Usage: "path to manifest file",
&cli.StringFlag{
Name: "manifest",
Aliases: []string{"m"},
Required: true,
Usage: "Path to manifest file",
Action: cmdargs.EnsureNotEmpty("manifest"),
},
},
},
{
Name: "manifest",
Usage: "manifest-related commands",
Subcommands: []cli.Command{
Usage: "Manifest-related commands",
Subcommands: []*cli.Command{
{
Name: "add-group",
Usage: "adds group to the manifest",
Usage: "Adds group to the manifest",
UsageText: "neo-go contract manifest add-group -w wallet [--wallet-config path] -n nef -m manifest -a address -s address",
Action: manifestAddGroup,
Flags: manifestAddGroupFlags,
@ -323,13 +367,10 @@ func initSmartContract(ctx *cli.Context) error {
return err
}
contractName := ctx.String("name")
if contractName == "" {
return cli.NewExitError(errNoSmartContractName, 1)
}
// Check if the file already exists, if yes, exit
if _, err := os.Stat(contractName); err == nil {
return cli.NewExitError(errFileExist, 1)
return cli.Exit(errFileExist, 1)
}
basePath := contractName
@ -338,7 +379,7 @@ func initSmartContract(ctx *cli.Context) error {
// create base directory
if err := os.Mkdir(basePath, os.ModePerm); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
m := ProjectConfig{
@ -363,10 +404,10 @@ func initSmartContract(ctx *cli.Context) error {
}
b, err := yaml.Marshal(m)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if err := os.WriteFile(filepath.Join(basePath, "neo-go.yml"), b, 0644); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
ver := ModVersion
@ -376,18 +417,18 @@ func initSmartContract(ctx *cli.Context) error {
gm := []byte("module " + contractName + `
go 1.20
go 1.22
require (
github.com/nspcc-dev/neo-go/pkg/interop ` + ver + `
)`)
if err := os.WriteFile(filepath.Join(basePath, "go.mod"), gm, 0644); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
data := []byte(fmt.Sprintf(smartContractTmpl, contractName))
if err := os.WriteFile(filepath.Join(basePath, fileName), data, 0644); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
fmt.Fprintf(ctx.App.Writer, "Successfully initialized smart contract [%s]\n", contractName)
@ -400,16 +441,13 @@ func contractCompile(ctx *cli.Context) error {
return err
}
src := ctx.String("in")
if len(src) == 0 {
return cli.NewExitError(errNoInput, 1)
}
manifestFile := ctx.String("manifest")
confFile := ctx.String("config")
debugFile := ctx.String("debug")
out := ctx.String("out")
bindings := ctx.String("bindings")
if len(confFile) == 0 && (len(manifestFile) != 0 || len(debugFile) != 0 || len(bindings) != 0) {
return cli.NewExitError(errNoConfFile, 1)
return cli.Exit(errNoConfFile, 1)
}
autocomplete := len(manifestFile) == 0 &&
len(confFile) == 0 &&
@ -419,7 +457,7 @@ func contractCompile(ctx *cli.Context) error {
var root string
fileInfo, err := os.Stat(src)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to stat source file or directory: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to stat source file or directory: %w", err), 1)
}
if fileInfo.IsDir() {
base := filepath.Base(fileInfo.Name())
@ -470,7 +508,7 @@ func contractCompile(ctx *cli.Context) error {
result, err := compiler.CompileAndSave(src, o)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if ctx.Bool("verbose") {
fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(result))
@ -484,34 +522,25 @@ func calcHash(ctx *cli.Context) error {
return err
}
sender := ctx.Generic("sender").(*flags.Address)
if !sender.IsSet {
return cli.NewExitError("sender is not set", 1)
}
p := ctx.String("in")
if p == "" {
return cli.NewExitError(errors.New("no .nef file was provided"), 1)
}
mpath := ctx.String("manifest")
if mpath == "" {
return cli.NewExitError(errors.New("no manifest file provided"), 1)
}
f, err := os.ReadFile(p)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't read .nef file: %w", err), 1)
return cli.Exit(fmt.Errorf("can't read .nef file: %w", err), 1)
}
nefFile, err := nef.FileFromBytes(f)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't unmarshal .nef file: %w", err), 1)
return cli.Exit(fmt.Errorf("can't unmarshal .nef file: %w", err), 1)
}
manifestBytes, err := os.ReadFile(mpath)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to read manifest file: %w", err), 1)
}
m := &manifest.Manifest{}
err = json.Unmarshal(manifestBytes, m)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to restore manifest file: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to restore manifest file: %w", err), 1)
}
fmt.Fprintln(ctx.App.Writer, "Contract hash:", state.CreateContractHash(sender.Uint160(), nefFile.Checksum, m.Name).StringLE())
return nil
@ -528,7 +557,7 @@ func invokeFunction(ctx *cli.Context) error {
func invokeInternal(ctx *cli.Context, signAndPush bool) error {
var (
err error
exitErr *cli.ExitError
exitErr cli.ExitCoder
operation string
params []any
paramsStart = 1
@ -539,22 +568,23 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
args := ctx.Args()
if !args.Present() {
return cli.NewExitError(errNoScriptHash, 1)
return cli.Exit(errNoScriptHash, 1)
}
script, err := flags.ParseAddress(args[0])
argsSlice := args.Slice()
script, err := flags.ParseAddress(argsSlice[0])
if err != nil {
return cli.NewExitError(fmt.Errorf("incorrect script hash: %w", err), 1)
return cli.Exit(fmt.Errorf("incorrect script hash: %w", err), 1)
}
if len(args) <= 1 {
return cli.NewExitError(errNoMethod, 1)
if len(argsSlice) <= 1 {
return cli.Exit(errNoMethod, 1)
}
operation = args[1]
operation = argsSlice[1]
paramsStart++
if len(args) > paramsStart {
cosignersOffset, scParams, err = cmdargs.ParseParams(args[paramsStart:], true)
if len(argsSlice) > paramsStart {
cosignersOffset, scParams, err = cmdargs.ParseParams(argsSlice[paramsStart:], true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
params = make([]any, len(scParams))
for i := range scParams {
@ -575,7 +605,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
if signAndPush {
acc, w, err = options.GetAccFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer w.Close()
}
@ -595,7 +625,7 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
if signAndPush {
signersAccounts, err = cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.None)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
}
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -615,12 +645,12 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
out := ctx.String("out")
resp, err = inv.Call(script, operation, params...)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if resp.State != "HALT" {
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s", resp.State, resp.FaultException)
if !signAndPush {
return cli.NewExitError(errText, 1)
return cli.Exit(errText, 1)
}
action := "send"
@ -630,42 +660,38 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
process = "Saving"
}
if !ctx.Bool("force") {
return cli.NewExitError(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1)
return cli.Exit(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1)
}
fmt.Fprintln(ctx.App.Writer, errText+".\n"+process+" transaction...")
}
if !signAndPush {
b, err := json.MarshalIndent(resp, "", " ")
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
fmt.Fprintln(ctx.App.Writer, string(b))
return nil
}
if len(resp.Script) == 0 {
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
return cli.Exit(errors.New("no script returned from the RPC node"), 1)
}
tx, err := act.MakeUnsignedUncheckedRun(resp.Script, resp.GasConsumed, nil)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to create tx: %w", err), 1)
}
return txctx.SignAndSend(ctx, act, acc, tx)
}
func testInvokeScript(ctx *cli.Context) error {
src := ctx.String("in")
if len(src) == 0 {
return cli.NewExitError(errNoInput, 1)
}
b, err := os.ReadFile(src)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
nefFile, err := nef.FileFromBytes(b)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to restore .nef file: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to restore .nef file: %w", err), 1)
}
signers, exitErr := cmdargs.GetSignersFromContext(ctx, 0)
@ -683,12 +709,12 @@ func testInvokeScript(ctx *cli.Context) error {
resp, err := inv.Run(nefFile.Script)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
b, err = json.MarshalIndent(resp, "", " ")
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
fmt.Fprintln(ctx.App.Writer, string(b))
@ -714,9 +740,6 @@ func inspect(ctx *cli.Context) error {
}
in := ctx.String("in")
compile := ctx.Bool("compile")
if len(in) == 0 {
return cli.NewExitError(errNoInput, 1)
}
var (
b []byte
err error
@ -724,16 +747,16 @@ func inspect(ctx *cli.Context) error {
if compile {
b, err = compiler.Compile(in, nil)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to compile: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to compile: %w", err), 1)
}
} else {
f, err := os.ReadFile(in)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read .nef file: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to read .nef file: %w", err), 1)
}
nefFile, err := nef.FileFromBytes(f)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to restore .nef file: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to restore .nef file: %w", err), 1)
}
b = nefFile.Script
}
@ -748,22 +771,22 @@ func inspect(ctx *cli.Context) error {
func contractDeploy(ctx *cli.Context) error {
nefFile, f, err := readNEFFile(ctx.String("in"))
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
m, manifestBytes, err := readManifest(ctx.String("manifest"), util.Uint160{})
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to read manifest file: %w", err), 1)
}
var appCallParams = []any{f, manifestBytes}
signOffset, data, err := cmdargs.ParseParams(ctx.Args(), true)
signOffset, data, err := cmdargs.ParseParams(ctx.Args().Slice(), true)
if err != nil {
return cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
return cli.Exit(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
}
if len(data) > 1 {
return cli.NewExitError("'data' should be represented as a single parameter", 1)
return cli.Exit("'data' should be represented as a single parameter", 1)
}
if len(data) != 0 {
appCallParams = append(appCallParams, data[0])
@ -771,7 +794,7 @@ func contractDeploy(ctx *cli.Context) error {
acc, w, err := options.GetAccFromContext(ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1)
return cli.Exit(fmt.Errorf("can't get sender address: %w", err), 1)
}
defer w.Close()
sender := acc.ScriptHash()
@ -801,12 +824,12 @@ func ParseContractConfig(confFile string) (ProjectConfig, error) {
conf := ProjectConfig{}
confBytes, err := os.ReadFile(confFile)
if err != nil {
return conf, cli.NewExitError(err, 1)
return conf, cli.Exit(err, 1)
}
err = yaml.Unmarshal(confBytes, &conf)
if err != nil {
return conf, cli.NewExitError(fmt.Errorf("bad config: %w", err), 1)
return conf, cli.Exit(fmt.Errorf("bad config: %w", err), 1)
}
return conf, nil
}

View file

@ -9,7 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)

View file

@ -0,0 +1,146 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for NeoFS Object NFT contract.
package nft
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.DivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.DivisibleWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep11.NewDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep11dt = nep11.NewDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11dt.DivisibleReader, *nep24t, actor, hash}, nep11dt.DivisibleWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}

View file

@ -0,0 +1,141 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for NeoFS Object NFT contract.
package nft
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.DivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.DivisibleWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{*nep11.NewDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
var nep11dt = nep11.NewDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11dt.DivisibleReader, *nep24t, actor, hash}, nep11dt.DivisibleWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}

View file

@ -0,0 +1,311 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for HASHY NFT contract.
package nft
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// NftRoyaltyRecipientShare is a contract-specific nft.RoyaltyRecipientShare type used by its methods.
type NftRoyaltyRecipientShare struct {
Address util.Uint160
Share *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.NonDivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.BaseWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep11ndt = nep11.NewNonDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, *nep24t, actor, hash}, nep11ndt.BaseWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
func (c *Contract) scriptForSetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "setRoyaltyInfo", ctx, tokenID, recipients)
}
// SetRoyaltyInfo creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (util.Uint256, uint32, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// SetRoyaltyInfoTransaction creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetRoyaltyInfoTransaction(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// SetRoyaltyInfoUnsigned creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetRoyaltyInfoUnsigned(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// itemToNftRoyaltyRecipientShare converts stack item into *NftRoyaltyRecipientShare.
// NULL item is returned as nil pointer without error.
func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyaltyRecipientShare, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(NftRoyaltyRecipientShare)
err = res.FromStackItem(item)
return res, err
}
// Ensure *NftRoyaltyRecipientShare is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&NftRoyaltyRecipientShare{})
// Ensure *NftRoyaltyRecipientShare is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&NftRoyaltyRecipientShare{})
// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.Address, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Address: %w", err)
}
index++
res.Share, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Share: %w", err)
}
return nil
}
// ToStackItem creates [stackitem.Item] representing NftRoyaltyRecipientShare.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = stackitem.NewByteArray(res.Address.BytesBE()), error(nil)
if err != nil {
return nil, fmt.Errorf("field Address: %w", err)
}
items = append(items, itm)
itm, err = (*stackitem.BigInteger)(res.Share), error(nil)
if err != nil {
return nil, fmt.Errorf("field Share: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing NftRoyaltyRecipientShare.
// It implements [smartcontract.Convertible] interface so that NftRoyaltyRecipientShare
// could be used with invokers.
func (res *NftRoyaltyRecipientShare) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.Address)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Address: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.Share)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Share: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}

View file

@ -0,0 +1,306 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for HASHY NFT contract.
package nft
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"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"
)
// NftRoyaltyRecipientShare is a contract-specific nft.RoyaltyRecipientShare type used by its methods.
type NftRoyaltyRecipientShare struct {
Address util.Uint160
Share *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.NonDivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.BaseWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
var nep11ndt = nep11.NewNonDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, *nep24t, actor, hash}, nep11ndt.BaseWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
func (c *Contract) scriptForSetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "setRoyaltyInfo", ctx, tokenID, recipients)
}
// SetRoyaltyInfo creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (util.Uint256, uint32, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// SetRoyaltyInfoTransaction creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetRoyaltyInfoTransaction(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// SetRoyaltyInfoUnsigned creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetRoyaltyInfoUnsigned(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// itemToNftRoyaltyRecipientShare converts stack item into *NftRoyaltyRecipientShare.
// NULL item is returned as nil pointer without error.
func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyaltyRecipientShare, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(NftRoyaltyRecipientShare)
err = res.FromStackItem(item)
return res, err
}
// Ensure *NftRoyaltyRecipientShare is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&NftRoyaltyRecipientShare{})
// Ensure *NftRoyaltyRecipientShare is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&NftRoyaltyRecipientShare{})
// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.Address, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Address: %w", err)
}
index++
res.Share, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Share: %w", err)
}
return nil
}
// ToStackItem creates [stackitem.Item] representing NftRoyaltyRecipientShare.
// It implements [stackitem.Convertible] interface.
func (res *NftRoyaltyRecipientShare) ToStackItem() (stackitem.Item, error) {
if res == nil {
return stackitem.Null{}, nil
}
var (
err error
itm stackitem.Item
items = make([]stackitem.Item, 0, 2)
)
itm, err = stackitem.NewByteArray(res.Address.BytesBE()), error(nil)
if err != nil {
return nil, fmt.Errorf("field Address: %w", err)
}
items = append(items, itm)
itm, err = (*stackitem.BigInteger)(res.Share), error(nil)
if err != nil {
return nil, fmt.Errorf("field Share: %w", err)
}
items = append(items, itm)
return stackitem.NewStruct(items), nil
}
// ToSCParameter creates [smartcontract.Parameter] representing NftRoyaltyRecipientShare.
// It implements [smartcontract.Convertible] interface so that NftRoyaltyRecipientShare
// could be used with invokers.
func (res *NftRoyaltyRecipientShare) ToSCParameter() (smartcontract.Parameter, error) {
if res == nil {
return smartcontract.Parameter{Type: smartcontract.AnyType}, nil
}
var (
err error
prm smartcontract.Parameter
prms = make([]smartcontract.Parameter, 0, 2)
)
prm, err = smartcontract.NewParameterFromValue(res.Address)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Address: %w", err)
}
prms = append(prms, prm)
prm, err = smartcontract.NewParameterFromValue(res.Share)
if err != nil {
return smartcontract.Parameter{}, fmt.Errorf("field Share: %w", err)
}
prms = append(prms, prm)
return smartcontract.Parameter{Type: smartcontract.ArrayType, Value: prms}, nil
}

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"
@ -186,17 +187,29 @@ func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) {
}
// itemToCrazyStruct converts stack item into *CrazyStruct.
// NULL item is returned as nil pointer without error.
func itemToCrazyStruct(item stackitem.Item, err error) (*CrazyStruct, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(CrazyStruct)
err = res.FromStackItem(item)
return res, err
}
// Ensure *CrazyStruct is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&CrazyStruct{})
// Ensure *CrazyStruct is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&CrazyStruct{})
// FromStackItem retrieves fields of CrazyStruct from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *CrazyStruct) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -225,18 +238,85 @@ 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) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(SimpleStruct)
err = res.FromStackItem(item)
return res, err
}
// Ensure *SimpleStruct is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&SimpleStruct{})
// Ensure *SimpleStruct is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&SimpleStruct{})
// FromStackItem retrieves fields of SimpleStruct from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *SimpleStruct) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -259,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"
@ -186,17 +187,29 @@ func (c *Contract) UnexportedFieldUnsigned() (*transaction.Transaction, error) {
}
// itemToUnnamed converts stack item into *Unnamed.
// NULL item is returned as nil pointer without error.
func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(Unnamed)
err = res.FromStackItem(item)
return res, err
}
// Ensure *Unnamed is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&Unnamed{})
// Ensure *Unnamed is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&Unnamed{})
// FromStackItem retrieves fields of Unnamed from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -225,18 +238,85 @@ 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) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(UnnamedX)
err = res.FromStackItem(item)
return res, err
}
// Ensure *UnnamedX is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&UnnamedX{})
// Ensure *UnnamedX is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&UnnamedX{})
// FromStackItem retrieves fields of UnnamedX from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -259,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) {

View file

@ -0,0 +1,16 @@
name: Test royalty
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-24-Payable"]
events:
- name: RoyaltiesTransferred
parameters:
- name: royaltyToken
type: Hash160
- name: royaltyRecipient
type: Hash160
- name: buyer
type: Hash160
- name: tokenId
type: ByteArray
- name: amount
type: Integer

View file

@ -0,0 +1,13 @@
package royalty
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
// RoyaltiesTransferred notifies about royalty payment. This method is called by marketplace
// contract when royalties are transferred.
func RoyaltiesTransferred(royaltyToken, royaltyRecipient, buyer interop.Hash160, tokenId []byte, amount int) {
runtime.Notify("RoyaltiesTransferred", royaltyToken, royaltyRecipient, buyer, std.Deserialize(tokenId), amount)
}

View file

@ -0,0 +1,57 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package royalty contains RPC wrappers for Test royalty contract.
package royalty
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Contract implements all contract methods.
type Contract struct {
actor Actor
hash util.Uint160
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
return &Contract{actor, hash}
}
// RoyaltiesTransferred creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) RoyaltiesTransferred(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredTransaction creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RoyaltiesTransferredTransaction(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredUnsigned creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RoyaltiesTransferredUnsigned(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "royaltiesTransferred", nil, royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}

View file

@ -0,0 +1,53 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package royalty contains RPC wrappers for Test royalty contract.
package royalty
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Actor is used by Contract to call state-changing methods.
type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Contract implements all contract methods.
type Contract struct {
actor Actor
hash util.Uint160
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{actor, hash}
}
// RoyaltiesTransferred creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) RoyaltiesTransferred(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredTransaction creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RoyaltiesTransferredTransaction(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredUnsigned creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RoyaltiesTransferredUnsigned(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "royaltiesTransferred", nil, royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}

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"
@ -370,17 +371,29 @@ func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) {
}
// itemToUnnamed converts stack item into *Unnamed.
// NULL item is returned as nil pointer without error.
func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(Unnamed)
err = res.FromStackItem(item)
return res, err
}
// Ensure *Unnamed is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&Unnamed{})
// Ensure *Unnamed is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&Unnamed{})
// FromStackItem retrieves fields of Unnamed from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -403,18 +416,73 @@ 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) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(UnnamedX)
err = res.FromStackItem(item)
return res, err
}
// Ensure *UnnamedX is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&UnnamedX{})
// Ensure *UnnamedX is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&UnnamedX{})
// FromStackItem retrieves fields of UnnamedX from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -442,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"
@ -366,17 +367,29 @@ func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) {
}
// itemToUnnamed converts stack item into *Unnamed.
// NULL item is returned as nil pointer without error.
func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(Unnamed)
err = res.FromStackItem(item)
return res, err
}
// Ensure *Unnamed is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&Unnamed{})
// Ensure *Unnamed is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&Unnamed{})
// FromStackItem retrieves fields of Unnamed from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -399,18 +412,73 @@ 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) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(UnnamedX)
err = res.FromStackItem(item)
return res, err
}
// Ensure *UnnamedX is a proper [stackitem.Convertible].
var _ = stackitem.Convertible(&UnnamedX{})
// Ensure *UnnamedX is a proper [smartcontract.Convertible].
var _ = smartcontract.Convertible(&UnnamedX{})
// FromStackItem retrieves fields of UnnamedX from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
// It implements [stackitem.Convertible] interface.
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
@ -438,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

@ -1 +1 @@
{"name":"verify","abi":{"methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Hello world!","parameters":[{"name":"args","type":"Array"}]}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null}
{"name":"verify","abi":{"methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Hello world!","parameters":[{"name":"args","type":"Array"}]}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null,"features":{}}

View file

@ -4,6 +4,7 @@
"supportedstandards" : [],
"name" : "verify",
"trusts" : [],
"features": {},
"permissions" : [
{
"methods" : "*",

View file

@ -65,7 +65,6 @@
"key": "6PYSATFztBa3CHjSR6sLAKungUEAbQUCVE16KzmaQQ38gLeYGZ9Knd5mGv",
"label": "verify",
"contract": {
"script": "VwEAEUBXAANA",
"parameters": [],
"deployed": true
},

View file

@ -16,34 +16,36 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
var (
// GasFlag is a flag used for the additional network fee.
GasFlag = flags.Fixed8Flag{
Name: "gas, g",
Usage: "network fee to add to the transaction (prioritizing it)",
GasFlag = &flags.Fixed8Flag{
Name: "gas",
Aliases: []string{"g"},
Usage: "Network fee to add to the transaction (prioritizing it)",
}
// SysGasFlag is a flag used for the additional system fee.
SysGasFlag = flags.Fixed8Flag{
Name: "sysgas, e",
Usage: "system fee to add to the transaction (compensating for execution)",
SysGasFlag = &flags.Fixed8Flag{
Name: "sysgas",
Aliases: []string{"e"},
Usage: "System fee to add to the transaction (compensating for execution)",
}
// OutFlag is a flag used for file output.
OutFlag = cli.StringFlag{
OutFlag = &cli.StringFlag{
Name: "out",
Usage: "file (JSON) to put signature context with a transaction to",
Usage: "File (JSON) to put signature context with a transaction to",
}
// ForceFlag is a flag used to force transaction send.
ForceFlag = cli.BoolFlag{
ForceFlag = &cli.BoolFlag{
Name: "force",
Usage: "Do not ask for a confirmation (and ignore errors)",
}
// AwaitFlag is a flag used to wait for the transaction to be included in a block.
AwaitFlag = cli.BoolFlag{
AwaitFlag = &cli.BoolFlag{
Name: "await",
Usage: "wait for the transaction to be included in a block",
Usage: "Wait for the transaction to be included in a block",
}
)
@ -71,7 +73,7 @@ func SignAndSend(ctx *cli.Context, act *actor.Actor, acc *wallet.Account, tx *tr
promptTime := time.Now()
err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
waitTime := time.Since(promptTime)
// Compensate for confirmation waiting.
@ -83,17 +85,17 @@ func SignAndSend(ctx *cli.Context, act *actor.Actor, acc *wallet.Account, tx *tr
)
resTx, vub, err = act.SignAndSend(tx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if ctx.Bool("await") {
aer, err = act.Wait(resTx, vub, err)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to await transaction %s: %w", resTx.StringLE(), err), 1)
return cli.Exit(fmt.Errorf("failed to await transaction %s: %w", resTx.StringLE(), err), 1)
}
}
}
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
DumpTransactionInfo(ctx.App.Writer, tx.Hash(), aer)

View file

@ -16,20 +16,20 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func cancelTx(ctx *cli.Context) error {
args := ctx.Args()
args := ctx.Args().Slice()
if len(args) == 0 {
return cli.NewExitError("transaction hash is missing", 1)
return cli.Exit("transaction hash is missing", 1)
} else if len(args) > 1 {
return cli.NewExitError("only one transaction hash is accepted", 1)
return cli.Exit("only one transaction hash is accepted", 1)
}
txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x"))
if err != nil {
return cli.NewExitError(fmt.Sprintf("invalid tx hash: %s", args[0]), 1)
return cli.Exit(fmt.Sprintf("invalid tx hash: %s", args[0]), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -37,13 +37,13 @@ func cancelTx(ctx *cli.Context) error {
acc, w, err := options.GetAccFromContext(ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1)
}
defer w.Close()
signers, err := cmdargs.GetSignersAccounts(acc, w, nil, transaction.CalledByEntry)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
}
c, a, exitErr := options.GetRPCWithActor(gctx, ctx, signers)
if exitErr != nil {
@ -52,11 +52,11 @@ func cancelTx(ctx *cli.Context) error {
mainTx, _ := c.GetRawTransactionVerbose(txHash)
if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) {
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1)
return cli.Exit(fmt.Errorf("target transaction %s is accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1)
}
if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) {
return cli.NewExitError(fmt.Errorf("account %s is not a signer of the conflicting transaction", acc.Address), 1)
return cli.Exit(fmt.Errorf("account %s is not a signer of the conflicting transaction", acc.Address), 1)
}
resHash, resVub, err := a.SendTunedRun([]byte{byte(opcode.RET)}, []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: txHash}}}, func(r *result.Invoke, t *transaction.Transaction) error {
@ -64,8 +64,8 @@ func cancelTx(ctx *cli.Context) error {
if err != nil {
return err
}
if mainTx != nil && t.NetworkFee < mainTx.NetworkFee+1 {
t.NetworkFee = mainTx.NetworkFee + 1
if mainTx != nil {
t.NetworkFee = max(t.NetworkFee, mainTx.NetworkFee+1)
}
t.NetworkFee += int64(flags.Fixed8FromContext(ctx, "gas"))
if mainTx != nil {
@ -74,7 +74,7 @@ func cancelTx(ctx *cli.Context) error {
return nil
})
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to send conflicting transaction: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to send conflicting transaction: %w", err), 1)
}
var res *state.AppExecResult
if ctx.Bool("await") {
@ -82,19 +82,19 @@ func cancelTx(ctx *cli.Context) error {
if err != nil {
if errors.Is(err, waiter.ErrTxNotAccepted) {
if mainTx == nil {
return cli.NewExitError(fmt.Errorf("neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of conlicting transaction). Main transaction is unknown to the provided RPC node, thus still has chances to be accepted, you may try cancellation again", resVub), 1)
return cli.Exit(fmt.Errorf("neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of conlicting transaction). Main transaction is unknown to the provided RPC node, thus still has chances to be accepted, you may try cancellation again", resVub), 1)
}
fmt.Fprintf(ctx.App.Writer, "Neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of both target and conflicting transactions). Main transaction is not valid anymore, cancellation is successful\n", resVub)
return nil
}
return cli.NewExitError(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1)
return cli.Exit(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1)
}
if txHash.Equals(res.Container) {
tx, err := c.GetRawTransactionVerbose(txHash)
if err != nil {
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted", txHash), 1)
return cli.Exit(fmt.Errorf("target transaction %s is accepted", txHash), 1)
}
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, tx.Blockhash.StringLE()), 1)
return cli.Exit(fmt.Errorf("target transaction %s is accepted at block %s", txHash, tx.Blockhash.StringLE()), 1)
}
fmt.Fprintln(ctx.App.Writer, "Conflicting transaction accepted")
}

View file

@ -6,32 +6,107 @@ import (
"fmt"
"os"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/cli/txctx"
vmcli "github.com/nspcc-dev/neo-go/cli/vm"
"github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/urfave/cli"
"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 {
txDumpFlags := append([]cli.Flag{}, options.RPC...)
func NewCommands() []*cli.Command {
// By default, RPC flag is required. sendtx and txdump may be called without provided rpc-endpoint.
rpcFlagOriginal, _ := options.RPC[0].(*cli.StringFlag)
rpcFlag := *rpcFlagOriginal
rpcFlag.Required = false
txDumpFlags := append([]cli.Flag{&rpcFlag}, options.RPC[1:]...)
txSendFlags := append(txDumpFlags, txctx.AwaitFlag)
txCancelFlags := append([]cli.Flag{
flags.AddressFlag{
Name: "address, a",
Usage: "address to use as conflicting transaction signee (and gas source)",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to use as conflicting transaction signee (and gas source)",
},
txctx.GasFlag,
txctx.AwaitFlag,
}, options.RPC...)
txCancelFlags = append(txCancelFlags, options.Wallet...)
return []cli.Command{
uploadBinFlags := append([]cli.Flag{
&cli.StringFlag{
Name: "block-attribute",
Usage: "Attribute key of the block object",
Value: neofs.DefaultBlockAttribute,
Action: cmdargs.EnsureNotEmpty("block-attribute"),
},
&cli.StringFlag{
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",
Usage: "Size of index file",
Value: neofs.DefaultIndexFileSize,
},
&cli.UintFlag{
Name: "workers",
Usage: "Number of workers to fetch and upload blocks concurrently",
Value: 20,
},
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",
Usage: "Various helper commands",
Subcommands: []cli.Command{
Subcommands: []*cli.Command{
{
Name: "convert",
Usage: "Convert provided argument into other possible formats",
@ -44,7 +119,7 @@ func NewCommands() []cli.Command {
{
Name: "sendtx",
Usage: "Send complete transaction stored in a context file",
UsageText: "sendtx [-r <endpoint>] <file.in> [--await]",
UsageText: "sendtx [-r <endpoint>] [--await] <file.in>",
Description: `Sends the transaction from the given context file to the given RPC node if it's
completely signed and ready. This command expects a ContractParametersContext
JSON file for input, it can't handle binary (or hex- or base64-encoded)
@ -57,7 +132,7 @@ func NewCommands() []cli.Command {
{
Name: "canceltx",
Usage: "Cancel transaction by sending conflicting transaction",
UsageText: "canceltx <txid> -r <endpoint> --wallet <wallet> [--account <account>] [--wallet-config <path>] [--gas <gas>] [--await]",
UsageText: "canceltx -r <endpoint> --wallet <wallet> [--address <account>] [--wallet-config <path>] [--gas <gas>] [--await] <txid>",
Description: `Aims to prevent a transaction from being added to the blockchain by dispatching a more
prioritized conflicting transaction to the specified RPC node. The input for this command should
be the transaction hash. If another account is not specified, the conflicting transaction is
@ -90,28 +165,43 @@ func NewCommands() []cli.Command {
{
Name: "ops",
Usage: "Pretty-print VM opcodes of the given base64- or hex- encoded script (base64 is checked first). If the input file is specified, then the script is taken from the file.",
UsageText: "ops <base64/hex-encoded script> [-i path-to-file] [--hex]",
UsageText: "ops [-i path-to-file] [--hex] <base64/hex-encoded script>",
Action: handleOps,
Flags: []cli.Flag{
cli.StringFlag{
Name: "in, i",
Usage: "input file containing base64- or hex- encoded script representation",
&cli.StringFlag{
Name: "in",
Aliases: []string{"i"},
Usage: "Input file containing base64- or hex- encoded script representation",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "hex",
Usage: "use hex encoding and do not check base64",
Usage: "Use hex encoding and do not check base64",
},
},
},
{
Name: "upload-bin",
Usage: "Fetch blocks from RPC node and upload them to the NeoFS container",
UsageText: "neo-go util upload-bin --fs-rpc-endpoint <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]",
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,
},
},
},
}
}
func handleParse(ctx *cli.Context) error {
res, err := vmcli.Parse(ctx.Args())
res, err := vmcli.Parse(ctx.Args().Slice())
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
fmt.Fprint(ctx.App.Writer, res)
return nil
@ -127,21 +217,21 @@ func handleOps(ctx *cli.Context) error {
if len(in) != 0 {
b, err := os.ReadFile(in)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to read file: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to read file: %w", err), 1)
}
s = string(b)
} else {
if !ctx.Args().Present() {
return cli.NewExitError("missing script", 1)
return cli.Exit("missing script", 1)
}
s = ctx.Args()[0]
s = ctx.Args().Slice()[0]
}
b, err = base64.StdEncoding.DecodeString(s)
if err != nil || ctx.Bool("hex") {
b, err = hex.DecodeString(s)
}
if err != nil {
return cli.NewExitError("unknown encoding: base64 or hex are supported", 1)
return cli.Exit("unknown encoding: base64 or hex are supported", 1)
}
v := vm.New()
v.LoadScript(b)

View file

@ -8,30 +8,30 @@ import (
"github.com/nspcc-dev/neo-go/cli/paramcontext"
"github.com/nspcc-dev/neo-go/cli/query"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func txDump(ctx *cli.Context) error {
args := ctx.Args()
args := ctx.Args().Slice()
if len(args) == 0 {
return cli.NewExitError("missing input file", 1)
return cli.Exit("missing input file", 1)
} else if len(args) > 1 {
return cli.NewExitError("only one input file is accepted", 1)
return cli.Exit("only one input file is accepted", 1)
}
c, err := paramcontext.Read(args[0])
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
tx, ok := c.Verifiable.(*transaction.Transaction)
if !ok {
return cli.NewExitError("verifiable item is not a transaction", 1)
return cli.Exit("verifiable item is not a transaction", 1)
}
err = query.DumpApplicationLog(ctx, nil, tx, nil, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if ctx.String(options.RPCEndpointFlag) != "" {
@ -41,15 +41,15 @@ func txDump(ctx *cli.Context) error {
var err error
cl, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
res, err := cl.InvokeScript(tx.Script, tx.Signers)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
resS, err := json.MarshalIndent(res, "", " ")
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
fmt.Fprintln(ctx.App.Writer, string(resS))
}

View file

@ -8,25 +8,25 @@ import (
"github.com/nspcc-dev/neo-go/cli/txctx"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func sendTx(ctx *cli.Context) error {
args := ctx.Args()
args := ctx.Args().Slice()
if len(args) == 0 {
return cli.NewExitError("missing input file", 1)
return cli.Exit("missing input file", 1)
} else if len(args) > 1 {
return cli.NewExitError("only one input file is accepted", 1)
return cli.Exit("only one input file is accepted", 1)
}
pc, err := paramcontext.Read(args[0])
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
tx, err := pc.GetCompleteTransaction()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to complete transaction: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to complete transaction: %w", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -34,18 +34,18 @@ func sendTx(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create RPC client: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to create RPC client: %w", err), 1)
}
res, err := c.SendRawTransaction(tx)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1)
}
var aer *state.AppExecResult
if ctx.Bool("await") {
version, err := c.GetVersion()
aer, err = waiter.New(c, version).Wait(res, tx.ValidUntilBlock, err)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1)
return cli.Exit(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1)
}
}
txctx.DumpTransactionInfo(ctx.App.Writer, res, aer)

449
cli/util/upload_bin.go Normal file
View file

@ -0,0 +1,449 @@
package util
import (
"context"
"fmt"
"strconv"
"sync"
"time"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/services/helpers/neofs"
"github.com/nspcc-dev/neofs-sdk-go/client"
"github.com/nspcc-dev/neofs-sdk-go/container"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"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/user"
"github.com/urfave/cli/v2"
)
func uploadBin(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
attr := ctx.String("block-attribute")
numWorkers := ctx.Uint("workers")
maxParallelSearches := ctx.Uint("searchers")
maxRetries := ctx.Uint("retries")
debug := ctx.Bool("debug")
indexFileSize := ctx.Uint("index-file-size")
indexAttrKey := ctx.String("index-attribute")
acc, _, err := options.GetAccFromContext(ctx)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to load account: %v", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
rpc, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to create RPC client: %v", err), 1)
}
signer, pWrapper, err := options.GetNeoFSClientPool(ctx, acc)
if err != nil {
return cli.Exit(err, 1)
}
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))
containerID, err := getContainer(ctx, pWrapper, magic, maxRetries, debug)
if err != nil {
return cli.Exit(err, 1)
}
currentBlockHeight, err := rpc.GetBlockCount()
if err != nil {
return cli.Exit(fmt.Sprintf("failed to get current block height from RPC: %v", err), 1)
}
fmt.Fprintln(ctx.App.Writer, "Chain block height:", currentBlockHeight)
i, buf, err := searchIndexFile(ctx, pWrapper, containerID, acc.PrivateKey(), signer, indexFileSize, attr, indexAttrKey, maxParallelSearches, maxRetries, debug)
if err != nil {
return cli.Exit(fmt.Errorf("failed to find objects: %w", err), 1)
}
err = uploadBlocksAndIndexFiles(ctx, pWrapper, rpc, signer, containerID, attr, indexAttrKey, buf, i, indexFileSize, uint(currentBlockHeight), numWorkers, maxRetries, debug)
if err != nil {
return cli.Exit(fmt.Errorf("failed to upload objects: %w", err), 1)
}
return nil
}
// retry function with exponential backoff.
func retry(action func() error, maxRetries uint, debug bool) error {
var err error
backoff := neofs.InitialBackoff
for i := range maxRetries {
if err = action(); err == nil {
return nil // Success, no retry needed.
}
if debug {
fmt.Printf("Retry %d: %v\n", i, err)
}
time.Sleep(backoff) // Backoff before retrying.
backoff *= time.Duration(neofs.BackoffFactor)
if backoff > neofs.MaxBackoff {
backoff = neofs.MaxBackoff
}
}
return err // Return the last error after exhausting retries.
}
// uploadBlocksAndIndexFiles uploads the blocks and index files to the container using the pool.
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
}
fmt.Fprintln(ctx.App.Writer, "Uploading blocks and index files...")
for indexFileStart := currentIndexFileID * indexFileSize; indexFileStart < currentBlockHeight; indexFileStart += indexFileSize {
var (
indexFileEnd = min(indexFileStart+indexFileSize, currentBlockHeight)
errCh = make(chan error)
doneCh = make(chan struct{})
wg sync.WaitGroup
)
fmt.Fprintf(ctx.App.Writer, "Processing batch from %d to %d\n", indexFileStart, indexFileEnd-1)
wg.Add(int(numWorkers))
for i := range numWorkers {
go func(i uint) {
defer wg.Done()
for blockIndex := indexFileStart + i; blockIndex < indexFileEnd; blockIndex += numWorkers {
if !oid.ID(buf[blockIndex%indexFileSize*oid.Size : blockIndex%indexFileSize*oid.Size+oid.Size]).IsZero() {
if debug {
fmt.Fprintf(ctx.App.Writer, "Block %d is already uploaded\n", blockIndex)
}
continue
}
var blk *block.Block
errGet := retry(func() error {
var errGetBlock error
blk, errGetBlock = rpc.GetBlockByIndex(uint32(blockIndex))
if errGetBlock != nil {
return fmt.Errorf("failed to fetch block %d: %w", blockIndex, errGetBlock)
}
return nil
}, maxRetries, debug)
if errGet != nil {
select {
case errCh <- errGet:
default:
}
return
}
bw := io.NewBufBinWriter()
blk.EncodeBinary(bw.BinWriter)
if bw.Err != nil {
select {
case errCh <- fmt.Errorf("failed to encode block %d: %w", blockIndex, bw.Err):
default:
}
return
}
attrs := []object.Attribute{
*object.NewAttribute(attr, strconv.Itoa(int(blk.Index))),
*object.NewAttribute("Primary", strconv.Itoa(int(blk.PrimaryIndex))),
*object.NewAttribute("Hash", blk.Hash().StringLE()),
*object.NewAttribute("PrevHash", blk.PrevHash.StringLE()),
*object.NewAttribute("BlockTime", strconv.FormatUint(blk.Timestamp, 10)),
*object.NewAttribute("Timestamp", strconv.FormatInt(time.Now().Unix(), 10)),
}
var (
objBytes = bw.Bytes()
resOid oid.ID
)
errRetr := retry(func() error {
var errUpload error
resOid, errUpload = uploadObj(ctx.Context, p, signer, containerID, objBytes, attrs)
if errUpload != nil {
return errUpload
}
if debug {
fmt.Fprintf(ctx.App.Writer, "Uploaded block %d with object ID: %s\n", blockIndex, resOid.String())
}
return errUpload
}, maxRetries, debug)
if errRetr != nil {
select {
case errCh <- errRetr:
default:
}
return
}
copy(buf[blockIndex%indexFileSize*oid.Size:], resOid[:])
}
}(i)
}
go func() {
wg.Wait()
close(doneCh)
}()
select {
case err := <-errCh:
return fmt.Errorf("upload error: %w", err)
case <-doneCh:
}
fmt.Fprintf(ctx.App.Writer, "Successfully processed batch of blocks: from %d to %d\n", indexFileStart, indexFileEnd-1)
// Additional check for empty OIDs in the buffer.
for k := uint(0); k < (indexFileEnd-indexFileStart)*oid.Size; k += oid.Size {
if oid.ID(buf[k : k+oid.Size]).IsZero() {
return fmt.Errorf("empty OID found in index file %d at position %d (block index %d)", indexFileStart/indexFileSize, k/oid.Size, indexFileStart/indexFileSize*indexFileSize+k/oid.Size)
}
}
if indexFileEnd-indexFileStart == indexFileSize {
attrs := []object.Attribute{
*object.NewAttribute(indexAttributeKey, strconv.Itoa(int(indexFileStart/indexFileSize))),
*object.NewAttribute("IndexSize", strconv.Itoa(int(indexFileSize))),
*object.NewAttribute("Timestamp", strconv.FormatInt(time.Now().Unix(), 10)),
}
err := retry(func() error {
var errUpload error
_, errUpload = uploadObj(ctx.Context, p, signer, containerID, buf, attrs)
return errUpload
}, maxRetries, debug)
if err != nil {
return fmt.Errorf("failed to upload index file: %w", err)
}
fmt.Fprintln(ctx.App.Writer, "Successfully uploaded index file ", indexFileStart/indexFileSize)
}
clear(buf)
}
return nil
}
// searchIndexFile returns the ID and buffer for the next index file to be uploaded.
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)
doneCh = make(chan struct{})
errCh = make(chan error)
existingIndexCount = uint(0)
filters = object.NewSearchFilters()
)
go func() {
defer close(doneCh)
// Search for existing index files.
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)
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 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)
// Start block parsing goroutines.
var (
// processedIndices is a mapping from position in buffer to the block index.
// It prevents duplicates.
processedIndices sync.Map
wg sync.WaitGroup
oidCh = make(chan oid.ID, 2*maxParallelSearches)
)
wg.Add(int(maxParallelSearches))
for range maxParallelSearches {
go func() {
defer wg.Done()
for id := range oidCh {
var obj object.Object
errRetr := retry(func() error {
var errGet error
obj, _, errGet = p.ObjectGetInit(ctx.Context, containerID, id, signer, client.PrmObjectGet{})
return errGet
}, maxRetries, debug)
if errRetr != nil {
select {
case errCh <- fmt.Errorf("failed to fetch object %s: %w", id.String(), errRetr):
default:
}
return
}
blockIndex, err := getBlockIndex(obj, blockAttributeKey)
if err != nil {
select {
case errCh <- fmt.Errorf("failed to get block index from object %s: %w", id.String(), err):
default:
}
return
}
pos := uint(blockIndex) % indexFileSize
if _, ok := processedIndices.LoadOrStore(pos, blockIndex); !ok {
copy(buf[pos*oid.Size:], id[:])
}
}
}()
}
// Search for blocks within the index file range.
objIDs := searchObjects(ctx.Context, p, containerID, privKeys, blockAttributeKey, existingIndexCount*indexFileSize, (existingIndexCount+1)*indexFileSize, maxParallelSearches, maxRetries, debug, errCh)
for id := range objIDs {
oidCh <- id
}
close(oidCh)
wg.Wait()
}()
select {
case err := <-errCh:
return existingIndexCount, nil, err
case <-doneCh:
return existingIndexCount, buf, nil
}
}
// 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 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
defer close(res)
for i := startIndex; i < endIndex; i += neofs.DefaultSearchBatchSize * maxParallelSearches {
for j := range maxParallelSearches {
start := i + j*neofs.DefaultSearchBatchSize
end := start + neofs.DefaultSearchBatchSize
if start >= endIndex {
break
}
if end > endIndex {
end = endIndex
}
wg.Add(1)
go func(start, end uint) {
defer wg.Done()
prm := client.PrmObjectSearch{}
filters := object.NewSearchFilters()
if len(additionalFilters) != 0 {
filters = additionalFilters[0]
}
if end == start+1 {
filters.AddFilter(blockAttributeKey, fmt.Sprintf("%d", start), object.MatchStringEqual)
} else {
filters.AddFilter(blockAttributeKey, fmt.Sprintf("%d", start), object.MatchNumGE)
filters.AddFilter(blockAttributeKey, fmt.Sprintf("%d", end), object.MatchNumLT)
}
prm.SetFilters(filters)
var objIDs []oid.ID
err := retry(func() error {
var errBlockSearch error
objIDs, errBlockSearch = neofs.ObjectSearch(ctx, p, privKeys, containerID.String(), prm)
return errBlockSearch
}, maxRetries, debug)
if err != nil {
select {
case errCh <- fmt.Errorf("failed to search for block(s) from %d to %d: %w", start, end, err):
default:
}
return
}
for _, id := range objIDs {
res <- id
}
}(start, end)
}
wg.Wait()
}
}()
return res
}
// uploadObj uploads object to the container using provided settings.
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
resOID = oid.ID{}
)
hdr.SetContainerID(containerID)
hdr.SetOwner(signer.UserID())
hdr.SetAttributes(attrs...)
writer, err := p.ObjectPutInit(ctx, hdr, signer, prmObjectPutInit)
if err != nil {
return resOID, fmt.Errorf("failed to initiate object upload: %w", err)
}
_, err = writer.Write(objData)
if err != nil {
_ = writer.Close()
return resOID, fmt.Errorf("failed to write object data: %w", err)
}
err = writer.Close()
if err != nil {
return resOID, fmt.Errorf("failed to close object writer: %w", err)
}
res := writer.GetResult()
resOID = res.StoredObjectID()
return resOID, nil
}
func getBlockIndex(header object.Object, attribute string) (int, error) {
for _, attr := range header.UserAttributes() {
if attr.Key() == attribute {
value := attr.Value()
blockIndex, err := strconv.Atoi(value)
if err != nil {
return -1, fmt.Errorf("attribute %s has invalid value: %s, error: %w", attribute, value, err)
}
return blockIndex, nil
}
}
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

@ -190,3 +190,21 @@ func TestAwaitUtilCancelTx(t *testing.T) {
t.Fatal(fmt.Errorf("unexpected error: %w", err))
}
}
func TestUploadBin(t *testing.T) {
e := testcli.NewExecutor(t, true)
args := []string{
"neo-go", "util", "upload-bin",
"--cid", "test",
"--wallet", "./not-exist.json",
"--block-attribute", "block",
"--index-attribute", "oid-index",
"--fsr", "st1.local.fs.neo.org:8080",
}
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 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

@ -12,6 +12,7 @@ import (
"io"
"math/big"
"os"
"slices"
"strconv"
"strings"
"text/tabwriter"
@ -41,10 +42,9 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
@ -70,22 +70,22 @@ const (
)
var (
historicFlag = cli.IntFlag{
historicFlag = &cli.IntFlag{
Name: historicFlagFullName,
Usage: "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " +
"Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1.",
}
gasFlag = cli.Int64Flag{
gasFlag = &cli.Int64Flag{
Name: gasFlagFullName,
Usage: "GAS limit for this execution (integer number, satoshi).",
}
hashFlag = cli.StringFlag{
hashFlag = &flags.AddressFlag{
Name: hashFlagFullName,
Usage: "Smart-contract hash in LE form or address",
}
)
var commands = []cli.Command{
var commands = []*cli.Command{
{
Name: "exit",
Usage: "Exit the VM prompt",
@ -110,6 +110,26 @@ Example:
> break 12`,
Action: handleBreak,
},
{
Name: "delete",
Usage: "Remove a breakpoint",
UsageText: `delete <ip>`,
Description: `<ip> is mandatory parameter.
Example:
> delete 12`,
Action: handleRemoveBreak,
},
{
Name: "ib",
Usage: "List breakpoints",
UsageText: `ib`,
Description: `List breakpoints.
Example:
> ib`,
Action: handleListBreak,
},
{
Name: "jump",
Usage: "Jump to the specified instruction (absolute IP value)",
@ -339,9 +359,10 @@ Example:
Usage: "Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration)",
UsageText: `env [-v]`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: verboseFlagFullName + ",v",
Usage: "Print the whole blockchain node configuration.",
&cli.BoolFlag{
Name: verboseFlagFullName,
Aliases: []string{"v"},
Usage: "Print the whole blockchain node configuration.",
},
},
Description: `Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration).
@ -353,15 +374,17 @@ Example:
{
Name: "storage",
Usage: "Dump storage of the contract with the specified hash, address or ID as is at the current stage of script invocation",
UsageText: `storage <hash-or-address-or-id> [<prefix>] [--backwards] [--diff]`,
UsageText: `storage [--backwards] [--diff] <hash-or-address-or-id> [<prefix>]`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: backwardsFlagFullName + ",b",
Usage: "Backwards traversal direction",
&cli.BoolFlag{
Name: backwardsFlagFullName,
Aliases: []string{"b"},
Usage: "Backwards traversal direction",
},
cli.BoolFlag{
Name: diffFlagFullName + ",d",
Usage: "Dump only those storage items that were added or changed during the current script invocation. Note that this call won't show removed storage items, use 'changes' command for that.",
&cli.BoolFlag{
Name: diffFlagFullName,
Aliases: []string{"d"},
Usage: "Dump only those storage items that were added or changed during the current script invocation. Note that this call won't show removed storage items, use 'changes' command for that.",
},
},
Description: `Dump storage of the contract with the specified hash, address or ID as is at the current stage of script invocation.
@ -400,7 +423,7 @@ func init() {
if !c.Hidden {
var flagsItems []readline.PrefixCompleterInterface
for _, f := range c.Flags {
names := strings.SplitN(f.GetName(), ", ", 2) // only long name will be offered
names := strings.SplitN(f.Names()[0], ", ", 2) // only long name will be offered
flagsItems = append(flagsItems, readline.PcItem("--"+names[0]))
}
pcItems = append(pcItems, readline.PcItem(c.Name, flagsItems...))
@ -459,7 +482,7 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg
log, _, logCloser, err := options.HandleLoggingParams(false, cfg.ApplicationConfiguration)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("failed to init logger: %w", err), 1)
return nil, cli.Exit(fmt.Errorf("failed to init logger: %w", err), 1)
}
filter := zap.WrapCore(func(z zapcore.Core) zapcore.Core {
return options.NewFilteringCore(z, func(entry zapcore.Entry) bool {
@ -479,12 +502,12 @@ func NewWithConfig(printLogotype bool, onExit func(int), c *readline.Config, cfg
chain, err := core.NewBlockchain(store, cfg.Blockchain(), fLog)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %w", err), 1)
return nil, cli.Exit(fmt.Errorf("could not initialize blockchain: %w", err), 1)
}
// Do not run chain, we need only state-related functionality from it.
ic, err := chain.GetTestVM(trigger.Application, nil, nil)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("failed to create test VM: %w", err), 1)
return nil, cli.Exit(fmt.Errorf("failed to create test VM: %w", err), 1)
}
vmcli := CLI{
@ -594,6 +617,33 @@ func handleBreak(c *cli.Context) error {
return nil
}
func handleRemoveBreak(c *cli.Context) error {
if !checkVMIsReady(c.App) {
return nil
}
n, err := getInstructionParameter(c)
if err != nil {
return err
}
v := getVMFromContext(c.App)
v.RemoveBreakPoint(n)
fmt.Fprintf(c.App.Writer, "breakpoint removed at instruction %d\n", n)
return nil
}
func handleListBreak(c *cli.Context) error {
if !checkVMIsReady(c.App) {
return nil
}
v := getVMFromContext(c.App)
for _, bp := range v.Context().BreakPoints() {
fmt.Fprintf(c.App.Writer, "%d\n", bp)
}
return nil
}
func handleJump(c *cli.Context) error {
if !checkVMIsReady(c.App) {
return nil
@ -610,7 +660,7 @@ func handleJump(c *cli.Context) error {
}
func getInstructionParameter(c *cli.Context) (int, error) {
args := c.Args()
args := c.Args().Slice()
if len(args) != 1 {
return 0, fmt.Errorf("%w: <ip>", ErrMissingParameter)
}
@ -645,11 +695,11 @@ func handleSlots(c *cli.Context) error {
var rawSlot string
switch c.Command.Name {
case "sslot":
rawSlot = vmCtx.DumpStaticSlot()
rawSlot = dumpSlot(vmCtx.StaticsSlot())
case "lslot":
rawSlot = vmCtx.DumpLocalSlot()
rawSlot = dumpSlot(vmCtx.LocalsSlot())
case "aslot":
rawSlot = vmCtx.DumpArgumentsSlot()
rawSlot = dumpSlot(vmCtx.ArgumentsSlot())
default:
return errors.New("unknown slot")
}
@ -657,6 +707,14 @@ func handleSlots(c *cli.Context) error {
return nil
}
func dumpSlot(s *vm.Slot) string {
if s == nil {
return "[]"
}
b, _ := json.MarshalIndent(s, "", " ")
return string(b)
}
// prepareVM retrieves --historic flag from context (if set) and resets app state
// (to the specified historic height if given).
func prepareVM(c *cli.Context, tx *transaction.Transaction) error {
@ -682,15 +740,12 @@ func getHashFlag(c *cli.Context) (util.Uint160, error) {
if !c.IsSet(hashFlagFullName) {
return util.Uint160{}, nil
}
h, err := flags.ParseAddress(c.String(hashFlagFullName))
if err != nil {
return util.Uint160{}, fmt.Errorf("failed to parse contract hash: %w", err)
}
return h, nil
h := c.Generic(hashFlagFullName).(*flags.Address)
return h.Uint160(), nil
}
func handleLoadNEF(c *cli.Context) error {
args := c.Args()
args := c.Args().Slice()
if len(args) < 1 {
return fmt.Errorf("%w: <nef> is required", ErrMissingParameter)
}
@ -734,7 +789,7 @@ func handleLoadNEF(c *cli.Context) error {
}
var signers []transaction.Signer
if signersStartOffset != 0 && len(args) > signersStartOffset {
signers, err = cmdargs.ParseSigners(c.Args()[signersStartOffset:])
signers, err = cmdargs.ParseSigners(args[signersStartOffset:])
if err != nil {
return fmt.Errorf("%w: failed to parse signers: %w", ErrInvalidParameter, err)
}
@ -761,7 +816,7 @@ func handleLoadNEF(c *cli.Context) error {
}
func handleLoadBase64(c *cli.Context) error {
args := c.Args()
args := c.Args().Slice()
if len(args) < 1 {
return fmt.Errorf("%w: <string>", ErrMissingParameter)
}
@ -801,7 +856,7 @@ func createFakeTransaction(script []byte, signers []transaction.Signer) *transac
}
func handleLoadHex(c *cli.Context) error {
args := c.Args()
args := c.Args().Slice()
if len(args) < 1 {
return fmt.Errorf("%w: <string>", ErrMissingParameter)
}
@ -833,7 +888,7 @@ func handleLoadHex(c *cli.Context) error {
}
func handleLoadGo(c *cli.Context) error {
args := c.Args()
args := c.Args().Slice()
if len(args) < 1 {
return fmt.Errorf("%w: <file>", ErrMissingParameter)
}
@ -885,7 +940,7 @@ func handleLoadGo(c *cli.Context) error {
}
func handleLoadTx(c *cli.Context) error {
args := c.Args()
args := c.Args().Slice()
if len(args) < 1 {
return fmt.Errorf("%w: <file-or-hash>", ErrMissingParameter)
}
@ -933,7 +988,7 @@ func handleLoadDeployed(c *cli.Context) error {
if !c.Args().Present() {
return errors.New("contract hash, address or ID is mandatory argument")
}
args := c.Args()
args := c.Args().Slice()
hashOrID := args[0]
ic := getInteropContextFromContext(c.App)
h, err := flags.ParseAddress(hashOrID)
@ -1062,7 +1117,7 @@ func getManifestFromFile(name string) (*manifest.Manifest, error) {
func handleRun(c *cli.Context) error {
v := getVMFromContext(c.App)
cs := getContractStateFromContext(c.App)
args := c.Args()
args := c.Args().Slice()
if len(args) != 0 {
var (
params []stackitem.Item
@ -1105,7 +1160,7 @@ func handleRun(c *cli.Context) error {
breaks := v.Context().BreakPoints() // We ensure that there's a context loaded.
ic.ReuseVM(v)
v.GasLimit = gasLimit
v.LoadNEFMethod(&cs.NEF, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
v.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
for _, bp := range breaks {
v.AddBreakPoint(bp)
}
@ -1181,7 +1236,7 @@ func handleStep(c *cli.Context) error {
return nil
}
v := getVMFromContext(c.App)
args := c.Args()
args := c.Args().Slice()
if len(args) > 0 {
n, err = strconv.Atoi(args[0])
if err != nil {
@ -1422,7 +1477,7 @@ func (c *CLI) Run() error {
}
func handleParse(c *cli.Context) error {
res, err := Parse(c.Args())
res, err := Parse(c.Args().Slice())
if err != nil {
return err
}
@ -1456,7 +1511,9 @@ func Parse(args []string) (string, error) {
}
buf = fmt.Appendf(buf, "Hex to String\t%s\n", fmt.Sprintf("%q", string(rawStr)))
buf = fmt.Appendf(buf, "Hex to Integer\t%s\n", bigint.FromBytes(rawStr))
buf = fmt.Appendf(buf, "Swap Endianness\t%s\n", hex.EncodeToString(slice.CopyReverse(rawStr)))
var clonedStr = slices.Clone(rawStr)
slices.Reverse(clonedStr)
buf = fmt.Appendf(buf, "Swap Endianness\t%s\n", hex.EncodeToString(clonedStr))
}
if addr, err := address.StringToUint160(arg); err == nil {
buf = fmt.Appendf(buf, "Address to BE ScriptHash\t%s\n", addr)

View file

@ -339,6 +339,31 @@ func TestRun_WithNewVMContextAndBreakpoints(t *testing.T) {
e.checkNextLine(t, "at breakpoint 10 (ADD)*")
e.checkStack(t, 13)
})
t.Run("contract breakpoints", func(t *testing.T) {
src := `package kek
func Main(a, b int) int {
var c = a + b
return c + 5
}`
tmpDir := t.TempDir()
filename := prepareLoadgoSrc(t, tmpDir, src)
e := newTestVMCLI(t)
e.runProgWithTimeout(t, 10*time.Second,
"loadgo "+filename,
"break 7",
"break 8",
"ib",
"delete 7",
)
e.checkNextLine(t, "READY: loaded \\d* instructions")
e.checkNextLine(t, "breakpoint added at instruction 7")
e.checkNextLine(t, "breakpoint added at instruction 8")
e.checkNextLine(t, "7")
e.checkNextLine(t, "8")
e.checkNextLine(t, "breakpoint removed at instruction 7")
})
}
// prepareLoadgoSrc prepares provided SC source file for loading into VM via `loadgo` command.
@ -353,7 +378,7 @@ require (
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0
)
replace github.com/nspcc-dev/neo-go/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + `
go 1.20`)
go 1.22`)
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm))
return filename
}
@ -1188,7 +1213,7 @@ func TestDumpStorage(t *testing.T) {
"storage "+address.Uint160ToString(h),
"storage 1",
"storage 1 "+hex.EncodeToString(expected[0].Key),
"storage 1 --backwards",
"storage --backwards 1",
"exit",
)
e.checkStorage(t, expected...)
@ -1214,11 +1239,11 @@ func TestDumpStorageDiff(t *testing.T) {
diff := storage.KeyValue{Key: []byte{3}, Value: []byte{3}}
e.runProg(t,
"storage 1",
"storage 1 --diff",
"storage --diff 1",
"loadhex "+hex.EncodeToString(script.Bytes()),
"run",
"storage 1",
"storage 1 --diff",
"storage --diff 1",
"exit",
)

View file

@ -8,16 +8,16 @@ import (
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// NewCommands returns 'vm' command.
func NewCommands() []cli.Command {
func NewCommands() []*cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
cfgFlags = append(cfgFlags, options.Network...)
return []cli.Command{{
return []*cli.Command{{
Name: "vm",
Usage: "start the virtual machine",
Usage: "Start the virtual machine",
Action: startVMPrompt,
Flags: cfgFlags,
}}
@ -30,7 +30,7 @@ func startVMPrompt(ctx *cli.Context) error {
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if ctx.NumFlags() == 0 {
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
@ -42,7 +42,7 @@ func startVMPrompt(ctx *cli.Context) error {
p, err := NewWithConfig(true, os.Exit, &readline.Config{}, cfg)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create VM CLI: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to create VM CLI: %w", err), 1)
}
return p.Run()
}

View file

@ -39,7 +39,7 @@ func TestRegisterCandidate(t *testing.T) {
e.CheckEOF(t)
// missing address
e.RunWithError(t, "neo-go", "wallet", "candidate", "register",
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "candidate", "register",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet)
@ -131,7 +131,7 @@ func TestRegisterCandidate(t *testing.T) {
})
// missing address
e.RunWithError(t, "neo-go", "wallet", "candidate", "unregister",
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "candidate", "unregister",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet)
// additional argument
@ -153,7 +153,7 @@ func TestRegisterCandidate(t *testing.T) {
require.Equal(t, 0, len(vs))
// query voter: missing address
e.RunWithError(t, "neo-go", "query", "voter")
e.RunWithError(t, "neo-go", "query", "voter", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
// Excessive parameters.
e.RunWithError(t, "neo-go", "query", "voter", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], validatorAddress, validatorAddress)
e.RunWithError(t, "neo-go", "query", "committee", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0], "something")

View file

@ -12,7 +12,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func signStoredTransaction(ctx *cli.Context) error {
@ -28,52 +28,52 @@ func signStoredTransaction(ctx *cli.Context) error {
pc, err := paramcontext.Read(ctx.String("in"))
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if !addrFlag.IsSet {
return cli.NewExitError("address was not provided", 1)
return cli.Exit("address was not provided", 1)
}
acc, _, err := options.GetAccFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
tx, ok := pc.Verifiable.(*transaction.Transaction)
if !ok {
return cli.NewExitError("verifiable item is not a transaction", 1)
return cli.Exit("verifiable item is not a transaction", 1)
}
if !tx.HasSigner(acc.ScriptHash()) {
return cli.NewExitError("tx signers don't contain provided account", 1)
return cli.Exit("tx signers don't contain provided account", 1)
}
if acc.CanSign() {
sign := acc.SignHashable(pc.Network, pc.Verifiable)
if err := pc.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil {
return cli.NewExitError(fmt.Errorf("can't add signature: %w", err), 1)
return cli.Exit(fmt.Errorf("can't add signature: %w", err), 1)
}
} else if rpcNode == "" {
return cli.NewExitError(fmt.Errorf("can't sign transactions with the given account and no RPC endpoing given to send anything signed"), 1)
return cli.Exit(fmt.Errorf("can't sign transactions with the given account and no RPC endpoing given to send anything signed"), 1)
}
// Not saving and not sending, print.
if out == "" && rpcNode == "" {
txt, err := json.MarshalIndent(pc, " ", " ")
if err != nil {
return cli.NewExitError(fmt.Errorf("can't display resulting context: %w", err), 1)
return cli.Exit(fmt.Errorf("can't display resulting context: %w", err), 1)
}
fmt.Fprintln(ctx.App.Writer, string(txt))
return nil
}
if out != "" {
if err := paramcontext.Save(pc, out); err != nil {
return cli.NewExitError(fmt.Errorf("can't save resulting context: %w", err), 1)
return cli.Exit(fmt.Errorf("can't save resulting context: %w", err), 1)
}
}
if rpcNode != "" {
tx, err = pc.GetCompleteTransaction()
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to complete transaction: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to complete transaction: %w", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -82,17 +82,17 @@ func signStoredTransaction(ctx *cli.Context) error {
var err error // `GetRPCClient` returns specialized type.
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create RPC client: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to create RPC client: %w", err), 1)
}
res, err := c.SendRawTransaction(tx)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1)
return cli.Exit(fmt.Errorf("failed to submit transaction to RPC node: %w", err), 1)
}
if ctx.Bool("await") {
version, err := c.GetVersion()
aer, err = waiter.New(c, version).Wait(res, tx.ValidUntilBlock, err)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1)
return cli.Exit(fmt.Errorf("failed to await transaction %s: %w", res.StringLE(), err), 1)
}
}
}

View file

@ -79,10 +79,10 @@ func TestSignMultisigTx(t *testing.T) {
"--out", txPath)
// missing wallet
e.RunWithError(t, "neo-go", "wallet", "sign")
e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "wallet", "sign")
// missing in
e.RunWithError(t, "neo-go", "wallet", "sign",
e.RunWithErrorCheck(t, `Required flag "in" not set`, "neo-go", "wallet", "sign",
"--wallet", wallet2Path)
// missing address

View file

@ -4,6 +4,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"slices"
"strconv"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
@ -19,39 +20,41 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func newNEP11Commands() []cli.Command {
func newNEP11Commands() []*cli.Command {
maxIters := strconv.Itoa(config.DefaultMaxIteratorResultItems)
tokenAddressFlag := flags.AddressFlag{
Name: "token",
Usage: "Token contract address or hash in LE",
tokenAddressFlag := &flags.AddressFlag{
Name: "token",
Usage: "Token contract address or hash in LE",
Required: true,
}
ownerAddressFlag := flags.AddressFlag{
Name: "address",
Usage: "NFT owner address or hash in LE",
ownerAddressFlag := &flags.AddressFlag{
Name: "address",
Usage: "NFT owner address or hash in LE",
Required: true,
}
tokenID := cli.StringFlag{
tokenID := &cli.StringFlag{
Name: "id",
Usage: "Hex-encoded token ID",
}
balanceFlags := make([]cli.Flag, len(baseBalanceFlags))
copy(balanceFlags, baseBalanceFlags)
balanceFlags := slices.Clone(baseBalanceFlags)
balanceFlags = append(balanceFlags, tokenID)
balanceFlags = append(balanceFlags, options.RPC...)
transferFlags := make([]cli.Flag, len(baseTransferFlags))
copy(transferFlags, baseTransferFlags)
transferFlags := slices.Clone(baseTransferFlags)
transferFlags = append(transferFlags, tokenID)
transferFlags = append(transferFlags, options.RPC...)
return []cli.Command{
return []*cli.Command{
{
Name: "balance",
Usage: "get address balance",
UsageText: "balance -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>] [--id <token-id>]",
Description: `Prints NEP-11 balances for address and assets/IDs specified. By default (no
address or token parameter) all tokens (NFT contracts) for all accounts in
Usage: "Get address balance",
UsageText: "balance [-w wallet] [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>] [--id <token-id>]",
Description: `Prints NEP-11 balances for address and assets/IDs specified. One of wallet
or address must be specified, passing both is valid too. If a wallet is
given without an address all tokens (NFT contracts) for all accounts in
the specified wallet are listed with all tokens (actual NFTs) insied. A
single account can be chosen with the address option and/or a single NFT
contract can be selected with the token option. Further, you can specify a
@ -70,14 +73,14 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "import",
Usage: "import NEP-11 token to a wallet",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> --timeout <time> --token <hash>",
Usage: "Import NEP-11 token to a wallet",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] --token <hash>",
Action: importNEP11Token,
Flags: importFlags,
},
{
Name: "info",
Usage: "print imported NEP-11 token info",
Usage: "Print imported NEP-11 token info",
UsageText: "print -w wallet [--wallet-config path] [--token <hash-or-name>]",
Action: printNEP11Info,
Flags: []cli.Flag{
@ -88,7 +91,7 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "remove",
Usage: "remove NEP-11 token from the wallet",
Usage: "Remove NEP-11 token from the wallet",
UsageText: "remove -w wallet [--wallet-config path] --token <hash-or-name>",
Action: removeNEP11Token,
Flags: []cli.Flag{
@ -100,8 +103,8 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "transfer",
Usage: "transfer NEP-11 tokens",
UsageText: "transfer -w wallet [--wallet-config path] --rpc-endpoint <node> --timeout <time> --from <addr> --to <addr> --token <hash-or-name> --id <token-id> [--amount string] [--await] [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
Usage: "Transfer NEP-11 tokens",
UsageText: "transfer -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] --from <addr> --to <addr> --token <hash-or-name> --id <token-id> [--amount string] [--await] [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
Action: transferNEP11,
Flags: transferFlags,
Description: `Transfers specified NEP-11 token with optional cosigners list attached to
@ -116,7 +119,7 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "properties",
Usage: "print properties of NEP-11 token",
Usage: "Print properties of NEP-11 token",
UsageText: "properties --rpc-endpoint <node> [--timeout <time>] --token <hash> --id <token-id> [--historic <block/hash>]",
Action: printNEP11Properties,
Flags: append([]cli.Flag{
@ -127,7 +130,7 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "ownerOf",
Usage: "print owner of non-divisible NEP-11 token with the specified ID",
Usage: "Print owner of non-divisible NEP-11 token with the specified ID",
UsageText: "ownerOf --rpc-endpoint <node> [--timeout <time>] --token <hash> --id <token-id> [--historic <block/hash>]",
Action: printNEP11NDOwner,
Flags: append([]cli.Flag{
@ -138,7 +141,7 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "ownerOfD",
Usage: "print set of owners of divisible NEP-11 token with the specified ID (" + maxIters + " will be printed at max)",
Usage: "Print set of owners of divisible NEP-11 token with the specified ID (" + maxIters + " will be printed at max)",
UsageText: "ownerOfD --rpc-endpoint <node> [--timeout <time>] --token <hash> --id <token-id> [--historic <block/hash>]",
Action: printNEP11DOwner,
Flags: append([]cli.Flag{
@ -149,7 +152,7 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "tokensOf",
Usage: "print list of tokens IDs for the specified NFT owner (" + maxIters + " will be printed at max)",
Usage: "Print list of tokens IDs for the specified NFT owner (" + maxIters + " will be printed at max)",
UsageText: "tokensOf --rpc-endpoint <node> [--timeout <time>] --token <hash> --address <addr> [--historic <block/hash>]",
Action: printNEP11TokensOf,
Flags: append([]cli.Flag{
@ -160,7 +163,7 @@ func newNEP11Commands() []cli.Command {
},
{
Name: "tokens",
Usage: "print list of tokens IDs minted by the specified NFT (optional method; " + maxIters + " will be printed at max)",
Usage: "Print list of tokens IDs minted by the specified NFT (optional method; " + maxIters + " will be printed at max)",
UsageText: "tokens --rpc-endpoint <node> [--timeout <time>] --token <hash> [--historic <block/hash>]",
Action: printNEP11Tokens,
Flags: append([]cli.Flag{
@ -246,17 +249,13 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
return err
}
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1)
}
tokenID := ctx.String("id")
if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1)
return cli.Exit(errors.New("token ID should be specified"), 1)
}
tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -271,7 +270,7 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
n11 := nep11.NewDivisibleReader(inv, tokenHash.Uint160())
result, err := n11.OwnerOfExpanded(tokenIDBytes, config.DefaultMaxIteratorResultItems)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 divisible `ownerOf` method: %s", err.Error()), 1)
return cli.Exit(fmt.Sprintf("failed to call NEP-11 divisible `ownerOf` method: %s", err.Error()), 1)
}
for _, h := range result {
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(h))
@ -280,7 +279,7 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
n11 := nep11.NewNonDivisibleReader(inv, tokenHash.Uint160())
result, err := n11.OwnerOf(tokenIDBytes)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 non-divisible `ownerOf` method: %s", err.Error()), 1)
return cli.Exit(fmt.Sprintf("failed to call NEP-11 non-divisible `ownerOf` method: %s", err.Error()), 1)
}
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(result))
}
@ -291,15 +290,7 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
func printNEP11TokensOf(ctx *cli.Context) error {
var err error
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1)
}
acc := ctx.Generic("address").(*flags.Address)
if !acc.IsSet {
return cli.NewExitError("owner address flag was not set", 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
@ -311,7 +302,7 @@ func printNEP11TokensOf(ctx *cli.Context) error {
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.TokensOfExpanded(acc.Uint160(), config.DefaultMaxIteratorResultItems)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `tokensOf` method: %s", err.Error()), 1)
return cli.Exit(fmt.Sprintf("failed to call NEP-11 `tokensOf` method: %s", err.Error()), 1)
}
for i := range result {
@ -326,10 +317,6 @@ func printNEP11Tokens(ctx *cli.Context) error {
return err
}
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
@ -341,7 +328,7 @@ func printNEP11Tokens(ctx *cli.Context) error {
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.TokensExpanded(config.DefaultMaxIteratorResultItems)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call optional NEP-11 `tokens` method: %s", err.Error()), 1)
return cli.Exit(fmt.Sprintf("failed to call optional NEP-11 `tokens` method: %s", err.Error()), 1)
}
for i := range result {
@ -356,17 +343,13 @@ func printNEP11Properties(ctx *cli.Context) error {
return err
}
tokenHash := ctx.Generic("token").(*flags.Address)
if !tokenHash.IsSet {
return cli.NewExitError("token contract hash was not set", 1)
}
tokenID := ctx.String("id")
if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1)
return cli.Exit(errors.New("token ID should be specified"), 1)
}
tokenIDBytes, err := hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid tokenID bytes: %w", err), 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -380,12 +363,12 @@ func printNEP11Properties(ctx *cli.Context) error {
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.Properties(tokenIDBytes)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `properties` method: %s", err.Error()), 1)
return cli.Exit(fmt.Sprintf("failed to call NEP-11 `properties` method: %s", err.Error()), 1)
}
bytes, err := stackitem.ToJSON(result)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to convert result to JSON: %s", err), 1)
return cli.Exit(fmt.Sprintf("failed to convert result to JSON: %s", err), 1)
}
fmt.Fprintln(ctx.App.Writer, string(bytes))
return nil

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"math/big"
"slices"
"strings"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
@ -27,7 +28,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
// transferTarget represents target address, token amount and data for transfer.
@ -39,7 +40,7 @@ type transferTarget struct {
}
var (
tokenFlag = cli.StringFlag{
tokenFlag = &cli.StringFlag{
Name: "token",
Usage: "Token to use (hash or name (for NEO/GAS or imported tokens))",
}
@ -47,17 +48,19 @@ var (
walletPathFlag,
walletConfigFlag,
tokenFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "Address to use",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to use",
},
}
importFlags = append([]cli.Flag{
walletPathFlag,
walletConfigFlag,
flags.AddressFlag{
Name: "token",
Usage: "Token contract address or hash in LE",
&flags.AddressFlag{
Name: "token",
Usage: "Token contract address or hash in LE",
Required: true,
},
}, options.RPC...)
baseTransferFlags = []cli.Flag{
@ -71,7 +74,7 @@ var (
txctx.SysGasFlag,
txctx.ForceFlag,
txctx.AwaitFlag,
cli.StringFlag{
&cli.StringFlag{
Name: "amount",
Usage: "Amount of asset to send",
},
@ -88,21 +91,21 @@ var (
}, options.RPC...)
)
func newNEP17Commands() []cli.Command {
balanceFlags := make([]cli.Flag, len(baseBalanceFlags))
copy(balanceFlags, baseBalanceFlags)
func newNEP17Commands() []*cli.Command {
balanceFlags := slices.Clone(baseBalanceFlags)
balanceFlags = append(balanceFlags, options.RPC...)
transferFlags := make([]cli.Flag, len(baseTransferFlags))
copy(transferFlags, baseTransferFlags)
transferFlags := slices.Clone(baseTransferFlags)
transferFlags = append(transferFlags, options.RPC...)
return []cli.Command{
return []*cli.Command{
{
Name: "balance",
Usage: "get address balance",
UsageText: "balance -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>]",
Description: `Prints NEP-17 balances for address and tokens specified. By default (no
address or token parameter) all tokens for all accounts in the specified wallet
are listed. A single account can be chosen with the address option and/or a
Usage: "Get address balance",
UsageText: "balance [-w wallet] [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] [--address <address>] [--token <hash-or-name>]",
Description: `Prints NEP-17 balances for address and tokens specified. One of wallet
or address must be specified, passing both is valid too. If a wallet is
given without an address all tokens for all accounts in this wallet are
listed. A single account can be chosen with the address option and/or a
single token can be selected with the token option. Tokens can be specified
by hash, address, name or symbol. Hashes and addresses always work (as long
as they belong to a correct NEP-17 contract), while names or symbols (if
@ -118,14 +121,14 @@ func newNEP17Commands() []cli.Command {
},
{
Name: "import",
Usage: "import NEP-17 token to a wallet",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> --timeout <time> --token <hash>",
Usage: "Import NEP-17 token to a wallet",
UsageText: "import -w wallet [--wallet-config path] --rpc-endpoint <node> [--timeout <time>] --token <hash>",
Action: importNEP17Token,
Flags: importFlags,
},
{
Name: "info",
Usage: "print imported NEP-17 token info",
Usage: "Print imported NEP-17 token info",
UsageText: "print -w wallet [--wallet-config path] [--token <hash-or-name>]",
Action: printNEP17Info,
Flags: []cli.Flag{
@ -136,7 +139,7 @@ func newNEP17Commands() []cli.Command {
},
{
Name: "remove",
Usage: "remove NEP-17 token from the wallet",
Usage: "Remove NEP-17 token from the wallet",
UsageText: "remove -w wallet [--wallet-config path] --token <hash-or-name>",
Action: removeNEP17Token,
Flags: []cli.Flag{
@ -148,8 +151,8 @@ func newNEP17Commands() []cli.Command {
},
{
Name: "transfer",
Usage: "transfer NEP-17 tokens",
UsageText: "transfer -w wallet [--wallet-config path] [--await] --rpc-endpoint <node> --timeout <time> --from <addr> --to <addr> --token <hash-or-name> --amount string [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
Usage: "Transfer NEP-17 tokens",
UsageText: "transfer -w wallet [--wallet-config path] [--await] --rpc-endpoint <node> [--timeout <time>] --from <addr> --to <addr> --token <hash-or-name> --amount string [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
Action: transferNEP17,
Flags: transferFlags,
Description: `Transfers specified NEP-17 token amount with optional 'data' parameter and cosigners
@ -163,7 +166,7 @@ func newNEP17Commands() []cli.Command {
},
{
Name: "multitransfer",
Usage: "transfer NEP-17 tokens to multiple recipients",
Usage: "Transfer NEP-17 tokens to multiple recipients",
UsageText: `multitransfer -w wallet [--wallet-config path] [--await] --rpc-endpoint <node> --timeout <time> --from <addr>` +
` <token1>:<addr1>:<amount1> [<token2>:<addr2>:<amount2> [...]] [-- <cosigner1:Scope> [<cosigner2> [...]]]`,
Action: multiTransferNEP17,
@ -215,30 +218,40 @@ func getNEP17Balance(ctx *cli.Context) error {
}
func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Context, *rpcclient.Client, util.Uint160, string, *wallet.Token, string) error) error {
var accounts []*wallet.Account
var addresses []util.Uint160
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
wall, _, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("bad wallet: %w", err), 1)
if !errors.Is(err, errNoPath) {
return cli.Exit(fmt.Errorf("bad wallet: %w", err), 1)
}
} else {
defer wall.Close()
}
defer wall.Close()
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
addrHash := addrFlag.Uint160()
acc := wall.GetAccount(addrHash)
if acc == nil {
return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
if wall != nil {
acc := wall.GetAccount(addrHash)
if acc == nil {
return cli.Exit(fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addrHash)), 1)
}
}
accounts = append(accounts, acc)
addresses = append(addresses, addrHash)
} else {
if len(wall.Accounts) == 0 {
return cli.NewExitError(errors.New("no accounts in the wallet"), 1)
if wall == nil {
return cli.Exit(errors.New("neither wallet nor address specified"), 1)
}
if len(wall.Accounts) == 0 {
return cli.Exit(errors.New("no accounts in the wallet"), 1)
}
for _, acc := range wall.Accounts {
addresses = append(addresses, acc.ScriptHash())
}
accounts = wall.Accounts
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -246,7 +259,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
name := ctx.String("token")
@ -274,7 +287,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
// But if we have an exact hash, it must be correct.
token, err = getTokenWithStandard(c, h, standard)
if err != nil {
return cli.NewExitError(fmt.Errorf("%q is not a valid %s token: %w", name, standard, err), 1)
return cli.Exit(fmt.Errorf("%q is not a valid %s token: %w", name, standard, err), 1)
}
}
}
@ -284,19 +297,19 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
if len(tokenID) > 0 {
_, err = hex.DecodeString(tokenID)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid token ID: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid token ID: %w", err), 1)
}
}
}
for k, acc := range accounts {
for k, addr := range addresses {
if k != 0 {
fmt.Fprintln(ctx.App.Writer)
}
fmt.Fprintf(ctx.App.Writer, "Account %s\n", acc.Address)
fmt.Fprintf(ctx.App.Writer, "Account %s\n", address.Uint160ToString(addr))
err = accHandler(ctx, c, acc.ScriptHash(), name, token, tokenID)
err = accHandler(ctx, c, addr, name, token, tokenID)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
}
return nil
@ -319,6 +332,9 @@ func printAssetBalance(ctx *cli.Context, balance result.NEP17Balance) {
}
func getMatchingToken(ctx *cli.Context, w *wallet.Wallet, name string, standard string) (*wallet.Token, error) {
if w == nil {
return getMatchingTokenAux(ctx, nil, 0, name, standard)
}
return getMatchingTokenAux(ctx, func(i int) *wallet.Token {
return w.Extra.Tokens[i]
}, len(w.Extra.Tokens), name, standard)
@ -360,7 +376,7 @@ func getMatchingTokenRPC(ctx *cli.Context, c *rpcclient.Client, addr util.Uint16
func getMatchingTokenAux(ctx *cli.Context, get func(i int) *wallet.Token, n int, name string, standard string) (*wallet.Token, error) {
var token *wallet.Token
var count int
for i := 0; i < n; i++ {
for i := range n {
t := get(i)
if t != nil && (t.Hash.StringLE() == name || t.Address() == name || t.Symbol == name || t.Name == name) && t.Standard == standard {
if count == 1 {
@ -388,20 +404,17 @@ func importNEPToken(ctx *cli.Context, standard string) error {
}
wall, _, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
tokenHashFlag := ctx.Generic("token").(*flags.Address)
if !tokenHashFlag.IsSet {
return cli.NewExitError("token contract hash was not set", 1)
}
tokenHash := tokenHashFlag.Uint160()
for _, t := range wall.Extra.Tokens {
if t.Hash.Equals(tokenHash) && t.Standard == standard {
printTokenInfo(ctx, t)
return cli.NewExitError(fmt.Errorf("%s token already exists", standard), 1)
return cli.Exit(fmt.Errorf("%s token already exists", standard), 1)
}
}
@ -410,17 +423,17 @@ func importNEPToken(ctx *cli.Context, standard string) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
tok, err := getTokenWithStandard(c, tokenHash, standard)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't receive token info: %w", err), 1)
return cli.Exit(fmt.Errorf("can't receive token info: %w", err), 1)
}
wall.AddToken(tok)
if err := wall.Save(); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
printTokenInfo(ctx, tok)
return nil
@ -457,14 +470,14 @@ func printNEPInfo(ctx *cli.Context, standard string) error {
}
wall, _, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
if name := ctx.String("token"); name != "" {
token, err := getMatchingToken(ctx, wall, name, standard)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
printTokenInfo(ctx, token)
return nil
@ -493,13 +506,13 @@ func removeNEPToken(ctx *cli.Context, standard string) error {
}
wall, _, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if !ctx.Bool("force") {
if ok := askForConsent(ctx.App.Writer); !ok {
@ -507,9 +520,9 @@ func removeNEPToken(ctx *cli.Context, standard string) error {
}
}
if err := wall.RemoveToken(token.Hash); err != nil {
return cli.NewExitError(fmt.Errorf("can't remove token: %w", err), 1)
return cli.Exit(fmt.Errorf("can't remove token: %w", err), 1)
} else if err := wall.Save(); err != nil {
return cli.NewExitError(fmt.Errorf("error while saving wallet: %w", err), 1)
return cli.Exit(fmt.Errorf("error while saving wallet: %w", err), 1)
}
return nil
}
@ -517,31 +530,31 @@ func removeNEPToken(ctx *cli.Context, standard string) error {
func multiTransferNEP17(ctx *cli.Context) error {
wall, pass, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
fromFlag := ctx.Generic("from").(*flags.Address)
from, err := getDefaultAddress(fromFlag, wall)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
acc, err := options.GetUnlockedAccount(wall, from, pass)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
if ctx.NArg() == 0 {
return cli.NewExitError("empty recipients list", 1)
return cli.Exit("empty recipients list", 1)
}
var (
recipients []transferTarget
cosignersSepPos = ctx.NArg() // `--` position.
)
for i := 0; i < ctx.NArg(); i++ {
for i := range ctx.NArg() {
arg := ctx.Args().Get(i)
if arg == cmdargs.CosignersSeparator {
cosignersSepPos = i
@ -554,7 +567,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
}
signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
}
c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts)
if exitErr != nil {
@ -562,11 +575,11 @@ func multiTransferNEP17(ctx *cli.Context) error {
}
cache := make(map[string]*wallet.Token)
for i := 0; i < cosignersSepPos; i++ {
for i := range cosignersSepPos {
arg := ctx.Args().Get(i)
ss := strings.SplitN(arg, ":", 3)
if len(ss) != 3 {
return cli.NewExitError("send format must be '<token>:<addr>:<amount>", 1)
return cli.Exit("send format must be '<token>:<addr>:<amount>", 1)
}
token, ok := cache[ss[0]]
if !ok {
@ -574,18 +587,18 @@ func multiTransferNEP17(ctx *cli.Context) error {
if err != nil {
token, err = getMatchingTokenRPC(ctx, c, from, ss[0], manifest.NEP17StandardName)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
return cli.Exit(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
}
}
}
cache[ss[0]] = token
addr, err := address.StringToUint160(ss[1])
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid address: '%s'", ss[1]), 1)
return cli.Exit(fmt.Errorf("invalid address: '%s'", ss[1]), 1)
}
amount, err := fixedn.FromString(ss[2], int(token.Decimals))
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid amount: %w", err), 1)
}
recipients = append(recipients, transferTarget{
Token: token.Hash,
@ -597,7 +610,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
tx, err := makeMultiTransferNEP17(act, recipients)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't make transaction: %w", err), 1)
return cli.Exit(fmt.Errorf("can't make transaction: %w", err), 1)
}
return txctx.SignAndSend(ctx, act, acc, tx)
}
@ -611,18 +624,18 @@ func transferNEP(ctx *cli.Context, standard string) error {
wall, pass, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
fromFlag := ctx.Generic("from").(*flags.Address)
from, err := getDefaultAddress(fromFlag, wall)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
acc, err := options.GetUnlockedAccount(wall, from, pass)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -638,7 +651,7 @@ func transferNEP(ctx *cli.Context, standard string) error {
}
signersAccounts, err := cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.CalledByEntry)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
}
c, act, exitErr := options.GetRPCWithActor(gctx, ctx, signersAccounts)
@ -647,15 +660,12 @@ func transferNEP(ctx *cli.Context, standard string) error {
}
toFlag := ctx.Generic("to").(*flags.Address)
if !toFlag.IsSet {
return cli.NewExitError(errors.New("missing receiver address (--to)"), 1)
}
to := toFlag.Uint160()
token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard)
if err != nil {
token, err = getMatchingTokenRPC(ctx, c, from, ctx.String("token"), standard)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
return cli.Exit(fmt.Errorf("can't fetch matching token from RPC-node: %w", err), 1)
}
}
@ -663,7 +673,7 @@ func transferNEP(ctx *cli.Context, standard string) error {
amount, err := fixedn.FromString(amountArg, int(token.Decimals))
// It's OK for NEP-11 transfer to not have amount set.
if err != nil && (standard == manifest.NEP17StandardName || amountArg != "") {
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid amount: %w", err), 1)
}
switch standard {
case manifest.NEP17StandardName:
@ -672,11 +682,11 @@ func transferNEP(ctx *cli.Context, standard string) error {
case manifest.NEP11StandardName:
tokenID := ctx.String("id")
if tokenID == "" {
return cli.NewExitError(errors.New("token ID should be specified"), 1)
return cli.Exit(errors.New("token ID should be specified"), 1)
}
tokenIDBytes, terr := hex.DecodeString(tokenID)
if terr != nil {
return cli.NewExitError(fmt.Errorf("invalid token ID: %w", terr), 1)
return cli.Exit(fmt.Errorf("invalid token ID: %w", terr), 1)
}
if amountArg == "" {
n11 := nep11.NewNonDivisible(act, token.Hash)
@ -686,10 +696,10 @@ func transferNEP(ctx *cli.Context, standard string) error {
tx, err = n11.TransferDUnsigned(act.Sender(), to, amount, tokenIDBytes, data)
}
default:
return cli.NewExitError(fmt.Errorf("unsupported token standard %s", standard), 1)
return cli.Exit(fmt.Errorf("unsupported token standard %s", standard), 1)
}
if err != nil {
return cli.NewExitError(fmt.Errorf("can't make transaction: %w", err), 1)
return cli.Exit(fmt.Errorf("can't make transaction: %w", err), 1)
}
return txctx.SignAndSend(ctx, act, acc, tx)

View file

@ -12,15 +12,15 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
func newValidatorCommands() []cli.Command {
return []cli.Command{
func newValidatorCommands() []*cli.Command {
return []*cli.Command{
{
Name: "register",
Usage: "register as a new candidate",
UsageText: "register -w <path> -r <rpc> -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
Usage: "Register as a new candidate",
UsageText: "register -w <path> -r <rpc> [-s timeout] -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
Action: handleRegister,
Flags: append([]cli.Flag{
walletPathFlag,
@ -30,16 +30,18 @@ func newValidatorCommands() []cli.Command {
txctx.OutFlag,
txctx.ForceFlag,
txctx.AwaitFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "Address to register",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Required: true,
Usage: "Address to register",
},
}, options.RPC...),
},
{
Name: "unregister",
Usage: "unregister self as a candidate",
UsageText: "unregister -w <path> -r <rpc> -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
Usage: "Unregister self as a candidate",
UsageText: "unregister -w <path> -r <rpc> [-s timeout] -a <addr> [-g gas] [-e sysgas] [--out file] [--force] [--await]",
Action: handleUnregister,
Flags: append([]cli.Flag{
walletPathFlag,
@ -49,15 +51,17 @@ func newValidatorCommands() []cli.Command {
txctx.OutFlag,
txctx.ForceFlag,
txctx.AwaitFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "Address to unregister",
&flags.AddressFlag{
Name: "address",
Required: true,
Aliases: []string{"a"},
Usage: "Address to unregister",
},
}, options.RPC...),
},
{
Name: "vote",
Usage: "vote for a validator",
Usage: "Vote for a validator",
UsageText: "vote -w <path> -r <rpc> [-s <timeout>] [-g gas] [-e sysgas] -a <addr> [-c <public key>] [--out file] [--force] [--await]",
Description: `Votes for a validator by calling "vote" method of a NEO native
contract. Do not provide candidate argument to perform unvoting. If --await flag is
@ -72,13 +76,16 @@ func newValidatorCommands() []cli.Command {
txctx.OutFlag,
txctx.ForceFlag,
txctx.AwaitFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "Address to vote from",
&flags.AddressFlag{
Name: "address",
Required: true,
Aliases: []string{"a"},
Usage: "Address to vote from",
},
cli.StringFlag{
Name: "candidate, c",
Usage: "Public key of candidate to vote for",
&cli.StringFlag{
Name: "candidate",
Aliases: []string{"c"},
Usage: "Public key of candidate to vote for",
},
}, options.RPC...),
},
@ -103,18 +110,15 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
}
wall, pass, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
addrFlag := ctx.Generic("address").(*flags.Address)
if !addrFlag.IsSet {
return cli.NewExitError("address was not provided", 1)
}
addr := addrFlag.Uint160()
acc, err := options.GetUnlockedAccount(wall, addr, pass)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -122,7 +126,7 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
signers, err := cmdargs.GetSignersAccounts(acc, wall, nil, transaction.CalledByEntry)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
return cli.Exit(fmt.Errorf("invalid signers: %w", err), 1)
}
_, act, exitErr := options.GetRPCWithActor(gctx, ctx, signers)
if exitErr != nil {
@ -132,7 +136,7 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
contract := neo.New(act)
tx, err := mkTx(contract, addr, acc)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return txctx.SignAndSend(ctx, act, acc, tx)
}

View file

@ -8,6 +8,7 @@ import (
"io"
"math/big"
"os"
"slices"
"strings"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
@ -24,7 +25,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"github.com/urfave/cli/v2"
)
const (
@ -47,38 +48,43 @@ var (
)
var (
walletPathFlag = cli.StringFlag{
Name: "wallet, w",
Usage: "Path to the wallet file ('-' to read from stdin); conflicts with --wallet-config flag.",
walletPathFlag = &cli.StringFlag{
Name: "wallet",
Aliases: []string{"w"},
Usage: "Path to the wallet file ('-' to read from stdin); conflicts with --wallet-config flag.",
}
walletConfigFlag = cli.StringFlag{
walletConfigFlag = &cli.StringFlag{
Name: "wallet-config",
Usage: "Path to the wallet config file; conflicts with --wallet flag.",
}
wifFlag = cli.StringFlag{
wifFlag = &cli.StringFlag{
Name: "wif",
Usage: "WIF to import",
}
decryptFlag = cli.BoolFlag{
Name: "decrypt, d",
Usage: "Decrypt encrypted keys.",
decryptFlag = &cli.BoolFlag{
Name: "decrypt",
Aliases: []string{"d"},
Usage: "Decrypt encrypted keys.",
}
inFlag = cli.StringFlag{
Name: "in",
Usage: "file with JSON transaction",
inFlag = &cli.StringFlag{
Name: "in",
Required: true,
Usage: "File with JSON transaction",
Action: cmdargs.EnsureNotEmpty("in"),
}
fromAddrFlag = flags.AddressFlag{
fromAddrFlag = &flags.AddressFlag{
Name: "from",
Usage: "Address to send an asset from",
}
toAddrFlag = flags.AddressFlag{
Name: "to",
Usage: "Address to send an asset to",
toAddrFlag = &flags.AddressFlag{
Name: "to",
Usage: "Address to send an asset to",
Required: true,
}
)
// NewCommands returns 'wallet' command.
func NewCommands() []cli.Command {
func NewCommands() []*cli.Command {
claimFlags := []cli.Flag{
walletPathFlag,
walletConfigFlag,
@ -87,9 +93,11 @@ func NewCommands() []cli.Command {
txctx.OutFlag,
txctx.ForceFlag,
txctx.AwaitFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "Address to claim GAS for",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Required: true,
Usage: "Address to claim GAS for",
},
}
claimFlags = append(claimFlags, options.RPC...)
@ -99,67 +107,78 @@ func NewCommands() []cli.Command {
txctx.OutFlag,
txctx.AwaitFlag,
inFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "Address to use",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to use",
},
}
signFlags = append(signFlags, options.RPC...)
return []cli.Command{{
// By default, RPC flag is required. signtx may be called without provided rpc-endpoint.
rpcFlagOriginal, _ := options.RPC[0].(*cli.StringFlag)
rpcFlag := *rpcFlagOriginal
rpcFlag.Required = false
signFlags = append(signFlags, &rpcFlag)
signFlags = append(signFlags, options.RPC[1:]...)
return []*cli.Command{{
Name: "wallet",
Usage: "create, open and manage a Neo wallet",
Subcommands: []cli.Command{
Usage: "Create, open and manage a Neo wallet",
Subcommands: []*cli.Command{
{
Name: "claim",
Usage: "claim GAS",
Usage: "Claim GAS",
UsageText: "neo-go wallet claim -w wallet [--wallet-config path] [-g gas] [-e sysgas] -a address -r endpoint [-s timeout] [--out file] [--force] [--await]",
Action: claimGas,
Flags: claimFlags,
},
{
Name: "init",
Usage: "create a new wallet",
Usage: "Create a new wallet",
UsageText: "neo-go wallet init -w wallet [--wallet-config path] [-a]",
Action: createWallet,
Flags: []cli.Flag{
walletPathFlag,
walletConfigFlag,
cli.BoolFlag{
Name: "account, a",
Usage: "Create a new account",
&cli.BoolFlag{
Name: "account",
Aliases: []string{"a"},
Usage: "Create a new account",
},
},
},
{
Name: "change-password",
Usage: "change password for accounts",
UsageText: "neo-go wallet change-password -w wallet -a address",
Usage: "Change password for accounts",
UsageText: "neo-go wallet change-password -w wallet [-a address]",
Action: changePassword,
Flags: []cli.Flag{
walletPathFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "address to change password for",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to change password for",
},
},
},
{
Name: "convert",
Usage: "convert addresses from existing Neo Legacy NEP6-wallet to Neo N3 format",
Usage: "Convert addresses from existing Neo Legacy NEP6-wallet to Neo N3 format",
UsageText: "neo-go wallet convert -w legacywallet [--wallet-config path] -o n3wallet",
Action: convertWallet,
Flags: []cli.Flag{
walletPathFlag,
walletConfigFlag,
cli.StringFlag{
Name: "out, o",
Usage: "where to write converted wallet",
&cli.StringFlag{
Name: "out",
Aliases: []string{"o"},
Required: true,
Usage: "Where to write converted wallet",
Action: cmdargs.EnsureNotEmpty("out"),
},
},
},
{
Name: "create",
Usage: "add an account to the existing wallet",
Usage: "Add an account to the existing wallet",
UsageText: "neo-go wallet create -w wallet [--wallet-config path]",
Action: addAccount,
Flags: []cli.Flag{
@ -169,7 +188,7 @@ func NewCommands() []cli.Command {
},
{
Name: "dump",
Usage: "check and dump an existing Neo wallet",
Usage: "Check and dump an existing Neo wallet",
UsageText: "neo-go wallet dump -w wallet [--wallet-config path] [-d]",
Description: `Prints the given wallet (via -w option or via wallet configuration file) in JSON
format to the standard output. If -d is given, private keys are unencrypted and
@ -185,21 +204,22 @@ func NewCommands() []cli.Command {
},
{
Name: "dump-keys",
Usage: "dump public keys for account",
Usage: "Dump public keys for account",
UsageText: "neo-go wallet dump-keys -w wallet [--wallet-config path] [-a address]",
Action: dumpKeys,
Flags: []cli.Flag{
walletPathFlag,
walletConfigFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "address to print public keys for",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Address to print public keys for",
},
},
},
{
Name: "export",
Usage: "export keys for address",
Usage: "Export keys for address",
UsageText: "export -w wallet [--wallet-config path] [--decrypt] [<address>]",
Description: `Prints the key for the given account to the standard output. It uses NEP-2
encrypted format by default (the way NEP-6 wallets store it) or WIF format if
@ -216,18 +236,19 @@ func NewCommands() []cli.Command {
},
{
Name: "import",
Usage: "import WIF of a standard signature contract",
Usage: "Import WIF of a standard signature contract",
UsageText: "import -w wallet [--wallet-config path] --wif <wif> [--name <account_name>]",
Action: importWallet,
Flags: []cli.Flag{
walletPathFlag,
walletConfigFlag,
wifFlag,
cli.StringFlag{
Name: "name, n",
Usage: "Optional account name",
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: "Optional account name",
},
cli.StringFlag{
&cli.StringFlag{
Name: "contract",
Usage: "Verification script for custom contracts",
},
@ -235,7 +256,7 @@ func NewCommands() []cli.Command {
},
{
Name: "import-multisig",
Usage: "import multisig contract",
Usage: "Import multisig contract",
UsageText: "import-multisig -w wallet [--wallet-config path] [--wif <wif>] [--name <account_name>] --min <m>" +
" [<pubkey1> [<pubkey2> [...]]]",
Description: `Imports a standard multisignature contract with "m out of n" signatures required where "m" is
@ -250,53 +271,60 @@ func NewCommands() []cli.Command {
walletPathFlag,
walletConfigFlag,
wifFlag,
cli.StringFlag{
Name: "name, n",
Usage: "Optional account name",
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: "Optional account name",
},
cli.IntFlag{
Name: "min, m",
Usage: "Minimal number of signatures",
&cli.IntFlag{
Name: "min",
Aliases: []string{"m"},
Usage: "Minimal number of signatures",
},
},
},
{
Name: "import-deployed",
Usage: "import deployed contract",
UsageText: "import-deployed -w wallet [--wallet-config path] --wif <wif> --contract <hash> [--name <account_name>]",
Usage: "Import deployed contract",
UsageText: "import-deployed -w wallet [--wallet-config path] --wif <wif> --contract <hash> --rpc-endpoint <endpoint> [-s <timeout>] [--name <account_name>]",
Action: importDeployed,
Flags: append([]cli.Flag{
walletPathFlag,
walletConfigFlag,
wifFlag,
cli.StringFlag{
Name: "name, n",
Usage: "Optional account name",
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: "Optional account name",
},
flags.AddressFlag{
Name: "contract, c",
Usage: "Contract hash or address",
&flags.AddressFlag{
Name: "contract",
Aliases: []string{"c"},
Required: true,
Usage: "Contract hash or address",
},
}, options.RPC...),
},
{
Name: "remove",
Usage: "remove an account from the wallet",
Usage: "Remove an account from the wallet",
UsageText: "remove -w wallet [--wallet-config path] [--force] --address <addr>",
Action: removeAccount,
Flags: []cli.Flag{
walletPathFlag,
walletConfigFlag,
txctx.ForceFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "Account address or hash in LE form to be removed",
&flags.AddressFlag{
Name: "address",
Aliases: []string{"a"},
Required: true,
Usage: "Account address or hash in LE form to be removed",
},
},
},
{
Name: "sign",
Usage: "cosign transaction with multisig/contract/additional account",
Usage: "Cosign transaction with multisig/contract/additional account",
UsageText: "sign -w wallet [--wallet-config path] --address <address> --in <file.in> [--out <file.out>] [-r <endpoint>] [--await]",
Description: `Signs the given (in file.in) context (which must be a transaction
signing context) for the given address using the given wallet. This command can
@ -312,7 +340,7 @@ func NewCommands() []cli.Command {
},
{
Name: "strip-keys",
Usage: "remove private keys for all accounts",
Usage: "Remove private keys for all accounts",
UsageText: "neo-go wallet strip-keys -w wallet [--wallet-config path] [--force]",
Description: `Removes private keys for all accounts from the given wallet. Notice,
this is a very dangerous action (you can lose keys if you don't have a wallet
@ -329,17 +357,17 @@ func NewCommands() []cli.Command {
},
{
Name: "nep17",
Usage: "work with NEP-17 contracts",
Usage: "Work with NEP-17 contracts",
Subcommands: newNEP17Commands(),
},
{
Name: "nep11",
Usage: "work with NEP-11 contracts",
Usage: "Work with NEP-11 contracts",
Subcommands: newNEP11Commands(),
},
{
Name: "candidate",
Usage: "work with candidates",
Usage: "Work with candidates",
Subcommands: newValidatorCommands(),
},
},
@ -358,24 +386,24 @@ func changePassword(ctx *cli.Context) error {
}
wall, _, err := openWallet(ctx, false)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
if len(wall.Accounts) == 0 {
return cli.NewExitError("wallet has no accounts", 1)
return cli.Exit("wallet has no accounts", 1)
}
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
// Check for account presence first before asking for password.
acc := wall.GetAccount(addrFlag.Uint160())
if acc == nil {
return cli.NewExitError("account is missing", 1)
return cli.Exit("account is missing", 1)
}
}
oldPass, err := input.ReadPassword(EnterOldPasswordPrompt)
if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading old password: %w", err), 1)
return cli.Exit(fmt.Errorf("error reading old password: %w", err), 1)
}
for i := range wall.Accounts {
@ -384,13 +412,13 @@ func changePassword(ctx *cli.Context) error {
}
err := wall.Accounts[i].Decrypt(oldPass, wall.Scrypt)
if err != nil {
return cli.NewExitError(fmt.Errorf("unable to decrypt account %s: %w", wall.Accounts[i].Address, err), 1)
return cli.Exit(fmt.Errorf("unable to decrypt account %s: %w", wall.Accounts[i].Address, err), 1)
}
}
pass, err := readNewPassword()
if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading new password: %w", err), 1)
return cli.Exit(fmt.Errorf("error reading new password: %w", err), 1)
}
for i := range wall.Accounts {
if addrFlag.IsSet && wall.Accounts[i].Address != addrFlag.String() {
@ -398,12 +426,12 @@ func changePassword(ctx *cli.Context) error {
}
err := wall.Accounts[i].Encrypt(pass, wall.Scrypt)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
}
err = wall.Save()
if err != nil {
return cli.NewExitError(fmt.Errorf("Error saving the wallet: %w", err), 1)
return cli.Exit(fmt.Errorf("error saving the wallet: %w", err), 1)
}
return nil
}
@ -414,16 +442,13 @@ func convertWallet(ctx *cli.Context) error {
}
wall, pass, err := newWalletV2FromFile(ctx.String("wallet"), ctx.String("wallet-config"))
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
out := ctx.String("out")
if len(out) == 0 {
return cli.NewExitError("missing out path", 1)
}
newWallet, err := wallet.NewWallet(out)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
newWallet.Scrypt = wall.Scrypt
@ -431,19 +456,19 @@ func convertWallet(ctx *cli.Context) error {
if len(wall.Accounts) != 1 || pass == nil {
password, err := input.ReadPassword(fmt.Sprintf("Enter password for account %s (label '%s') > ", acc.Address, acc.Label))
if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading password: %w", err), 1)
return cli.Exit(fmt.Errorf("error reading password: %w", err), 1)
}
pass = &password
}
newAcc, err := acc.convert(*pass, wall.Scrypt)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
newWallet.AddAccount(newAcc)
}
if err := newWallet.Save(); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return nil
}
@ -454,12 +479,12 @@ func addAccount(ctx *cli.Context) error {
}
wall, pass, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
if err := createAccount(wall, pass); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return nil
@ -468,7 +493,7 @@ func addAccount(ctx *cli.Context) error {
func exportKeys(ctx *cli.Context) error {
wall, pass, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
@ -476,28 +501,25 @@ func exportKeys(ctx *cli.Context) error {
decrypt := ctx.Bool("decrypt")
if ctx.NArg() == 0 && decrypt {
return cli.NewExitError(errors.New("address must be provided if '--decrypt' flag is used"), 1)
return cli.Exit(errors.New("address must be provided if '--decrypt' flag is used"), 1)
} else if ctx.NArg() > 0 {
// check address format just to catch possible typos
addr = ctx.Args().First()
_, err := address.StringToUint160(addr)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't parse address: %w", err), 1)
return cli.Exit(fmt.Errorf("can't parse address: %w", err), 1)
}
}
var wifs []string
loop:
for _, a := range wall.Accounts {
if addr != "" && a.Address != addr {
continue
}
for i := range wifs {
if a.EncryptedWIF == wifs[i] {
continue loop
}
if slices.Contains(wifs, a.EncryptedWIF) {
continue
}
wifs = append(wifs, a.EncryptedWIF)
@ -508,14 +530,14 @@ loop:
if pass == nil {
password, err := input.ReadPassword(EnterPasswordPrompt)
if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading password: %w", err), 1)
return cli.Exit(fmt.Errorf("error reading password: %w", err), 1)
}
pass = &password
}
pk, err := keys.NEP2Decrypt(wif, *pass, wall.Scrypt)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
wif = pk.WIF()
@ -536,22 +558,22 @@ func importMultisig(ctx *cli.Context) error {
wall, pass, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
m := ctx.Int("min")
if ctx.NArg() < m {
return cli.NewExitError(errors.New("insufficient number of public keys"), 1)
return cli.Exit(errors.New("insufficient number of public keys"), 1)
}
args := []string(ctx.Args())
args := ctx.Args().Slice()
pubs := make([]*keys.PublicKey, len(args))
for i := range args {
pubs[i], err = keys.NewPublicKeyFromString(args[i])
if err != nil {
return cli.NewExitError(fmt.Errorf("can't decode public key %d: %w", i, err), 1)
return cli.Exit(fmt.Errorf("can't decode public key %d: %w", i, err), 1)
}
}
@ -579,31 +601,31 @@ loop:
if acc != nil {
err = acc.ConvertMultisigEncrypted(accPub, m, pubs)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if label != nil {
acc.Label = *label
}
if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return nil
}
if !ctx.IsSet("wif") {
return cli.NewExitError(errors.New("none of the provided public keys correspond to an existing key in the wallet or multiple matching accounts found in the wallet, and no WIF is provided"), 1)
return cli.Exit(errors.New("none of the provided public keys correspond to an existing key in the wallet or multiple matching accounts found in the wallet, and no WIF is provided"), 1)
}
acc, err = newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if err := acc.ConvertMultisig(m, pubs); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return nil
@ -615,14 +637,11 @@ func importDeployed(ctx *cli.Context) error {
}
wall, pass, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
rawHash := ctx.Generic("contract").(*flags.Address)
if !rawHash.IsSet {
return cli.NewExitError("contract hash was not provided", 1)
}
var label *string
if ctx.IsSet("name") {
@ -631,7 +650,7 @@ func importDeployed(ctx *cli.Context) error {
}
acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
gctx, cancel := options.GetTimeoutContext(ctx)
@ -639,19 +658,20 @@ func importDeployed(ctx *cli.Context) error {
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
cs, err := c.GetContractStateByHash(rawHash.Uint160())
if err != nil {
return cli.NewExitError(fmt.Errorf("can't fetch contract info: %w", err), 1)
return cli.Exit(fmt.Errorf("can't fetch contract info: %w", err), 1)
}
md := cs.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
if md == nil || md.ReturnType != smartcontract.BoolType {
return cli.NewExitError("contract has no `verify` method with boolean return", 1)
return cli.Exit("contract has no `verify` method with boolean return", 1)
}
acc.Address = address.Uint160ToString(cs.Hash)
acc.Contract.Script = cs.NEF.Script
// Explicitly overwrite single signature script of the provided WIF since the contract is known to be deployed.
acc.Contract.Script = nil
acc.Contract.Parameters = acc.Contract.Parameters[:0]
for _, p := range md.Parameters {
acc.Contract.Parameters = append(acc.Contract.Parameters, wallet.ContractParam{
@ -662,7 +682,7 @@ func importDeployed(ctx *cli.Context) error {
acc.Contract.Deployed = true
if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return nil
@ -674,7 +694,7 @@ func importWallet(ctx *cli.Context) error {
}
wall, pass, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
@ -686,19 +706,19 @@ func importWallet(ctx *cli.Context) error {
acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt, label, pass)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if ctrFlag := ctx.String("contract"); ctrFlag != "" {
ctr, err := hex.DecodeString(ctrFlag)
if err != nil {
return cli.NewExitError("invalid contract", 1)
return cli.Exit("invalid contract", 1)
}
acc.Contract.Script = ctr
}
if err := addAccountAndSave(wall, acc); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
return nil
@ -710,17 +730,14 @@ func removeAccount(ctx *cli.Context) error {
}
wall, _, err := openWallet(ctx, true)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
addr := ctx.Generic("address").(*flags.Address)
if !addr.IsSet {
return cli.NewExitError("valid account address must be provided", 1)
}
acc := wall.GetAccount(addr.Uint160())
if acc == nil {
return cli.NewExitError("account wasn't found", 1)
return cli.Exit("account wasn't found", 1)
}
if !ctx.Bool("force") {
@ -731,10 +748,10 @@ func removeAccount(ctx *cli.Context) error {
}
if err := wall.RemoveAccount(acc.Address); err != nil {
return cli.NewExitError(fmt.Errorf("error on remove: %w", err), 1)
return cli.Exit(fmt.Errorf("error on remove: %w", err), 1)
}
if err := wall.Save(); err != nil {
return cli.NewExitError(fmt.Errorf("error while saving wallet: %w", err), 1)
return cli.Exit(fmt.Errorf("error while saving wallet: %w", err), 1)
}
return nil
}
@ -757,14 +774,14 @@ func dumpWallet(ctx *cli.Context) error {
}
wall, pass, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
if ctx.Bool("decrypt") {
if pass == nil {
password, err := input.ReadPassword(EnterPasswordPrompt)
if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading password: %w", err), 1)
return cli.Exit(fmt.Errorf("error reading password: %w", err), 1)
}
pass = &password
}
@ -772,7 +789,7 @@ func dumpWallet(ctx *cli.Context) error {
// Just testing the decryption here.
err := wall.Accounts[i].Decrypt(*pass, wall.Scrypt)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
}
}
@ -786,7 +803,7 @@ func dumpKeys(ctx *cli.Context) error {
}
wall, _, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
accounts := wall.Accounts
@ -795,7 +812,7 @@ func dumpKeys(ctx *cli.Context) error {
if addrFlag.IsSet {
acc := wall.GetAccount(addrFlag.Uint160())
if acc == nil {
return cli.NewExitError("account is missing", 1)
return cli.Exit("account is missing", 1)
}
accounts = []*wallet.Account{acc}
}
@ -825,7 +842,7 @@ func dumpKeys(ctx *cli.Context) error {
continue
}
if addrFlag.IsSet {
return cli.NewExitError(fmt.Errorf("unknown script type for address %s", address.Uint160ToString(addrFlag.Uint160())), 1)
return cli.Exit(fmt.Errorf("unknown script type for address %s", address.Uint160ToString(addrFlag.Uint160())), 1)
}
}
return nil
@ -837,7 +854,7 @@ func stripKeys(ctx *cli.Context) error {
}
wall, _, err := readWallet(ctx)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
if !ctx.Bool("force") {
@ -850,7 +867,7 @@ func stripKeys(ctx *cli.Context) error {
a.EncryptedWIF = ""
}
if err := wall.Save(); err != nil {
return cli.NewExitError(fmt.Errorf("error while saving wallet: %w", err), 1)
return cli.Exit(fmt.Errorf("error while saving wallet: %w", err), 1)
}
return nil
}
@ -866,28 +883,28 @@ func createWallet(ctx *cli.Context) error {
return errConflictingWalletFlags
}
if len(path) == 0 && len(configPath) == 0 {
return cli.NewExitError(errNoPath, 1)
return cli.Exit(errNoPath, 1)
}
var pass *string
if len(configPath) != 0 {
cfg, err := options.ReadWalletConfig(configPath)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
path = cfg.Path
pass = &cfg.Password
}
wall, err := wallet.NewWallet(path)
if err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if err := wall.Save(); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
if ctx.Bool("account") {
if err := createAccount(wall, pass); err != nil {
return cli.NewExitError(err, 1)
return cli.Exit(err, 1)
}
defer wall.Close()
}
@ -948,14 +965,14 @@ func createAccount(wall *wallet.Wallet, pass *string) error {
func openWallet(ctx *cli.Context, canUseWalletConfig bool) (*wallet.Wallet, *string, error) {
path, pass, err := getWalletPathAndPass(ctx, canUseWalletConfig)
if err != nil {
return nil, nil, cli.NewExitError(fmt.Errorf("failed to get wallet path or password: %w", err), 1)
return nil, nil, cli.Exit(fmt.Errorf("failed to get wallet path or password: %w", err), 1)
}
if path == "-" {
return nil, nil, errNoStdin
}
w, err := wallet.NewWalletFromFile(path)
if err != nil {
return nil, nil, cli.NewExitError(fmt.Errorf("failed to read wallet: %w", err), 1)
return nil, nil, cli.Exit(fmt.Errorf("failed to read wallet: %w", err), 1)
}
return w, pass, nil
}

View file

@ -40,10 +40,10 @@ func TestWalletAccountRemove(t *testing.T) {
require.NoError(t, err)
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "remove")
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "remove")
})
t.Run("missing address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "remove", "--wallet", walletPath)
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "remove", "--wallet", walletPath)
})
t.Run("invalid address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "remove", "--wallet", walletPath,
@ -109,7 +109,7 @@ func TestWalletChangePassword(t *testing.T) {
e.In.WriteString("pass\r")
e.In.WriteString("pass1\r")
e.In.WriteString("pass2\r")
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath)
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath, "--address", addr1)
})
t.Run("good, multiaccount", func(t *testing.T) {
e.In.WriteString("pass\r")
@ -593,7 +593,7 @@ func TestWalletClaimGas(t *testing.T) {
"--address", testcli.TestWalletAccount)
})
t.Run("missing address", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "claim",
e.RunWithErrorCheck(t, `Required flag "address" not set`, "neo-go", "wallet", "claim",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.TestWalletPath)
})
@ -605,7 +605,7 @@ func TestWalletClaimGas(t *testing.T) {
})
t.Run("missing endpoint", func(t *testing.T) {
e.In.WriteString("testpass\r")
e.RunWithError(t, "neo-go", "wallet", "claim",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "wallet", "claim",
"--wallet", testcli.TestWalletPath,
"--address", testcli.TestWalletAccount)
})
@ -711,19 +711,19 @@ func TestWalletImportDeployed(t *testing.T) {
require.NoError(t, err)
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "import-deployed")
e.RunWithErrorCheck(t, `Required flag "contract" not set`, "neo-go", "wallet", "import-deployed", "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("missing contract sh", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath)
e.RunWithErrorCheck(t, `Required flag "contract" not set`, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath, "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("missing WIF", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath, "--contract", h.StringLE())
"--wallet", walletPath, "--contract", h.StringLE(), "--rpc-endpoint", "http://"+e.RPC.Addresses()[0])
})
t.Run("missing endpoint", func(t *testing.T) {
e.In.WriteString("acc\rpass\rpass\r")
e.RunWithError(t, "neo-go", "wallet", "import-deployed",
e.RunWithErrorCheck(t, `Required flag "rpc-endpoint" not set`, "neo-go", "wallet", "import-deployed",
"--wallet", walletPath, "--contract", h.StringLE(),
"--wif", priv.WIF())
})
@ -992,7 +992,7 @@ func TestOfflineSigning(t *testing.T) {
e.Run(t, "neo-go", "util", "sendtx",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
txPath, "--await")
"--await", txPath)
e.CheckAwaitableTxPersisted(t)
})
}
@ -1062,7 +1062,7 @@ func TestWalletDumpKeys(t *testing.T) {
e.CheckNextLine(t, pubRegex)
e.CheckNextLine(t, "^\\s*$")
e.CheckNextLine(t, "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq")
for i := 0; i < 4; i++ {
for range 4 {
e.CheckNextLine(t, pubRegex)
}
e.CheckNextLine(t, "^\\s*$")
@ -1085,7 +1085,7 @@ func TestWalletDumpKeys(t *testing.T) {
cmd := append(cmd, "-a", "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq")
e.Run(t, cmd...)
e.CheckNextLine(t, "3 out of 4 multisig contract")
for i := 0; i < 4; i++ {
for range 4 {
e.CheckNextLine(t, pubRegex)
}
e.CheckEOF(t)
@ -1107,11 +1107,11 @@ func TestWalletConvert(t *testing.T) {
outPath := filepath.Join(tmpDir, "wallet.json")
cmd := []string{"neo-go", "wallet", "convert"}
t.Run("missing wallet", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "out" not set`, cmd...)
})
cmd = append(cmd, "--wallet", "testdata/testwallet_NEO2.json")
t.Run("missing out path", func(t *testing.T) {
e.RunWithError(t, cmd...)
e.RunWithErrorCheck(t, `Required flag "out" not set`, cmd...)
})
t.Run("invalid out path", func(t *testing.T) {
dir := t.TempDir()

37
config/config_embed.go Normal file
View file

@ -0,0 +1,37 @@
// Package config contains embedded YAML configuration files for different network modes
// of the Neo N3 blockchain and for NeoFS mainnet and testnet networks.
package config
import (
_ "embed"
)
// MainNet is the Neo N3 mainnet configuration.
//
//go:embed protocol.mainnet.yml
var MainNet []byte
// TestNet is the Neo N3 testnet configuration.
//
//go:embed protocol.testnet.yml
var TestNet []byte
// PrivNet is the private network configuration.
//
//go:embed protocol.privnet.yml
var PrivNet []byte
// MainNetNeoFS is the mainnet NeoFS configuration.
//
//go:embed protocol.mainnet.neofs.yml
var MainNetNeoFS []byte
// TestNetNeoFS is the testnet NeoFS configuration.
//
//go:embed protocol.testnet.neofs.yml
var TestNetNeoFS []byte
// UnitTestNet is the unit test network configuration.
//
//go:embed protocol.unit_testnet.yml
var UnitTestNet []byte

View file

@ -1,6 +1,6 @@
ProtocolConfiguration:
Magic: 91414437
MaxTraceableBlocks: 2102400
MaxTraceableBlocks: 17280
InitialGASSupply: 52000000
TimePerBlock: 15s
MemPoolSize: 50000
@ -27,6 +27,7 @@ ProtocolConfiguration:
Aspidochelone: 3000000
Basilisk: 4500000
Cockatrice: 5800000
Domovoi: 5800000
ApplicationConfiguration:
SkipBlockVerification: false
@ -79,3 +80,19 @@ ApplicationConfiguration:
Enabled: false
Addresses:
- ":2113"
NeoFSBlockFetcher:
Enabled: true
Addresses:
- st1.storage.fs.neo.org:8080
- st2.storage.fs.neo.org:8080
- st3.storage.fs.neo.org:8080
- st4.storage.fs.neo.org:8080
Timeout: 10m
DownloaderWorkersCount: 500
OIDBatchSize: 8000
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "BP71MqY7nJhpuHfdQU3infRSjMgVmSFFt9GfG2GGMZJj"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -39,6 +39,7 @@ ProtocolConfiguration:
Aspidochelone: 1730000
Basilisk: 4120000
Cockatrice: 5450000
Domovoi: 5570000
ApplicationConfiguration:
SkipBlockVerification: false
@ -97,3 +98,19 @@ ApplicationConfiguration:
Enabled: false
Addresses:
- ":2113"
NeoFSBlockFetcher:
Enabled: true
Addresses:
- st1.storage.fs.neo.org:8080
- st2.storage.fs.neo.org:8080
- st3.storage.fs.neo.org:8080
- st4.storage.fs.neo.org:8080
Timeout: 10m
DownloaderWorkersCount: 500
OIDBatchSize: 8000
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "3RCdP3ZubyKyo8qFeo7EJPryidTZaGCMdUjqFJaaEKBV"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -1,6 +1,6 @@
ProtocolConfiguration:
Magic: 735783775
MaxTraceableBlocks: 2102400
MaxTraceableBlocks: 17280
InitialGASSupply: 52000000
TimePerBlock: 15s
MemPoolSize: 50000
@ -24,6 +24,11 @@ ProtocolConfiguration:
- morph7.t5.fs.neo.org:50333
VerifyTransactions: true
P2PSigExtensions: true
Hardforks:
Aspidochelone: 0
Basilisk: 0
Cockatrice: 0
Domovoi: 0
ApplicationConfiguration:
SkipBlockVerification: false
@ -82,3 +87,19 @@ ApplicationConfiguration:
Enabled: false
Addresses:
- ":2113"
NeoFSBlockFetcher:
Enabled: true
Addresses:
- st1.storage.fs.neo.org:8080
- st2.storage.fs.neo.org:8080
- st3.storage.fs.neo.org:8080
- st4.storage.fs.neo.org:8080
Timeout: 10m
DownloaderWorkersCount: 500
OIDBatchSize: 8000
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "98xz5YeanzxRCpH6EfUhECVm2MynGYchDN4naJViHT9M"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -42,6 +42,7 @@ ProtocolConfiguration:
Aspidochelone: 210000
Basilisk: 2680000
Cockatrice: 3967000
Domovoi: 4144000
ApplicationConfiguration:
SkipBlockVerification: false
@ -99,3 +100,19 @@ ApplicationConfiguration:
Enabled: false
Addresses:
- ":2113"
NeoFSBlockFetcher:
Enabled: true
Addresses:
- st1.storage.fs.neo.org:8080
- st2.storage.fs.neo.org:8080
- st3.storage.fs.neo.org:8080
- st4.storage.fs.neo.org:8080
Timeout: 10m
DownloaderWorkersCount: 500
OIDBatchSize: 8000
BQueueSize: 16000 # must be larger than OIDBatchSize; recommended to be 2*OIDBatchSize or 3*OIDBatchSize
SkipIndexFilesSearch: false
IndexFileSize: 128000
ContainerID: "A8nGtDemWrm2SjfcGAG6wvrxmXwqc5fwr8ezNDm6FraT"
BlockAttribute: "Block"
IndexFileAttribute: "Index"

View file

@ -18,7 +18,11 @@ ProtocolConfiguration:
VerifyTransactions: true
P2PSigExtensions: true
Hardforks:
Aspidochelone: 25
Aspidochelone: 3
Basilisk: 6
Cockatrice: 9
Domovoi: 12
Echidna: 13
ApplicationConfiguration:
SkipBlockVerification: false

View file

@ -33,6 +33,13 @@ a dialect of Go rather than a complete port of the language:
it's up to the programmer whether assert can be performed successfully.
* type aliases including the built-in `any` alias are supported.
* generics are not supported, but eventually will be (at least, partially), ref. https://github.com/nspcc-dev/neo-go/issues/2376.
* `~` token is not supported
* `comparable` is not supported
* arrays (`[4]byte`) are not supported (https://github.com/nspcc-dev/neo-go/issues/3524)
* `min()` and `max()` are not supported (https://github.com/nspcc-dev/neo-go/issues/3090)
* `clear()` is not supported (https://github.com/nspcc-dev/neo-go/issues/3091)
* ranging over integers in `for` is not supported (https://github.com/nspcc-dev/neo-go/issues/3525)
* `for` loop variables are treated in pre-Go 1.22 way: a single instance is created for the whole loop
## VM API (interop layer)
Compiler translates interop function calls into Neo VM syscalls or (for custom

View file

@ -155,7 +155,7 @@ four-node setup.
#### Prerequisites
- `docker` of version >= 20.10.0
- `docker-compose`
- `docker compose` V2
- `go` compiler
#### Instructions
@ -166,7 +166,7 @@ make env_up # start containers, use "make env_single" for single CN
```
To monitor logs:
```bash
docker-compose -f .docker/docker-compose.yml logs -f
docker compose -f .docker/docker-compose.yml logs -f
```
To stop:

126
docs/neofs-blockstorage.md Normal file
View file

@ -0,0 +1,126 @@
# NeoFS block storage
Using NeoFS to store chain's blocks and snapshots was proposed in
[#3463](https://github.com/neo-project/neo/issues/3463). NeoGo contains several
extensions utilizing NeoFS block storage aimed to improve node synchronization
efficiency and reduce node storage size.
## Components and functionality
### Block storage schema
A single NeoFS container is used to store blocks and index files. Each container
has network magic attribute (`Magic:56753`). Each block is stored in a binary
form as a separate object with a unique OID and a set of attributes:
- block object identifier with block index value (`Block:1`)
- primary node index (`Primary:0`)
- block hash in the LE form (`Hash:5412a781caf278c0736556c0e544c7cfdbb6e3c62ae221ef53646be89364566b`)
- previous block hash in the LE form (`PrevHash:3654a054d82a8178c7dfacecc2c57282e23468a42ee407f14506368afe22d929`)
- millisecond-precision block creation timestamp (`BlockTime:1627894840919`)
- second-precision block uploading timestamp (`Timestamp:1627894840`)
Each index file is an object containing a constant-sized batch of raw block object
IDs in binary form ordered by block index. Each index file is marked with the
following attributes:
- index file identifier with consecutive file index value (`Index:0`)
- the number of OIDs included into index file (`IndexSize:128000`)
- second-precision index file uploading timestamp (`Timestamp:1627894840`)
### NeoFS BlockFetcher
NeoFS BlockFetcher service is designed as an alternative to P2P synchronisation
protocol. It allows to download blocks from a trusted container in the NeoFS network
and persist them to database using standard verification flow. NeoFS BlockFetcher
service primarily used during the node's bootstrap, providing a fast alternative to
P2P blocks synchronisation.
NeoFS BlockFetcher service has two modes of operation:
- Index File Search: Search for index files, which contain batches of block object
IDs and fetch blocks from NeoFS by retrieved OIDs.
- Direct Block Search: Search and fetch blocks directly from NeoFS container via
built-in NeoFS object search mechanism.
Operation mode of BlockFetcher can be configured via `SkipIndexFilesSearch`
parameter.
#### Operation flow
1. **OID Fetching**:
Depending on the mode, the service either:
- Searches for index files by index file attribute and reads block OIDs from index
file object-by-object.
- Searches blocks one by one directly by block attribute.
Once the OIDs are retrieved, they are immediately redirected to the
block downloading routines for further processing. The channel that
is used to redirect block OIDs to downloading routines is buffered
to provide smooth OIDs delivery without delays. The size of this channel
can be configured via `OIDBatchSize` parameter and equals to `2*OIDBatchSize`.
2. **Parallel Block Downloading**:
The number of downloading routines can be configured via
`DownloaderWorkersCount` parameter. It's up to the user to find the
balance between the downloading speed and blocks persist speed for every
node that uses NeoFS BlockFetcher. Downloaded blocks are placed to the
block queue directly.
3. **Block Insertion**:
Downloaded blocks are inserted into the blockchain using the same logic
as in the P2P synchronisation protocol. The block queue is used to order
downloaded blocks before they are inserted into the blockchain. The
size of the queue can be configured via the `BQueueSize` parameter
and should be larger than the `OIDBatchSize` parameter to avoid blocking
the downloading routines.
Once all blocks available in the NeoFS container are processed, the service
shuts down automatically.
### 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
```
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.
2. Searches for the index files stored in NeoFS.
3. Searches for the stored blocks from the latest incomplete index file.
4. Fetches missing blocks from the RPC node and uploads them to the NeoFS container.
5. After uploading the blocks, it creates index file based on the uploaded block OIDs.
6. Uploads the created index file to the NeoFS container.
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.
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`), `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.
### 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,9 +18,10 @@ 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. |
| Oracle | [Oracle Configuration](#Oracle-Configuration) | | Oracle module configuration. See the [Oracle Configuration](#Oracle-Configuration) section for details. |
| P2P | [P2P Configuration](#P2P-Configuration) | | Configuration values for P2P network interaction. See the [P2P Configuration](#P2P-Configuration) section for details. |
| P2PNotary | [P2P Notary Configuration](#P2P-Notary-Configuration) | | P2P Notary module configuration. See the [P2P Notary Configuration](#P2P-Notary-Configuration) section for details. |
@ -29,10 +30,12 @@ node-related settings described in the table below.
| Relay | `bool` | `true` | Determines whether the server is forwarding its inventory. |
| Consensus | [Consensus Configuration](#Consensus-Configuration) | | Describes consensus (dBFT) configuration. See the [Consensus Configuration](#Consensus-Configuration) for details. |
| RemoveUntraceableBlocks | `bool`| `false` | Denotes whether old blocks should be removed from cache and database. If enabled, then only the last `MaxTraceableBlocks` are stored and accessible to smart contracts. Old MPT data is also deleted in accordance with `GarbageCollectionPeriod` setting. If enabled along with `P2PStateExchangeExtensions` protocol extension, then old blocks and MPT states will be removed up to the second latest state synchronisation point (see `StateSyncInterval`). |
| RemoveUntraceableHeaders | `bool`| `false` | Used only with RemoveUntraceableBlocks and makes node delete untraceable block headers as well. Notice that this is an experimental option, not recommended for production use. |
| RPC | [RPC Configuration](#RPC-Configuration) | | Describes [RPC subsystem](rpc.md) configuration. See the [RPC Configuration](#RPC-Configuration) for details. |
| 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
@ -98,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.
@ -153,6 +161,59 @@ where:
Please, refer to the [Notary module documentation](./notary.md#Notary node module) for
details on module features.
### NeoFS BlockFetcher Configuration
`NeoFSBlockFetcher` configuration section contains settings for NeoFS
BlockFetcher module and has the following structure:
```
NeoFSBlockFetcher:
Enabled: true
UnlockWallet:
Path: "./wallet.json"
Password: "pass"
Addresses:
- st1.storage.fs.neo.org:8080
- st2.storage.fs.neo.org:8080
- st3.storage.fs.neo.org:8080
- st4.storage.fs.neo.org:8080
Timeout: 10m
DownloaderWorkersCount: 500
OIDBatchSize: 8000
BQueueSize: 16000
SkipIndexFilesSearch: false
ContainerID: "7a1cn9LNmAcHjESKWxRGG7RSZ55YHJF6z2xDLTCuTZ6c"
BlockAttribute: "Block"
IndexFileAttribute: "Index"
IndexFileSize: 128000
```
where:
- `Enabled` enables NeoFS BlockFetcher module.
- `UnlockWallet` contains wallet settings to retrieve account to sign requests to
NeoFS. Without this setting, the module will use randomly generated private key.
For configuration details see [Unlock Wallet Configuration](#Unlock-Wallet-Configuration)
- `Addresses` is a list of NeoFS storage nodes addresses. This parameter is required.
- `Timeout` is a timeout for a single request to NeoFS storage node (10 minutes by
default).
- `ContainerID` is a container ID to fetch blocks from. This parameter is required.
- `BlockAttribute` is an attribute name of NeoFS object that contains block
data. It's set to `Block` by default.
- `IndexFileAttribute` is an attribute name of NeoFS index object that contains block
object IDs. It's set to `Index` by default.
- `DownloaderWorkersCount` is a number of workers that download blocks from
NeoFS in parallel (500 by default).
- `OIDBatchSize` is the number of blocks to search per a single request to NeoFS
in case of disabled index files search. Also, for both modes of BlockFetcher
operation this setting manages the buffer size of OIDs and blocks transferring
channels. By default, it's set to a half of `BQueueSize` parameter.
- `BQueueSize` is a size of the block queue used to manage consecutive blocks
addition to the chain. It must be larger than `OIDBatchSize` and highly recommended
to be `2*OIDBatchSize` or `3*OIDBatchSize`. By default, it's set to 16000.
- `SkipIndexFilesSearch` is a flag that allows to skip index files search and search
for blocks directly. It is set to `false` by default.
- `IndexFileSize` is the number of OID objects stored in the index files. This
setting depends on the NeoFS block storage configuration and is applicable only if
`SkipIndexFilesSearch` is set to `false`. It's set to 128000 by default.
### Metrics Services Configuration
Metrics services configuration describes options for metrics services (pprof,
@ -190,6 +251,7 @@ RPC:
MaxRequestBodyBytes: 5242880
MaxRequestHeaderBytes: 1048576
MaxWebSocketClients: 64
MaxWebSocketFeeds: 16
SessionEnabled: false
SessionExpirationTime: 15
SessionBackedByMPT: false
@ -235,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
@ -250,8 +315,8 @@ where:
enable `SessionBackedByMPT`, see `SessionBackedByMPT` documentation for more
details.
- `SessionExpirationTime` is a lifetime of iterator session in seconds. It is set
to `TimePerBlock` seconds by default and is relevant only if `SessionEnabled`
is set to `true`.
to `TimePerBlock` seconds (but not less than 5s) by default and is relevant
only if `SessionEnabled` is set to `true`.
- `SessionBackedByMPT` is a flag forcing JSON-RPC server into using MPT-backed
storage for delayed iterator traversal. If `true`, then iterator resources got
after `invoke*` calls will be released immediately. Further iterator traversing
@ -332,7 +397,7 @@ protocol-related settings described in the table below.
| --- | --- | --- | --- | --- |
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
| Genesis | [Genesis](#Genesis-Configuration) | none | The set of genesis block settings including NeoGo-specific protocol extensions that should be enabled at the genesis block or during native contracts initialisation. |
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>`Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>`Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. It also includes [#3085](https://github.com/nspcc-dev/neo-go/pull/3085) (ported from the [reference](https://github.com/neo-project/neo/pull/2810)) that enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. <br>`Cockatrice` represents hard-fork introduced in [#3402](https://github.com/nspcc-dev/neo-go/pull/3402) (ported from the [reference](https://github.com/neo-project/neo/pull/2942)). Initially it is introduced along with the ability to update native contracts. This hard-fork also includes a couple of new native smart contract APIs: `keccak256` of native CryptoLib contract introduced in [#3301](https://github.com/nspcc-dev/neo-go/pull/3301) (ported from the [reference](https://github.com/neo-project/neo/pull/2925)) and `getCommitteeAddress` of native NeoToken contract inctroduced in [#3362](https://github.com/nspcc-dev/neo-go/pull/3362) (ported from the [reference](https://github.com/neo-project/neo/pull/3154)). |
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known stable hard-fork is applied from the zero blockchain height". See [Hardforks](#Hardforks) section for a list of supported keys. |
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |
| MaxBlockSystemFee | `int64` | `900000000000` | Maximum overall transactions system fee per block. |
@ -403,3 +468,40 @@ where:
Note that `Transaction` is a NeoGo extension that isn't supported by the NeoC#
node and must be disabled on the public Neo N3 networks.
### Hardforks
The latest stable hardfork as per 0.107.1 release is Domovoi. Echidna is still
in development and can change in an incompatible way.
| Name | Changes | References |
| --- | --- | --- |
| `Aspidochelone` | Adjusts the price of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. Also adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the 3.2.0 protocol change | https://github.com/nspcc-dev/neo-go/pull/2469 <br> https://github.com/neo-project/neo/pull/2712 <br> https://github.com/nspcc-dev/neo-go/pull/2519 <br> https://github.com/neo-project/neo/pull/2749 <br> https://github.com/neo-project/neo/pull/2653 |
| `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` | 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
Real networks with large number of blocks require a substantial amount of time
to synchronize. When operating a number of node instances with similar
configurations you may want to save some resources by performing synchronization
on one node and then copying the DB over to other instances. In general, this
can be done and this is supported, but NeoGo has a lot of options that may
affect this:
- any differences in `ProtocolConfiguration` section make (or may make) databases
incompatible, except for `MemPoolSize`, `P2PNotaryRequestPayloadPoolSize`,
`SeedList`, `TimePerBlock`.
Protocol configuration is expected to be the same on all nodes of the same
network, so don't touch it unless you know what you're doing.
- DB types (Level/Bolt) must be the same
- `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
to copy it this way too.

View file

@ -16,7 +16,8 @@ Currently supported events:
Contents: transaction. Filters: sender and signer.
* notification generated during execution
Contents: container hash, contract hash, notification name, stack item. Filters: contract hash, notification name.
Contents: container hash, contract hash, notification name, stack item.
Filters: contract hash, notification name, notification parameters.
* transaction/persisting script executed
Contents: application execution result. Filters: VM state, script container hash.
@ -57,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).
@ -84,9 +88,15 @@ Recognized stream names:
format for one of transaction's `Signers`.
* `notification_from_execution`
Filter: `contract` field containing a string with hex-encoded Uint160 (LE
representation) and/or `name` field containing a string with execution
representation), `name` field containing a string with execution
notification name which should be a valid UTF-8 string not longer than
32 bytes.
32 bytes and/or `parameters` field containing an ordered array of structs
with `type` and `value` fields. Not more than 16 parameters are accepted.
Parameter's `type` must be not-a-complex type from the list: `Any`,
`Boolean`, `Integer`, `ByteArray`, `String`, `Hash160`, `Hash256`, `PublicKey`
or `Signature`. Filter that allows any parameter must be omitted or must
be `Any` typed with zero value. It is prohibited to have `parameters` be
filled with `Any` types only.
* `transaction_executed`
Filter: `state` field containing `HALT` or `FAULT` string for successful
and failed executions respectively and/or `container` field containing

View file

@ -207,6 +207,15 @@ the error-free C# response that provides a default result.
NeoGo can generate an error in response to an invalid proof, unlike
the error-free C# implementation.
##### `getPeers`
NeoGo extends the `getpeers` RPC call to return the user agent
(`useragent` JSON field) and last known block height
(`lastknownheight` JSON field) for each connected peer where available.
The last known block height field may be stale depending on the
PingInterval node config and the time since the last ping.
Ping behavior may also differ between node implementations.
### Unsupported methods
Methods listed below are not going to be supported for various reasons
@ -239,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
@ -347,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

@ -105,13 +105,13 @@ READY: loaded 36 instructions
To make it even more complete, you can directly load hex or base64 strings into the VM:
```
NEO-GO-VM > loadhex 54c56b006c766b00527ac46c766b00c391640b006203005a616c756662030000616c7566
READY: loaded 36 instructions
NEO-GO-VM > run
NEO-GO-VM > loadhex 0c0c48656c6c6f20776f726c6421
READY: loaded 14 instructions
NEO-GO-VM 0 > run
[
{
"value": 10,
"type": "BigInteger"
"type": "ByteString",
"value": "SGVsbG8gd29ybGQh"
}
]

View file

@ -25,8 +25,8 @@ See the table below for the detailed examples description.
| [engine](engine) | This contract demonstrates how to use `runtime` interop package which implements an API for `System.Runtime.*` Neo system calls. Please, refer to the `runtime` [package documentation](../pkg/interop/doc.go) for details. |
| [events](events) | The contract shows how execution notifications with the different arguments types can be sent with the help of `runtime.Notify` function of the `runtime` interop package. Please, refer to the `runtime.Notify` [function documentation](../pkg/interop/runtime/runtime.go) for details. |
| [iterator](iterator) | This example describes a way to work with Neo iterators. Please, refer to the `iterator` [package documentation](../pkg/interop/iterator/iterator.go) for details. |
| [nft-d](nft-d) | NEP-11 divisible NFT. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) for details. |
| [nft-nd](nft-nd) | NEP-11 non-divisible NFT. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) for details. |
| [nft-d](nft-d) | NEP-11 divisible NFT. This contract implements the NEP-11 and the NEP-24 token standards. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) and NEP-24 [specification](https://github.com/neo-project/proposals/blob/master/nep-24.mediawiki) for details. |
| [nft-nd](nft-nd) | NEP-11 non-divisible NFT. This contract implements the NEP-11 and the NEP-24 token standards. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) and NEP-24 [specification](https://github.com/neo-project/proposals/blob/master/nep-24.mediawiki) for details. |
| [nft-nd-nns](nft-nd-nns) | Neo Name Service contract which is NEP-11 non-divisible NFT. The contract implements methods for Neo domain name system managing such as domains registration/transferring, records addition and names resolving. The package also contains tests implemented with [neotest](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/neotest). |
| [oracle](oracle) | Oracle demo contract exposing two methods that you can use to process URLs. It uses oracle native contract, see [interop package documentation](../pkg/interop/native/oracle/oracle.go) also. |
| [runtime](runtime) | This contract demonstrates how to use special `_initialize` and `_deploy` methods. See the [compiler documentation](../docs/compiler.md#vm-api-interop-layer ) for methods details. It also shows the pattern for checking owner witness inside the contract with the help of `runtime.CheckWitness` interop [function](../pkg/interop/runtime/runtime.go). |

View file

@ -4,25 +4,25 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
// NotifyScriptContainer sends runtime notification with script container hash
// NotifyScriptContainer sends runtime notification with script container hash.
func NotifyScriptContainer() {
tx := runtime.GetScriptContainer()
runtime.Notify("Tx", tx.Hash)
}
// NotifyCallingScriptHash sends runtime notification with calling script hash
// NotifyCallingScriptHash sends runtime notification with calling script hash.
func NotifyCallingScriptHash() {
callingScriptHash := runtime.GetCallingScriptHash()
runtime.Notify("Calling", callingScriptHash)
}
// NotifyExecutingScriptHash sends runtime notification about executing script hash
// NotifyExecutingScriptHash sends runtime notification about executing script hash.
func NotifyExecutingScriptHash() {
execScriptHash := runtime.GetExecutingScriptHash()
runtime.Notify("Executing", execScriptHash)
}
// NotifyEntryScriptHash sends notification about entry script hash
// NotifyEntryScriptHash sends notification about entry script hash.
func NotifyEntryScriptHash() {
entryScriptHash := runtime.GetEntryScriptHash()
runtime.Notify("Entry", entryScriptHash)

View file

@ -1,5 +1,5 @@
module github.com/nspcc-dev/neo-go/examples/engine
go 1.20
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
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-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
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,5 +1,5 @@
module github.com/nspcc-dev/neo-go/examples/events
go 1.20
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
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-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
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,5 +1,5 @@
module github.com/nspcc-dev/neo-go/examples/iterator
go 1.20
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
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-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
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

@ -7,7 +7,7 @@ import (
)
// _deploy primes contract's storage with some data to be used later.
func _deploy(_ any, _ bool) {
func _deploy(_ any, _ bool) { // nolint: unused
ctx := storage.GetContext() // RW context.
storage.Put(ctx, "foo1", "1")
storage.Put(ctx, "foo2", "2")

View file

@ -1,5 +1,5 @@
module github.com/nspcc-dev/neo-go/examples/nft
go 1.20
go 1.22
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
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-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
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

@ -425,3 +425,43 @@ func Update(nef, manifest []byte) {
}
management.Update(nef, manifest)
}
// RoyaltyRecipient contains information about the recipient and the royalty amount.
type RoyaltyRecipient struct {
Address interop.Hash160
Amount int
}
// RoyaltyInfo returns a list of royalty recipients and the corresponding royalty amounts.
func RoyaltyInfo(tokenID []byte, royaltyToken interop.Hash160, salePrice int) []RoyaltyRecipient {
if salePrice <= 0 {
panic("sale price must be positive")
}
executingHash := runtime.GetExecutingScriptHash()
if !royaltyToken.Equals(executingHash) {
panic("invalid royalty token")
}
ctx := storage.GetReadOnlyContext()
if !isTokenValid(ctx, tokenID) {
panic("unknown token")
}
ownerIter := ownersOf(ctx, tokenID)
var owners []interop.Hash160
for iterator.Next(ownerIter) {
owners = append(owners, iterator.Value(ownerIter).(interop.Hash160))
}
var (
recipients []RoyaltyRecipient
amount = salePrice / 10 / len(owners)
)
for _, owner := range owners {
recipients = append(recipients, RoyaltyRecipient{
Address: owner,
Amount: amount,
})
}
return recipients
}

View file

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

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