Compare commits

..

422 commits

Author SHA1 Message Date
Roman Khimov
fae1d562e7
Merge pull request #2303 from nspcc-dev/fix-uint160-sort-order
util: reverse the order of uint160 comparsion
2021-12-07 15:52:01 +03:00
Roman Khimov
ef282e6cb7 util: reverse the order of uint160 comparsion
A follow-up to #2302, sort hashes using reverse order. Fixes
f9c68613f41ca85ddb01ec757153c0d86018d2324f6ff85a4afc708bb598b722 validation.

Hi, neo-project/neo#938.
2021-12-07 15:19:55 +03:00
Roman Khimov
673ea0db87
Merge pull request #2302 from nspcc-dev/dont-sort-witnesses
core: don't sort witnesses prior to check, fix #2301
2021-12-07 12:12:42 +03:00
Roman Khimov
82ecf8c4c9 core: don't sort witnesses prior to check, fix #2301
8537700b7b was a bit wrong in its witness
treatment, contract-based verification scripts have zero length and this
script has a hash different from the target script hash to check, so sorting
can break witnesses in some cases.
2021-12-06 21:17:52 +03:00
Roman Khimov
56c20ed817
Merge pull request #2176 from nspcc-dev/rpc/fix-getunclaimed
rpc: do not return error if account is not found for `getunclaimed` call
2021-09-14 14:47:35 +03:00
Anna Shaleva
233aa94bba rpc: do not return error if account is not found for getunclaimed call
Follow the reference behaviour.
2021-09-14 13:58:33 +03:00
Roman Khimov
580f1787b9
Merge pull request #2154 from nspcc-dev/remark14
wallet: add remark14 option to migrate UTXO assets to N3
2021-09-03 13:09:14 +03:00
Roman Khimov
29fec1c2ee wallet: add remark14 option to migrate UTXO assets to N3
N3 migration works via sends to blackhole ANeo2toNeo3MigrationAddressxwPB2Hz
address with Remark14 attribute set to target N3 address [1]. This option
allows to create such transaction (but it doesn't check target address in any
way, if you make a mistake there you'll lose your assets forever). It works
like this:

$ neo-go wallet transfer -p file.wallet -r https://rpc-node:10331 --from AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU --to ANeo2toNeo3MigrationAddressxwPB2Hz --asset NEO --amount 10 --remark14 NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq

[1]: https://docs.neo.org/tutorials/en-us/migrationB.html
2021-09-02 18:51:47 +03:00
Roman Khimov
554dd75a9a docs: add a note about docker hub 2021-08-13 16:15:20 +03:00
Roman Khimov
7588bf75c2 CHANGELOG: release v0.78.4 2021-08-12 22:57:36 +03:00
Roman Khimov
f38d62ab14
Merge pull request #2126 from nspcc-dev/prevent-inconsistent-exit-2.x
core: take an addblock lock on exit
2021-08-12 17:47:11 +03:00
Roman Khimov
92e3474f6e core: take an addblock lock on exit
Prevent this:

2021-08-12T15:24:00.750+0300    INFO    shutting down service   {"service": "Prometheus", "endpoint": ":2112"}
2021-08-12T15:24:00.752+0300    INFO    shutting down service   {"service": "Pprof", "endpoint": ":2113"}
2021-08-12T15:24:00.868+0300    INFO    blockchain persist completed    {"persistedBlocks": 14, "persistedKeys": 3145, "headerHeight": 7806078, "blockHeight": 7794173, "took": "652.243264ms"}
2021-08-12T15:24:00.974+0300    INFO    blockchain persist completed    {"persistedBlocks": 1, "persistedKeys": 259, "headerHeight": 7806078, "blockHeight": 7794174, "took": "221.955904ms"}
panic: assignment to entry in nil map

goroutine 132 [running]:
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).drop(...)
        github.com/nspcc-dev/neo-go/pkg/core/storage/memory_store.go:71
github.com/nspcc-dev/neo-go/pkg/core/storage.(*MemoryStore).Delete(0xc0002889c0, 0xc022642060, 0x21, 0x30, 0x21, 0xc022642060)
        github.com/nspcc-dev/neo-go/pkg/core/storage/memory_store.go:79 +0x9c
github.com/nspcc-dev/neo-go/pkg/core/mpt.(*Trie).updateRefCount(0xc00029cd50, 0x70945b6e05b4ce98, 0xb058a7450d307de5, 0x660ef5aff9146084, 0x1293434e970256e5, 0xc000000001)
        github.com/nspcc-dev/neo-go/pkg/core/mpt/trie.go:410 +0x257
github.com/nspcc-dev/neo-go/pkg/core/mpt.(*Trie).Flush(0xc00029cd50)
        github.com/nspcc-dev/neo-go/pkg/core/mpt/trie.go:370 +0x23a
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).storeBlock(0xc0002c6000, 0xc022476500, 0x0, 0xd6b100)
        github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:869 +0x3ac5
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).AddBlock(0xc0002c6000, 0xc022476500, 0x0, 0x0)
        github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:446 +0xeb
github.com/nspcc-dev/neo-go/pkg/network.(*blockQueue).run(0xc0001e1ad0)
        github.com/nspcc-dev/neo-go/pkg/network/blockqueue.go:48 +0x168
created by github.com/nspcc-dev/neo-go/pkg/network.(*Server).Start
        github.com/nspcc-dev/neo-go/pkg/network/server.go:183 +0x25d

Which then leads to broken DB.
2021-08-12 16:49:29 +03:00
Roman Khimov
b3a0a8f115
Merge pull request #2116 from nspcc-dev/fix-pings-2.x
network: fix Ping messages
2021-08-06 11:43:20 +03:00
Roman Khimov
c3175112fe network: fix Ping messages
* NewPing() accepts block index first and nonce then.
 * Block height should be used, it'll be important for state exchanging nodes
2021-08-06 11:31:31 +03:00
Roman Khimov
5d86176ec1
Merge pull request #2107 from nspcc-dev/gas-amount-2.x
core: turn off GAS generation at 8M height
2021-08-04 19:02:38 +03:00
Evgeniy Stratonikov
cb76757e6b core: turn off GAS generation at 8M height
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
2021-08-04 17:00:13 +03:00
Roman Khimov
0888dda6a2
Merge pull request #2020 from nspcc-dev/fix-infinite-loop-in-discoverer
Fix infinite loop in discoverer
2021-06-18 16:29:58 +03:00
Roman Khimov
38d14d4e3a network: drop requests to discovery pool when it can't be handled
It happens from time to time in a four-node private network where there are
seeds (aka CNs) and not a lot of other nodes to connect to.

I don't know how to test for an infinite loop that has no side-effects, so no
test added here.
2021-06-18 10:55:29 +03:00
Roman Khimov
9d6439bbe6 network: don't attempt to connect to the same node twice
We can have multiple copies of the same address in the pool and we should only
proceed to connect once per attempt.
2021-06-18 10:50:01 +03:00
Roman Khimov
46a59e904a network: don't register addresses before version handshake
1) It duplicates registration in `version` message handler and no valid
   connection can work without version exchange.
2) On public networks we have seed nodes defined by names, so we register
   connections to them using these names, but then if connection is dropped we
   delist them by IP:PORT combinations which can lead to zero PeerCount() with
   all seeds still being registered as connected in the discovery subsystem
   and thus no reconnection attempts being made.
2021-06-18 10:49:42 +03:00
Roman Khimov
33db3f76c0
Merge pull request #1910 from nspcc-dev/fix-zero-mpt-init-2.x
[2.x] Fix zero MPT init
2021-04-19 14:46:08 +03:00
Roman Khimov
36938462f1 go.sum: tidy 2021-04-19 12:43:06 +03:00
Roman Khimov
bcc2d1bb8f dao: don't treat zero stateroot as valid during MPT init
Because it's not really valid and MPT state should be the same as if it was
initialized at height 0. Fixes #1870
2021-04-19 12:39:43 +03:00
Roman Khimov
d8c8593410
Merge pull request #1901 from nspcc-dev/nep5-tracking-fixes
NEP5 tracking fixes
2021-04-12 17:04:55 +03:00
Roman Khimov
1202494479 state: reject too big transfers, fix #1900 2021-04-12 12:14:33 +03:00
Roman Khimov
663f10192f core: record transfers even if 'from' balance is 0
Transfer events should still be saved even if balance tracking goes wild.
2021-03-16 12:57:20 +03:00
Roman Khimov
4a2bdf5ddc
Merge pull request #1783 from nspcc-dev/allow-specifying-sysgas-for-invocations-2.x
[2.x] cli: allow specifying system fee via -s for invocations
2021-02-27 10:01:51 +03:00
Roman Khimov
483fefbb62 cli: allow specifying system fee via -s for invocations
Makes it possible to migrate a contract on Neo 2.
2021-02-26 14:52:45 +03:00
Roman Khimov
259522abde CHANGELOG: fix typo 2021-02-26 14:46:37 +03:00
Roman Khimov
39a2360448 CHANGELOG: release 0.78.3 2021-02-22 11:51:26 +03:00
Roman Khimov
3f50f90dc5
Merge pull request #1760 from nspcc-dev/rpc-compatibility-fixes-and-docs
[2.x] RPC compatibility (and other) fixes and docs
2021-02-19 12:30:59 +03:00
Roman Khimov
1179fdb44d
Merge pull request #1761 from nspcc-dev/backport-from-3.0
[2.x] Backport from 3.0
2021-02-19 09:11:27 +03:00
Roman Khimov
98580ae9b4 transaction: always JSONize all contract fields for Publish tx 2021-02-18 17:28:19 +03:00
Roman Khimov
7fc4f3c4ea transaction: always marshal all token's data for register tx
Fix missing precision and token type.
2021-02-18 17:28:19 +03:00
Roman Khimov
9d6a5dc26f result: fix confirmations count for blocks
It's +1, current block actually had `-1` value calcuated that transformed into
4294967295.
2021-02-18 17:28:19 +03:00
Roman Khimov
8571107741 core: bump DB version for NEP5 tracking data change
It's incompatible.
2021-02-18 17:28:19 +03:00
Roman Khimov
eb1986d2fc state: use big.Int for NEP5 balances and transfer amounts
In general, NEP5 contracts are not limited to int64. And we have an example
of pnWETH Flamingo token now (with 18 decimals) that easily overflows int64,
so for correctness we need to store big.Int.

And as TransferLog is shared for different purposes I've decided to not make
it variable-length on Neo 2.
2021-02-18 17:28:19 +03:00
Roman Khimov
38842531ca core: drop NEP5 tracker data if balance is zero
Makes no sense storing it and returning to the user (C# plugin doesn't do
that).
2021-02-18 17:28:19 +03:00
Roman Khimov
d4f26fe473 docs: update RPC documentation
Add missing methods and notice a bit more implementation differences.
2021-02-18 17:28:19 +03:00
Roman Khimov
07c91ea22d core: fix getenrollments
Make getvalidators return the same set of keys with C# for mainnet.
2021-02-18 17:28:19 +03:00
Roman Khimov
1ef91fa409 rpc: encode port numbers as proper numbers in getpeers
The way C# node does it.
2021-02-18 17:28:19 +03:00
Roman Khimov
850d29060a result: assets are LE in JSON
LE/BE split is the worst NEO feature ever.
2021-02-18 17:28:19 +03:00
Roman Khimov
70a20ce031 core: fix system fee calculation
It was completely wrong starting from the genesis block.
2021-02-18 17:28:19 +03:00
Roman Khimov
5ff57e890b consensus: flush previous proposal on new block
Reusing proposals from previous blocks doesn't make sense. And reduce some
code duplication along the way.
2021-02-18 15:45:34 +03:00
Roman Khimov
d1e02c393d consensus: only use previous proposal if it has something in it
It might just be uninitialized it doesn't really make sense using zero-length
previous proposal anyway.
2021-02-18 15:42:06 +03:00
Roman Khimov
62a11807a8 fixedn: always correctly unmarshal Fixed8 values
Quoted or not, they should be unmarshalled without going through float64.
2021-02-18 15:38:02 +03:00
Evgeniy Stratonikov
feb6ba2ef7 io: allow to restrict string size 2021-02-18 15:36:22 +03:00
Roman Khimov
a46c93f6bb rpc: fix getblocksystemfee call
It should return cumulative fee and it should be wrapped into a string.
2021-02-15 17:58:39 +03:00
Roman Khimov
fda8b784c8 rpc/server: allow numbers for getblockheader call 2021-02-15 17:27:23 +03:00
Roman Khimov
9c34dea296 rpc/server: return more specific error in getblockhash 2021-02-15 16:18:03 +03:00
Roman Khimov
c3b6405f0f transaction: fix asset type JSONization
It's stringy in C#.
2021-02-12 23:02:51 +03:00
Roman Khimov
90a06ed7a5 rpc: fix IsFrozen JSONization for assets
C# node uses `frozen` as field name here.
2021-02-12 00:04:13 +03:00
Roman Khimov
9571ecffac core: add proper asset issuer for UTXO assets
It just wasn't set which is wrong (AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM was
always the issuer).
2021-02-12 00:01:00 +03:00
Roman Khimov
89fb02a7f8
Merge pull request #1648 from nspcc-dev/backport-fixes-from-master
Backport fixes from master to 2.x
2021-01-11 12:44:05 +03:00
Roman Khimov
17c010e07d
Merge pull request #1649 from nspcc-dev/dont-return-spent-in-gettxout
rpc: fix gettxout result for already spent outputs
2021-01-02 13:20:06 +03:00
Roman Khimov
9e35758653 rpc: fix gettxout result for already spent outputs
As per C# documentation [1]:
  If the transaction output is already spent, the result value will be null .

[1]: https://docs.neo.org/docs/en-us/reference/rpc/latest-version/api/gettxout.html
2020-12-31 13:54:42 +03:00
Evgenii Stratonikov
ee45d7739f network: fix requestTx()
2 bugs were here:
1. If amount of tx is small, no messages were sent.
2. Correctly cut byte slice if last message is small.
2020-12-31 13:00:50 +03:00
Roman Khimov
363c24d128 network: fix tx requests, we can't ask more than 500 txes at once 2020-12-31 13:00:00 +03:00
Evgenii Stratonikov
9756ed2b06 network: set timeout on write
Fix a bug occuring under high load when node
hangs during this write.
2020-12-31 12:58:00 +03:00
Evgenii Stratonikov
edf587bbf1 network: fix a bug in discovery with a peer connected twice
It could be the case that checks are performed simultaneosly and
peers connections goes down from 2 to 0. We must take such case into
account and register address as good in discovery.
2020-12-31 12:57:43 +03:00
Evgenii Stratonikov
0d2bc8f4a6 network: remove extra error check from Message.Encode()
Buffer write error is returned from `Encode()`.
2020-12-31 12:52:40 +03:00
Evgenii Stratonikov
0510d1e6c1 vmcli: return after error in break 2020-12-31 12:51:01 +03:00
Evgenii Stratonikov
5d6065c3ee vmcli: set breakpoint before the instruction
Breakpoint should occur before actual instruction execution.
2020-12-31 12:50:52 +03:00
Anna Shaleva
4a8259caea rpc: adjust getrawtransaction and gettransactionheight RPC call
We should not return transaction metadata from `getrawtransaction` in case
transaction is not in the mempool. Height shouldn't be returned from
`gettransactionheight` in case transaction is in the mempool.
2020-12-31 12:44:07 +03:00
Roman Khimov
124c674b17 *: gofmt -s 2020-12-31 11:52:27 +03:00
Roman Khimov
50f477b2af
Merge pull request #1623 from nspcc-dev/2x/core/mempool_fix
[master-2.x] core: fix bug with mempool.verifiedMap
2020-12-16 16:54:46 +03:00
Anna Shaleva
66a64dd4c9 core: fix bug with mempool.verifiedMap
Changes ported from #1621.
2020-12-16 14:14:09 +03:00
Roman Khimov
0333107327 CHANGELOG: release 0.78.2 2020-12-03 15:41:39 +03:00
Roman Khimov
75c4251a06
Merge pull request #1588 from nspcc-dev/keep-only-latest-in-the-config
config: add KeepOnlyLatestState setting where EnableStateRoot is set
2020-12-02 20:26:09 +03:00
Roman Khimov
73bb2c2543 config: add KeepOnlyLatestState setting where EnableStateRoot is set
With its default value set to `false`.
2020-12-02 16:52:04 +03:00
Roman Khimov
64d0876fc0
Merge pull request #1503 from nspcc-dev/mpt/refcount
Implement reference counting in MPT
2020-11-18 14:20:42 +03:00
Evgenii Stratonikov
0ffffb93d7 mpt: implement reference counting 2020-11-18 12:16:05 +03:00
Roman Khimov
3a1b67e9f7
Merge pull request #1532 from nspcc-dev/feature/retransmit
network: retransmit stale transactions
2020-11-12 14:27:27 +03:00
Evgenii Stratonikov
d93ddfda10 network: retransmit stale transactions 2020-11-11 15:51:36 +03:00
Roman Khimov
06f3c34981 mempool: replace timeStamp with blockStamp
Time is not really relevant for us here and we don't use this timestamp in any
way. Yet it occupies 24 bytes and we do two clock_gettime calls to get it.

Replace it with blockStamp which is going to be used in the future for
transaction retransmissions.

It allows to improve single-node TPS by another 3%.
2020-11-11 15:48:13 +03:00
Evgenii Stratonikov
f3abbf34e3 mpt: fill cached fields when getting node from store
Node which has been got from store shouldn't be flushed again.
2020-11-11 13:22:53 +03:00
Evgenii Stratonikov
df1792c80b mpt: export func for decoding node with type
`NodeObject` can contain auxilliary fields and shouldn't be used from outside.
2020-11-11 13:22:53 +03:00
Roman Khimov
49d176010e
Merge pull request #1523 from nspcc-dev/2x/rpc/batch_requests
rpc: allow batch JSON-RPC requests
2020-11-06 17:27:18 +03:00
Anna Shaleva
ef3eb0a842 rpc: allow batch JSON-RPC requests
Close #1509
2020-11-06 17:20:27 +03:00
Roman Khimov
559024671a
Merge pull request #1516 from nspcc-dev/getblocktransfertx
Getblocktransfertx RPC API for 2.x
2020-11-03 09:48:27 +03:00
Roman Khimov
68fc8168ec rpc: add getblocktransfertx RPC call
It uses a bit different output format than getalltransfertx, so
TransferTxEvent type was adjusted accordingly.
2020-11-02 19:01:48 +03:00
Roman Khimov
136e4b5886 core: move NotificationEvent->NEP5Transfer conversion to state
We'll need it as a separate function and it's cleaner this way.
2020-10-29 22:10:40 +03:00
Roman Khimov
52135dcade docs: improve formatting 2020-10-29 19:54:05 +03:00
Roman Khimov
d775f07a55 CHANGELOG: release 0.78.1 2020-10-22 20:01:00 +03:00
Roman Khimov
77ecdcabb4
Merge pull request #1487 from nspcc-dev/fix-boltdb-get-2.x
storage: fix Get for BoltDB, fix #1482
2020-10-13 21:24:41 +03:00
Roman Khimov
14c39b2b26 storage: fix Get for BoltDB, fix #1482 2020-10-13 19:21:57 +03:00
Roman Khimov
d2ee2b5f9f
Merge pull request #1483 from nspcc-dev/2.x-fix-empty-discovery-pool
2.x fix empty discovery pool
2020-10-13 19:00:20 +03:00
Roman Khimov
78773df6ec network: try connecting to seeds indefinitely, use them with 0 pool
If the node is to start with seeds unavailable it will try connecting to each
of them three times, blacklist them and then sit forever waiting for
something. It's not a good behavior, it should always try connecting to seeds
if nothing else works.
2020-10-13 18:00:31 +03:00
Roman Khimov
b36371ed94 network: an address should either be good or bad, but not both 2020-10-13 14:16:06 +03:00
Roman Khimov
50d6cd6b0d
Merge pull request #1475 from nspcc-dev/core/get_storage_items_fix
core: copy storage item key in simple.GetStorageItems
2020-10-10 12:32:00 +03:00
Anna Shaleva
168366f33e core: copy storage item key in simple.GetStorageItems
Close #1468.

We should copy the key to avoid bytes substitution. Otherwise there's a
chance that at the end of dao.Store.Seek(...) execution some keys won't
be the same as the original keys found inside saveToMap function.
2020-10-10 12:24:08 +03:00
Roman Khimov
46d314fb4d
Merge pull request #1429 from nspcc-dev/update-dbft-library-2.x
update dbft, pick timer improvements and other optimizations
2020-09-26 18:12:35 +03:00
Roman Khimov
ca0ff47c05 update dbft, pick timer improvements and other optimizations 2020-09-25 23:06:23 +03:00
Roman Khimov
4ff5bb361a
Merge pull request #1412 from nspcc-dev/config-gas-6195000
config: follow freegasheight update
2020-09-21 13:10:12 +03:00
Roman Khimov
c83cefecc6 config: follow freegasheight update
See neo-project/neo-node#669.
2020-09-21 12:56:39 +03:00
Roman Khimov
e3c360c477 vm: fix comment typo 2020-09-15 21:44:54 +03:00
Roman Khimov
6e5fd359ae CHANGELOG: release 0.78.0 2020-09-15 21:32:59 +03:00
Roman Khimov
9fb749ff68
Merge pull request #1404 from nspcc-dev/transfertx-string-fees
rpc/server: use strings for sys and net fees in TransferTx
2020-09-15 21:29:28 +03:00
Roman Khimov
b8705bdb79 rpc/server: use strings for sys and net fees in TransferTx
The same they're displayed for transactions.
2020-09-15 20:34:34 +03:00
Roman Khimov
08782e265b
Merge pull request #1402 from nspcc-dev/testnet-free-gas-update
config: update free GAS height again
2020-09-15 19:01:48 +03:00
Roman Khimov
ec631984d0
Merge pull request #1399 from nspcc-dev/getalltransfertx-2.x
Getalltransfertx 2.x
2020-09-15 19:01:24 +03:00
Roman Khimov
35c09a2d37 config: update free GAS height again
See neo-project/neo-node#665.
2020-09-15 18:49:20 +03:00
Roman Khimov
3efc373e8a
Merge pull request #1329 from nspcc-dev/feature/importroot
Allow to import state roots from dump
2020-09-15 18:42:44 +03:00
Evgenii Stratonikov
7a976d47f3 cli: allow to export state root dumps 2020-09-15 16:16:48 +03:00
Evgenii Stratonikov
67ee4ed0a6 cli: allow to import state root dumps 2020-09-15 16:16:48 +03:00
Roman Khimov
615ae1b3aa docs: update RPC documentation with *transfers changes 2020-09-15 12:46:40 +03:00
Roman Khimov
2e0e5cff25 rpc/client: add support for getutxotransfers call 2020-09-15 12:46:40 +03:00
Roman Khimov
26d4a05e57 rpc/client: update GetNEP5Transfers call 2020-09-15 12:46:40 +03:00
Roman Khimov
23719f7e72 rpc/server: add new getalltransfertx API
It unifies UTXO and NEP5 transfers for a given address and presents it with
transaction-level grouping (and additional metadata).
2020-09-15 12:46:39 +03:00
Roman Khimov
13f29805bb
Merge pull request #1400 from nspcc-dev/height-dependent-policy
core|config: make max(free)txperblock height-dependent
2020-09-15 12:36:58 +03:00
Evgenii Stratonikov
916603d495 cli: return proper error on block import fail 2020-09-15 12:03:59 +03:00
Evgenii Stratonikov
5703c4859b cli: rename readBlock to readBytes
It reads only byte slice.
2020-09-15 11:11:55 +03:00
Roman Khimov
2f436eee4f config: change configuration changes height for mainnet
See neo-project/neo-node#663.
2020-09-15 08:40:11 +03:00
Roman Khimov
4f7fa732cf core|config: make max(free)txperblock height-dependent
See neo-project/neo-modules#336.
2020-09-14 22:57:03 +03:00
Roman Khimov
010c22e2b5 rpc/server: limit the maximum number of elements for get*transfers 2020-09-14 17:48:17 +03:00
Roman Khimov
56d57611ca rpc: add paging to get*transfer calls 2020-09-13 00:12:45 +03:00
Roman Khimov
0ece58e6dd rpc/server: deduplicate parameter parsing for get*transfers 2020-09-11 22:33:17 +03:00
Roman Khimov
a1357789cf
Merge pull request #1397 from nspcc-dev/minimum-network-fee
Minimum network fee
2020-09-11 18:18:58 +03:00
Roman Khimov
e14ba6c855 core: fix policy-enforcing network fee check
0.001 should be added to the 'extra' value as per https://neo.org/blog/details/4148
2020-09-11 16:52:58 +03:00
Roman Khimov
c4d287f326 config: update FreeGas values
Based on neo-project/neo-node#656 changes.
2020-09-11 16:48:52 +03:00
Roman Khimov
0ef65d1bb9 config: add minimum network fee setting
Follow neo-project/neo#1901.
2020-09-11 15:41:08 +03:00
Roman Khimov
8865d5b2c5
Merge pull request #1395 from nspcc-dev/add-limits-to-gettransfers
Add limits to get(nep5|utxo)transfers
2020-09-09 15:45:09 +03:00
Roman Khimov
319e3996f4
Merge pull request #1394 from nspcc-dev/make-free-gas-height-dependent
config: allow configuring free gas depending on height
2020-09-08 19:07:02 +03:00
Roman Khimov
5b05081525 config: allow configuring free gas depending on height
And update default mainnet/testnet settings. Follow neo-project/neo#1888 and
neo-project/neo-node#656.
2020-09-08 18:52:53 +03:00
Roman Khimov
b310ac051b core/rpc: add continue flag to iterating functions
Most of the time we don't need to get all transfers from the DB and
deserialize them.
2020-09-08 15:38:33 +03:00
Roman Khimov
d3e415d3bd core/state: reverse the order of ForEachTransfer
When using limits we're usually concerned about the most recent
transfers. Returning 3 transfers from the middle of the chain isn't very
helpful.
2020-09-08 12:57:45 +03:00
Roman Khimov
6761efff24 rpc/server: add limit to get*transfers calls
Return only N transfers requested.
2020-09-08 12:56:52 +03:00
Roman Khimov
75ed6c8c08
Merge pull request #1393 from nspcc-dev/tx-set-vout-position
transaction: set output position when decoding
2020-09-07 17:46:04 +03:00
Roman Khimov
9aee3e5a34 transaction: set output position when decoding
We had a kludge for getrawtransaction to set this useless field, but
7e371588a7 broke it. Add it right into the
decoder now to fix all types of queries (getblock/getrawtransaction/gettxout).

Fixes #1392.
2020-09-07 15:37:57 +03:00
Roman Khimov
04ebef9119
Merge pull request #1386 from nspcc-dev/fix-neo-utxo-tracking
core: fix NEO UTXO tracking, drop Fixed8 multiplier
2020-09-04 15:40:24 +03:00
Roman Khimov
39897e811d core: fix NEO UTXO tracking, drop Fixed8 multiplier
When this vout:
         {
            "n" : 0,
            "address" : "ASkbjwosE3aKyGtDQkEgqhNq3Zpv8Xkt14",
            "asset" : "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
            "value" : "606"
         },

Becomes this transfer:
               {
                  "txid" : "0x192ab8e422aed6ac868cb329d6f9af20964134b591908c736d32a2fd8a51d7bf",
                  "amount" : "60600000000",
                  "block_index" : 6113653,
                  "timestamp" : 1599199074
               }

Something is wrong here.
2020-09-04 09:09:35 +03:00
Roman Khimov
1ffc316cc1
Merge pull request #1369 from nspcc-dev/fix/getapplog-2.x
rpc: support 0x form of Uint256 in requests (2.x)
2020-08-27 13:36:38 +03:00
Evgenii Stratonikov
8ff2d35723 rpc: support 0x form of Uint256 in requests 2020-08-27 12:56:32 +03:00
Roman Khimov
c4057b8906
Merge pull request #1316 from nspcc-dev/fix/wallet
consensus: exit if wrong password is provided in configuration
2020-08-14 16:26:45 +03:00
Evgenii Stratonikov
cf48e82242 consensus: exit if wrong password is provided in configuration 2020-08-14 15:33:32 +03:00
Roman Khimov
e8fd6fde99
Merge pull request #1325 from nspcc-dev/fix/watchonly
go.mod: update dbft version
2020-08-14 12:51:17 +03:00
Evgenii Stratonikov
43f986d24d go.mod: update dbft version 2020-08-14 12:47:45 +03:00
Roman Khimov
c86522719a
Merge pull request #1320 from nspcc-dev/fix-stateroot-generation
Fix stateroot generation
2020-08-14 10:01:55 +03:00
Roman Khimov
25307834ab consensus: don't generate stateroot before StateRootEnableIndex 2020-08-13 19:34:52 +03:00
Roman Khimov
ee61120e13 consensus: move stateroot generation from newBlockFromContext to newCommit
Fixes #1313. newCommit is only called once and only when we have enough
signatures unlike newBlockFromContext that is also being called to create
PrepareResponse.
2020-08-13 16:57:59 +03:00
Roman Khimov
5f46aa096f
Merge pull request #1310 from nspcc-dev/fix-yaml-import-path-2.x
*: use proper YAML library import path, fix #1306
2020-08-12 17:15:44 +03:00
Roman Khimov
56b2a16389 *: use proper YAML library import path, fix #1306 2020-08-12 16:26:41 +03:00
Roman Khimov
43eb670d45 mpt: fix comment typo 2020-08-11 21:55:51 +03:00
Roman Khimov
b8ec1b1d93 CHANGELOG: release 0.77.0 2020-08-11 15:48:51 +03:00
Roman Khimov
dfa53c635d config: revert the wrong part of 8850ecd4c
Sorry.
2020-08-10 19:56:34 +03:00
Roman Khimov
d8aac7c675
Merge pull request #1291 from nspcc-dev/stateroot-relaying-during-commit-2.x
Stateroot relaying during commit 2.x
2020-08-10 19:22:17 +03:00
Roman Khimov
47ed2eda43 docs: update neox.md with the latest protocol changes 2020-08-10 16:48:05 +03:00
Roman Khimov
8850ecd4ca config: enable stateroot for mainnet
See neo-project/neo-node#644.
2020-08-10 16:48:05 +03:00
Roman Khimov
92f8cee6cd network: raise MPTRoot broadcast priority
It can affect consensus process.
2020-08-10 16:48:05 +03:00
Roman Khimov
ab7a69bfec network: prevent useless getroots requests
At the height of A we only reliably have a state root for A-1, so there is no
point in requesting state root for A from our peers. Prevents tons of useless
traffic:

2020-08-10T00:02:35.537+0300    DEBUG   got msg {"addr": "127.0.0.1:20333", "type": "getroots"}
2020-08-10T00:02:35.537+0300    DEBUG   got msg {"addr": "127.0.0.1:20333", "type": "roots"}
2020-08-10T00:02:35.537+0300    DEBUG   got msg {"addr": "127.0.0.1:20333", "type": "getroots"}
2020-08-10T00:02:35.537+0300    DEBUG   got msg {"addr": "127.0.0.1:20333", "type": "roots"}
2020-08-10T00:02:35.539+0300    DEBUG   got msg {"addr": "127.0.0.1:20335", "type": "getroots"}
2020-08-10T00:02:35.539+0300    DEBUG   got msg {"addr": "127.0.0.1:20335", "type": "roots"}
2020-08-10T00:02:35.539+0300    DEBUG   got msg {"addr": "127.0.0.1:20334", "type": "getroots"}
2020-08-10T00:02:35.539+0300    DEBUG   got msg {"addr": "127.0.0.1:20334", "type": "roots"}
2020-08-10T00:02:35.540+0300    DEBUG   got msg {"addr": "127.0.0.1:20334", "type": "getroots"}
2020-08-10T00:02:35.540+0300    DEBUG   got msg {"addr": "127.0.0.1:20334", "type": "roots"}
2020-08-10T00:02:35.542+0300    DEBUG   got msg {"addr": "127.0.0.1:20336", "type": "getroots"}
2020-08-10T00:02:35.542+0300    DEBUG   got msg {"addr": "127.0.0.1:20336", "type": "roots"}
2020-08-10 16:48:05 +03:00
Roman Khimov
253c39d4ee consensus: move stateroot message generation to commit phase
* update dbft library, change to 64-bit timestamps and CV reason
 * modify messages
 * generate stateroot witness data from prepare* messages during commit

Fix #1273.
2020-08-10 16:48:05 +03:00
Roman Khimov
ffbdcb202f
Merge pull request #1288 from nspcc-dev/fix/utxo
rpc: adjust `getutxotransfers` RPC
2020-08-07 19:08:46 +03:00
Evgenii Stratonikov
57a325b3d5 rpc: adjust getutxotransfers RPC 2020-08-07 18:12:40 +03:00
Roman Khimov
ebcec6e5dc
Merge pull request #1282 from nspcc-dev/rpc/invoke_with_hashes
rpc: use hashes for verifying in `invoke*` calls
2020-08-07 17:00:36 +03:00
Anna Shaleva
4b351f3123 cli: use hashes for verifying in invoke* calls 2020-08-07 14:41:44 +03:00
Anna Shaleva
d20e54b725 rpc: fix method comment 2020-08-07 13:19:00 +03:00
Anna Shaleva
4715efd531 core, rpc: invoke script with hashes for verifying
Closes #1275
2020-08-07 13:18:55 +03:00
Roman Khimov
92851aa8e4
Merge pull request #1285 from nspcc-dev/drop-go1.12-2.x
[2.x] Drop go 1.12 support
2020-08-07 12:40:12 +03:00
Roman Khimov
f1ac01578b
Merge pull request #1284 from nspcc-dev/feature/nep5timestamps
rpc: provide timestamps in `getnep5transfers`
2020-08-07 12:34:43 +03:00
Roman Khimov
010d55e92f use -trimpath build flag for more reproducible builds
Fix #349.
2020-08-07 12:26:18 +03:00
Roman Khimov
bb4385ca50 drop support for Go 1.12
It's old and not maintained. Use latest Go for builds and test only with 1.13
and 1.14.
2020-08-07 12:25:45 +03:00
Roman Khimov
db98f8f30b
Merge pull request #1274 from nspcc-dev/lowercase-json-field-names-for-stateroot
rpc: use lowercase JSON field names for StateHeight
2020-08-07 12:24:59 +03:00
Evgenii Stratonikov
02033f8355 rpc: set default values in getutxotransfers 2020-08-07 09:49:09 +03:00
Evgenii Stratonikov
fe5e5ff44a rpc: provide timestamps in getnep5transfers
Set default value for the first timestamp to a week ago.
2020-08-07 09:43:23 +03:00
Roman Khimov
9aa7b7fc97
Merge pull request #1268 from nspcc-dev/feature/utxo
Implement `getutxotransfers` RPC (2.x)
2020-08-06 16:19:55 +03:00
Roman Khimov
edeb9a3d2e
Merge pull request #1272 from nspcc-dev/consensus/fix
consensus: create recovery message with state root
2020-08-06 15:16:34 +03:00
Evgenii Stratonikov
7bd4488ff9 core: do not store NEP5 transfer log in memory
Traversing transfer log instead of accumulating and returning it
is faster and takes less memory.
2020-08-06 14:22:30 +03:00
Evgenii Stratonikov
022fb04077 rpc: implement getutxotransfers RPC 2020-08-06 14:16:28 +03:00
Evgenii Stratonikov
407e348cd5 core: save UTXO transfer info 2020-08-06 14:03:21 +03:00
Roman Khimov
ab5eff620b rpc: use lowercase JSON field names for StateHeight
Follow neo-project/neo#1808. Note that this is an incompatible change, but
this feature is still considered to be experimental upstream.
2020-08-05 18:15:29 +03:00
Anna Shaleva
50e5e6fe29 consensus: create recovery message with state root
Closes #1270
2020-08-05 17:08:24 +03:00
Evgenii Stratonikov
e4fcd90b6d state: make NEP5Transfer log more generic 2020-08-04 16:57:36 +03:00
Evgenii Stratonikov
49f9c4ad7e dao: rename transfers to nep5transfers 2020-08-04 15:50:09 +03:00
Roman Khimov
e135719c38
Merge pull request #1250 from nspcc-dev/fix/nep5
Fix `getnep5*` RPC format
2020-08-03 18:05:39 +03:00
Evgenii Stratonikov
c4c9f9225c rpc: return raw values in getnep5*
C# nodes format result without using decimals.
2020-08-03 11:00:15 +03:00
Evgenii Stratonikov
c6d33c5841 core: save NEP5 transfer notify index
TransferNotifyIndex is the index of transfer event in the list
of all transfers in a transaction.
2020-08-03 10:58:23 +03:00
fyrchik
a702423c4a
Merge pull request #1217 from nspcc-dev/fix/nep5transfers
rpc: return transfers for migrated contracts
2020-07-31 19:43:09 +03:00
Evgenii Stratonikov
f0d75afc48 rpc: provide old transfers in getnep5transfers
Return performed transfers even if contract was migrated.
2020-07-24 14:39:16 +03:00
Evgenii Stratonikov
7cd1bca1e1 core,dao: save contract metadata on migration
After contract is migrated there is no way to retrieve it's state.
This commit implements some metadata for NEP5 contracts, so that
values important for diplaying transfer log aren't lost.
2020-07-24 14:38:10 +03:00
Evgenii Stratonikov
7bdbfbad19 rpc: fix getnep5transfers address check condition 2020-07-21 11:12:40 +03:00
Roman Khimov
8341913468 CHANGELOG: release 0.76.2 2020-07-19 07:43:41 +03:00
Roman Khimov
7d5d6b620e
Merge pull request #1213 from nspcc-dev/fix-stateheight-sync-for-nonzero-enableindex
core: fix stateroot height update for testnet
2020-07-18 21:37:11 +03:00
Roman Khimov
692565c5a2 network: fix stateroot processing for StateRootEnableIndex != 0
We we missing stateroot for StateRootEnableIndex, starting only with
StateRootEnableIndex + 1.
2020-07-18 20:58:47 +03:00
Roman Khimov
6d32751292 core: fix stateroot height update for testnet
When synchronizing with stateroot-enabled network from genesis and if
stateroot is not enabled in block zero we were failing to update state height
because initially it's updated with a jump from 0 to StateRootEnableIndex, so
we should allow that to happen to have correct state height.
2020-07-18 10:17:17 +03:00
Roman Khimov
dbbe57d11a
Merge pull request #1186 from nspcc-dev/fix-astack-clearance-on-call-2.x
vm: clear altstack on CALL, fix #1158
2020-07-18 07:32:05 +03:00
Roman Khimov
35c60cd8f4 vm: fix elements counting for isolated calls
The map and the counter are VM-wide, not context-specific, that's the whole
point of them.
2020-07-18 07:27:08 +03:00
Roman Khimov
3d62db4f34 vm: clear altstack on CALL, fix #1158
neo-vm doesn't copy altstack on CALL, so it effectively is cleared. But we
need to copy things back on return if there are any.
2020-07-18 07:27:08 +03:00
Roman Khimov
38c195fea9
Merge pull request #1185 from nspcc-dev/fix-panic-on-shutdown-2.x
network: copy peers for Shutdown iteration
2020-07-16 16:23:58 +03:00
Roman Khimov
4d0f4d3e51 network: copy peers for Shutdown iteration
We can't lock them (or there will be a deadlock), but we need to fix this:

fatal error: concurrent map iteration and map write

goroutine 1 [running]:
runtime.throw(0xdec086, 0x26)
        /usr/lib64/go/1.12/src/runtime/panic.go:617 +0x72 fp=0xc02fec2bf8 sp=0xc02fec2bc8 pc=0x42d932
runtime.mapiternext(0xc02fec2d40)
        /usr/lib64/go/1.12/src/runtime/map.go:860 +0x597 fp=0xc02fec2c80 sp=0xc02fec2bf8 pc=0x40efe7
github.com/nspcc-dev/neo-go/pkg/network.(*Server).Shutdown(0xc0000fc160)
        /home/rik/dev/neo-go2/pkg/network/server.go:194 +0x238 fp=0xc02fec2db0 sp=0xc02fec2c80 pc=0xa89da8
github.com/nspcc-dev/neo-go/cli/server.startServer(0xc0000fcc60, 0x0, 0x0)
        /home/rik/dev/neo-go2/cli/server/server.go:399 +0x7a9 fp=0xc02fec3820 sp=0xc02fec2db0 pc=0xae2079
...
2020-07-16 11:43:34 +03:00
Roman Khimov
e375267422 ROADMAP: drop from master-2.x branch
It makes no sense there, master-2.x doesn't really have any ROADMAP any more
and Neo 3 development is being tracked in master branch.
2020-07-15 17:56:35 +03:00
Roman Khimov
4374e98f15 CHANGELOG: release 0.76.1 2020-07-15 17:40:51 +03:00
Roman Khimov
bf3c29c319
Merge pull request #1149 from nspcc-dev/fix-nep5-balances-2.x
Fix nep5 balances migration (2.x)
2020-07-07 22:05:01 +03:00
Roman Khimov
1ab4f81fc3 dao: migrate nep5 balances with the contract
Fixes #1144. It's quite simple approach, we just update balance info right
upon contract migration. It will slow down migration transactions, but it
takes about 1-2 seconds to Seek through balances at mainnet's 3.8M, so the
approach should still work good enough. The other idea was to make lazy
updates (maintaining contract migration map), but it's more complicated to
implement (and implies that a balance get might also do a write).

There also is a concern about memory usage, it can give a spike of some tens
of megabytes, but that also is considered to be acceptable.
2020-07-07 19:55:55 +03:00
Roman Khimov
fd778e0250
Merge pull request #1146 from nspcc-dev/fix/getnep5balances
rpc: support stringified address in getnep5balances RPC
2020-07-03 18:56:34 +03:00
Evgenii Stratonikov
dcb00c61f2 rpc: support raw address in getnep5transfers RPC 2020-07-03 18:44:46 +03:00
Evgenii Stratonikov
59cd19f5d4 rpc: support stringified address in getnep5balances RPC 2020-07-03 18:44:46 +03:00
Evgenii Stratonikov
78900148ee rpc/request: add (*Param).GetUint160FromAddressOrHex()
Allow to get address from both representations.
2020-07-03 18:42:09 +03:00
Roman Khimov
5ec96b4ccc
Merge pull request #1116 from nspcc-dev/fix-consensus-synchronization-issue
consensus: prevent synchronization stalls
2020-06-26 17:28:33 +03:00
Roman Khimov
25b742d24e cache: prevent TestRelayCache_Add failures
random.Bytes() might be not random enough.
2020-06-26 12:42:12 +03:00
Roman Khimov
cefad683e3 consensus: prevent synchronization stalls
When CN is not up to date with the network is synchonizes blocks first and
only then starts consensus process. But while synchronizing it receives
consensus payloads and tries to process them even though messages reader
routine is not started yet. This leads to lots of goroutines waiting to send
their messages:

Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639919 [chan send, 4 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc005bd7680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc00507d170, 0xc005bdd560, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc00507d170)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 1639181 [chan send, 10 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc013bb6600)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc01361ee10, 0xc01342c780, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc01361ee10)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad
Jun 25 23:55:53 nodoka neo-go[32733]: goroutine 39454 [chan send, 32 minutes]:
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/consensus.(*service).OnPayload(0xc0000ecb40, 0xc014fea680)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:329 +0x31b
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleConsensusCmd(...)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:687
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*Server).handleMessage(0xc0000ba160, 0x1053260, 0xc0140b2ea0, 0xc014fe0ed0, 0x0, 0x0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/server.go:806 +0xd58
Jun 25 23:55:53 nodoka neo-go[32733]: github.com/nspcc-dev/neo-go/pkg/network.(*TCPPeer).handleConn(0xc0140b2ea0)
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_peer.go:160 +0x294
Jun 25 23:55:53 nodoka neo-go[32733]: created by github.com/nspcc-dev/neo-go/pkg/network.(*TCPTransport).Dial
Jun 25 23:55:53 nodoka neo-go[32733]: #011/go/src/github.com/nspcc-dev/neo-go/pkg/network/tcp_transport.go:38 +0x1ad

Luckily it doesn't break synchronization completely as eventually connection
timers fire, the node breaks all connections, create new ones and these new
ones request blocks successfully until another consensus payload stalls them
too. In the end the node reaches synchronization, message processing loop
starts and releases all of these waiting goroutines, but it's better for us to
avoid this happening at all.

This also makes double-starting a no-op which is a nice property.
2020-06-26 12:32:12 +03:00
Roman Khimov
c6894bfb3f vm/cli: fix load commands help 2020-06-26 00:13:58 +03:00
Roman Khimov
bc3f17a890 CHANGELOG: release 0.76.0 2020-06-25 22:05:27 +03:00
Roman Khimov
7e5533e77e
Merge pull request #1114 from nspcc-dev/fix-test-failures-2.x
Fix test failures 2.x
2020-06-25 22:01:16 +03:00
Roman Khimov
8f1ddc0651 rpc/server: fix error reporting in Start
This error message makes no sense when shutting down the server:
2020-06-25T19:29:53.251+0300    ERROR   failed to start RPC server      {"error": "http: Server closed"}

And ListenAndServer is documented to always return non-nil error one of which
is http.ErrServerClosed. This should also fix the following test failure:

==================
WARNING: DATA RACE
Read at 0x00c000254243 by goroutine 49:
  testing.(*common).logDepth()
      /usr/local/go/src/testing/testing.go:665 +0xa1
  testing.(*common).Logf()
      /usr/local/go/src/testing/testing.go:658 +0x8f
  testing.(*T).Logf()
      <autogenerated>:1 +0x75
  go.uber.org/zap/zaptest.testingWriter.Write()
      /go/pkg/mod/go.uber.org/zap@v1.10.0/zaptest/logger.go:130 +0x11f
  go.uber.org/zap/zaptest.(*testingWriter).Write()
      <autogenerated>:1 +0xa9
  go.uber.org/zap/zapcore.(*ioCore).Write()
      /go/pkg/mod/go.uber.org/zap@v1.10.0/zapcore/core.go:90 +0x1c3
  go.uber.org/zap/zapcore.(*CheckedEntry).Write()
      /go/pkg/mod/go.uber.org/zap@v1.10.0/zapcore/entry.go:215 +0x1e7
  go.uber.org/zap.(*Logger).Error()
      /go/pkg/mod/go.uber.org/zap@v1.10.0/logger.go:203 +0x95
  github.com/nspcc-dev/neo-go/pkg/rpc/server.(*Server).Start()
      /go/src/github.com/nspcc-dev/neo-go/pkg/rpc/server/server.go:179 +0x5c5

Previous write at 0x00c000254243 by goroutine 44:
  testing.tRunner.func1()
      /usr/local/go/src/testing/testing.go:900 +0x353
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:913 +0x1bb

Goroutine 49 (running) created at:
  github.com/nspcc-dev/neo-go/pkg/rpc/server.initClearServerWithInMemoryChain()
      /go/src/github.com/nspcc-dev/neo-go/pkg/rpc/server/server_helper_test.go:69 +0x305
  github.com/nspcc-dev/neo-go/pkg/rpc/server.initServerWithInMemoryChain()
      /go/src/github.com/nspcc-dev/neo-go/pkg/rpc/server/server_helper_test.go:78 +0x3c
  github.com/nspcc-dev/neo-go/pkg/rpc/server.testRPCProtocol()
      /go/src/github.com/nspcc-dev/neo-go/pkg/rpc/server/server_test.go:805 +0x53
  github.com/nspcc-dev/neo-go/pkg/rpc/server.TestRPC.func1()
      /go/src/github.com/nspcc-dev/neo-go/pkg/rpc/server/server_test.go:793 +0x44
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:909 +0x199

Goroutine 44 (finished) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:960 +0x651
  github.com/nspcc-dev/neo-go/pkg/rpc/server.TestRPC()
      /go/src/github.com/nspcc-dev/neo-go/pkg/rpc/server/server_test.go:792 +0x5d
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:909 +0x199
==================
2020-06-25 19:41:29 +03:00
Roman Khimov
c489f975d4 core: fix TestSubscriptions occasional failures
panic: Log in goroutine after TestSubscriptions has completed

goroutine 1079 [running]:
testing.(*common).logDepth(0xc00057a100, 0xc00039e210, 0xa4, 0x3)
	/usr/local/go/src/testing/testing.go:634 +0x51a
testing.(*common).log(...)
	/usr/local/go/src/testing/testing.go:614
testing.(*common).Logf(0xc00057a100, 0xe32eaa, 0x2, 0xc0009560e0, 0x1, 0x1)
	/usr/local/go/src/testing/testing.go:649 +0x91
go.uber.org/zap/zaptest.testingWriter.Write(0xf64120, 0xc00057a100, 0x0, 0xc0003fe400, 0xa5, 0x400, 0xc000958e40, 0xc0009560d0, 0xc000958e60)
	/go/pkg/mod/go.uber.org/zap@v1.10.0/zaptest/logger.go:130 +0x120
go.uber.org/zap/zapcore.(*ioCore).Write(0xc0005cd050, 0x0, 0xbfb54ffc0626aba2, 0x916de700, 0x1485500, 0x0, 0x0, 0xe43fb0, 0x1c, 0x0, ...)
	/go/pkg/mod/go.uber.org/zap@v1.10.0/zapcore/core.go:90 +0x1c5
go.uber.org/zap/zapcore.(*CheckedEntry).Write(0xc000102d10, 0xc00039a000, 0x5, 0x5)
	/go/pkg/mod/go.uber.org/zap@v1.10.0/zapcore/entry.go:215 +0x1e8
go.uber.org/zap.(*Logger).Info(0xc00035eba0, 0xe43fb0, 0x1c, 0xc00039a000, 0x5, 0x5)
	/go/pkg/mod/go.uber.org/zap@v1.10.0/logger.go:187 +0x96
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).persist(0xc00000cb40, 0xc00017c2c0, 0xbe8a00)
	/go/src/github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:839 +0x6c9
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).Run.func2(0xc00000cb40, 0xc0005c6c30)
	/go/src/github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:302 +0x54
created by github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).Run
	/go/src/github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:301 +0x25d
FAIL	github.com/nspcc-dev/neo-go/pkg/core	2.463s
2020-06-25 19:41:17 +03:00
Roman Khimov
a7e8f07073
Merge pull request #1109 from nspcc-dev/neox-doc
docs: add neox documentation
2020-06-25 17:36:02 +03:00
Roman Khimov
d12258e79c
Merge pull request #1106 from nspcc-dev/fix/rpc
Add missing neox rpc client wrappers
2020-06-25 17:11:02 +03:00
Evgenii Stratonikov
eb37f92881 rpc/client: support verifyproof RPC 2020-06-25 17:07:23 +03:00
Roman Khimov
e97c153a26 docs: add neox documentation 2020-06-25 17:05:10 +03:00
Evgenii Stratonikov
654877fb1b rpc/client: support getproof RPC 2020-06-25 17:04:23 +03:00
Roman Khimov
b517aa43db
Merge pull request #1105 from nspcc-dev/fix-key-recovery-doc
interop: fix key recovery functions documentation
2020-06-25 16:34:28 +03:00
Evgenii Stratonikov
d8dddabc86 rpc/client: support getstateheight RPC 2020-06-25 16:19:47 +03:00
Evgenii Stratonikov
b4b0ae0f51 rpc/client: support getstateroot RPC 2020-06-25 16:19:47 +03:00
Evgenii Stratonikov
45a95b5242 rpc/server: remove debug fmt.Println() from tests 2020-06-25 16:19:47 +03:00
Roman Khimov
d8556b80bc interop: fix key recovery functions documentation
Follow 7d786fac79 update.
2020-06-25 14:30:53 +03:00
Roman Khimov
3ccf19fc1e
Merge pull request #1097 from nspcc-dev/fix/mpt
core,dao: use MPT only if it is enabled in config
2020-06-24 16:18:59 +03:00
Evgenii Stratonikov
2b21102c14 core,dao: use MPT only if it is enabled in config 2020-06-24 16:13:27 +03:00
Roman Khimov
fbadb317f5
Merge pull request #1096 from nspcc-dev/neox-2.x
Neox 2.x
2020-06-24 16:08:19 +03:00
Roman Khimov
25fdc62203 core: add state height to prometheus metrics 2020-06-24 14:47:08 +03:00
Roman Khimov
ea17793aee core: fix unconditional MPT collapsing
It should depend on EnableStateRoot.
2020-06-24 14:41:57 +03:00
Roman Khimov
6d5e70163d Revert "Makefile: add neox suffix to neox-2.x branch"
This reverts commit 806b28aab7, we're merging to
master-2.x now.
2020-06-24 14:37:16 +03:00
Roman Khimov
03977083c0
Merge pull request #1095 from nspcc-dev/update-dbft-neox-2.x
consensus: update dbft library (neox-2.x)
2020-06-24 14:32:06 +03:00
Roman Khimov
cce4380eee
Merge pull request #1090 from nspcc-dev/feature/protocol
Enable state root exchange base on config
2020-06-24 13:47:47 +03:00
Roman Khimov
86110445a6 consensus: update dbft library
Pick up the following changes:
 * recovery message sending fix
 * tx re-requests on sendRecoveryRequest
 * proposed block checks fix
 * timeout calculation fix for InitializeConsensus
 * removal of TxPerBlock which is irrelevant for neo-go (policying is done by
   the node, not dbft library)
2020-06-24 13:39:12 +03:00
Evgenii Stratonikov
9ee9cb8e39 core: provide key recovery interops only if neox is enabled 2020-06-24 12:37:27 +03:00
Evgenii Stratonikov
b5fb63d091 *: specify first state root index in config 2020-06-24 12:37:27 +03:00
Evgenii Stratonikov
d128b55dbf *: add config flag for enabling state root feature 2020-06-24 12:37:27 +03:00
Evgenii Stratonikov
c06b3b669d io: make maxArraySize public 2020-06-24 12:14:33 +03:00
Evgenii Stratonikov
bc81b56708 consensus: fix decodeData doc-comment 2020-06-24 10:47:44 +03:00
Roman Khimov
2cddeadd9a
Merge pull request #1089 from nspcc-dev/fix/docker-startup
docker: fix shell condition check in entrypoint script
2020-06-23 16:42:39 +03:00
alexvanin
423495ecc3 docker: fix shell condition check in entrypoint script 2020-06-23 12:08:49 +03:00
Evgenii Stratonikov
2863343f03 consensus: use a method function for NewPayload() 2020-06-22 17:06:28 +03:00
Roman Khimov
3a6186af54
Merge pull request #1083 from nspcc-dev/fix/stateroot
Request verified state roots
2020-06-22 16:42:41 +03:00
Roman Khimov
fec8916cb9
Merge pull request #1087 from nspcc-dev/fix/readvarbytes
Allow to restrict slice size in `io.ReadVarBytes()`
2020-06-22 15:42:07 +03:00
Evgenii Stratonikov
ad0cf146e8 consensus: restrict invocation script size 2020-06-22 15:36:12 +03:00
Evgenii Stratonikov
511d18d409 io: allow to restrict slice size in ReadVarBytes 2020-06-22 15:35:42 +03:00
Roman Khimov
9767817ee4
Merge pull request #1086 from nspcc-dev/fix/recovery
consensus: fix commitCompact payload
2020-06-22 14:52:15 +03:00
Evgenii Stratonikov
aebed3826b consensus: fix commitCompact payload
Add state root signature to `commitCompact` in `recoveryMessage`.
2020-06-22 14:30:21 +03:00
Evgenii Stratonikov
caf53740d3 network: do not process state roots below verified height
Implement fast path to skip state root retrieval and decoding.
2020-06-22 12:33:35 +03:00
Evgenii Stratonikov
e2e1bd09ae network: request state roots if needed 2020-06-22 12:33:35 +03:00
Evgenii Stratonikov
f8051da0bd core: extend Blockchainer interface with StateHeight()
Allow to query current verified state root height.
2020-06-22 10:53:13 +03:00
Evgenii Stratonikov
f665843887 core: update verified state root height 2020-06-22 10:53:12 +03:00
Roman Khimov
9615e83c00
Merge pull request #952 from nspcc-dev/fix/structfield
compiler: set default values for complex struct fields (2.x)
2020-06-11 17:50:18 +03:00
Evgenii Stratonikov
cc5b5bff2e compiler: emit NOTEQUAL only for numbers
Dispatch based on types similarly to EQUAL.
2020-06-11 17:41:52 +03:00
Evgenii Stratonikov
9d6b0ee4a8 compiler: disallow comparing slices with nil
NEO VM does not distinguish between empty and nil slices. Supporting
this is not easy and requires changing lots of other opcodes.
Pointers are not supported anyway and slices can be checked for
emptiness by inspecting their `len`.
2020-06-11 17:40:02 +03:00
Evgenii Stratonikov
bf6aa02dcf compiler: set default values for complex struct fields
Set default value also for complex (struct, map) types.
Note: because VM does not distinguish empty array and nil array,
comparison 'a == nil' can't be handled properly without substantial
effort. This will be fixed in NEO3.
2020-06-11 17:40:02 +03:00
Roman Khimov
d5ba2e49a2
Merge pull request #1045 from nspcc-dev/fix/stateroot
consensus: verify state root in PrepareRequest
2020-06-11 11:25:02 +03:00
Evgenii Stratonikov
8614867439 consensus: verify state root in PrepareRequest
Fixes #1042.
2020-06-11 10:18:12 +03:00
Roman Khimov
4732103294
Merge pull request #1046 from nspcc-dev/fix-key-recovery-interops
Fix key recovery interops
2020-06-10 19:51:39 +03:00
Roman Khimov
7d786fac79 core: fix key recovery interops return value
It's uncompressed coordinate, both X and Y, not just X. Fix #1043.
2020-06-10 19:20:57 +03:00
Roman Khimov
a6541c4514 keys: add support for uncompressed serialization in PublicKey 2020-06-10 19:17:08 +03:00
Roman Khimov
c13ca8d597
Merge pull request #1041 from nspcc-dev/consensus-fixes-2.x-neox
Consensus fixes 2.x neox
2020-06-10 12:07:22 +03:00
Roman Khimov
37173bcf22 update dbft library
Include logging updates and LastSeenMessages fix.
2020-06-10 11:45:51 +03:00
Roman Khimov
788304ec49
Merge pull request #1021 from nspcc-dev/feature/remove
compiler: implement Remove builtin (2.x)
2020-06-10 11:09:56 +03:00
Roman Khimov
edd60e656e consensus: fix processing changeview payloads from recovery message
Using view number from the recovery message is just plain wrong, it's gonna be
higher than our current view and these messages will be treated as coming from
the future, even though they have their original view number included.
2020-06-09 18:17:01 +03:00
Roman Khimov
76f71ab1ef
Merge pull request #1012 from nspcc-dev/feature/mptrpc
rpc: implement MPT-related RPC (2.x)
2020-06-09 17:25:55 +03:00
Evgenii Stratonikov
1fd7938fd8 network: process state roots properly 2020-06-08 17:31:59 +03:00
Evgenii Stratonikov
519a98039c rpc: implement verifyproof RPC
Test getproof and verifyproof together.
2020-06-08 17:31:59 +03:00
Evgenii Stratonikov
8cbbddddaf rpc: implement getproof RPC 2020-06-08 17:31:58 +03:00
Evgenii Stratonikov
fe8038e8b7 rpc/server: implement getstateheight RPC 2020-06-08 16:40:37 +03:00
Evgenii Stratonikov
53dc7f27b6 rpc/server: implement getstateroot RPC 2020-06-08 16:40:37 +03:00
Evgenii Stratonikov
dcaa82b32b rpc: convert null value to a defaultT
Right now we convert it is unmarshaler into a float64(0)
so an error is supressed.
2020-06-08 16:39:02 +03:00
Roman Khimov
349d8a1984
Merge pull request #1022 from nspcc-dev/fix-maxfreetxperblock-neox-2.x
core: respect MaxFreeTransactionsPerBlock setting
2020-06-08 12:53:26 +03:00
Roman Khimov
6437f4b32e core: respect MaxFreeTransactionsPerBlock setting
Fix #1019.
2020-06-08 12:35:39 +03:00
Evgenii Stratonikov
9f7f94a6fb compiler: implement Remove builtin
Support removing items from collections via REMOVE opcode.
2020-06-08 12:05:44 +03:00
Roman Khimov
8e785feeb6
Merge pull request #1017 from nspcc-dev/neox/recover_interop
compiler: add recover interops
2020-06-06 22:59:13 +03:00
Anna Shaleva
4f1cf07075 compiler: add public key recovering syscalls
Added Secp256r1Recover and Secp256k1Recover syscalls.
2020-06-05 22:13:17 +03:00
Evgenii Stratonikov
7b1a54c934 rpc/server: unify boolean flag handling
Implement (*Param).GetBoolean() for converting parameter to bool value.
It is used for verbosity flag and is false iff it is either zero number
or empty sting.
2020-06-05 12:51:09 +03:00
Evgenii Stratonikov
44f93c7c69 rpc/server: simplify errors handling during parameter parsing 2020-06-05 12:51:09 +03:00
Roman Khimov
0b87a68210
Merge pull request #1014 from nspcc-dev/mpt-fixes
Mpt fixes
2020-06-04 19:04:02 +03:00
Roman Khimov
a1c4d7ce26 core: do MPT compaction every once in a while
We need to compact our in-memory MPT from time to time, otherwise it quickly
fills up all available memory. This raises two obvious quesions --- when to do
that and to what level do that.

As for 'when', I think it's quite easy to use our regular persistence interval
as an anchor (and it also frees up some memory), but we can't do that in the
persistence routine itself because of synchronization issues (adding some
synchronization primitives would add some cost that I'd also like to avoid),
so do it indirectly by comparing persisted and current height in `storeBlock`.

Choosing proper level is another problem, but if we're to roughly estimate one
full branch node to use 1K of memory (usually it's way less than that) then we
can easily store 1K of these nodes and that gives us a depth of 10 for our
trie.
2020-06-04 17:25:57 +03:00
Roman Khimov
e3af560d11 dao: optimize storage cache flushing
Items were serialized several times if there were several successful
transactions in a block, prevent that by using State field as a bitfield (as
it almost was intended to) and adding one more bit. It also eliminates useless
duplicate MPT traversions.

Confirmed to not break storage changes up to 3.3M on testnet.
2020-06-04 17:21:58 +03:00
Roman Khimov
69ccca675d core: fix PrevHash calculation for MPTRoot
This was differing from C# notion of PrevHash. It's not a previous root, but
rather a hash of the previous serialized MPTRoot structure (that is to be
signed by CNs).
2020-06-04 17:19:30 +03:00
Roman Khimov
685d3eb870 dao: prevent double serialization of StorageItems
Converting to MPT value serializes the StorageItem, so it makes no sense doing
it again.
2020-06-04 17:18:15 +03:00
Roman Khimov
f77c239296 mpt: fix extension node cache invalidation
It should always be invalidated if something changes in the `next` (below the
extension node).
2020-06-04 17:16:32 +03:00
Evgenii Stratonikov
5794bdb169 state: implement JSON marshaling for MPT* items 2020-06-03 18:09:28 +03:00
Roman Khimov
a6ffa90ceb
Merge pull request #1009 from nspcc-dev/update-dbft-2.x
consensus: update dbft to include CountFailed and RecoveryMessage changes
2020-06-03 16:12:05 +03:00
Roman Khimov
24785f1f50
Merge pull request #1008 from nspcc-dev/feature/mpt
core: update MPT during block processing
2020-06-03 16:07:04 +03:00
Roman Khimov
baafa30266
Merge pull request #1006 from nspcc-dev/interop/crypto
core: implement key recover interops
2020-06-03 14:39:44 +03:00
Anna Shaleva
6c06bc57cc core: implement key recover interops
Implement secp256k1 and secp256r1 recover interops, closes #1003.

Note:

We have to implement Koblitz-related math to recover keys properly
with Neo.Cryptography.Secp256k1Recover interop as far as standard
go elliptic package supports short-form Weierstrass curve with a=-3
only (see https://github.com/golang/go/issues/26776 for details).
However, it's not the best choise to have a lot of such math in our
project, so it would be better to use ready-made solution for
Koblitz-related cryptography.
2020-06-03 14:36:04 +03:00
Evgenii Stratonikov
10189b6ab3 consensus: extend payloads with StateRoot info
Create and verify witness after block processing.
2020-06-03 13:33:44 +03:00
Evgenii Stratonikov
edcfdb3bde network: add MPT-related P2P payloads 2020-06-03 13:33:44 +03:00
Evgenii Stratonikov
20f190ef69 core: update MPT during block processing 2020-06-03 13:33:44 +03:00
Roman Khimov
7d24fc5500
Merge pull request #1011 from nspcc-dev/optimize-mpt-2.x-neox
Optimize mpt 2.x neox
2020-06-03 11:36:17 +03:00
Roman Khimov
029fecbb71 mpt: don't flush nodes already present in the DB
It's just a waste of time.
2020-06-03 00:37:30 +03:00
Roman Khimov
1b6aee42d5 mpt: restructure nodes a bit, implement serialization and hash cache
It drastically reduces the number of allocations and hash calculations.
2020-06-03 00:37:21 +03:00
Roman Khimov
fd73310a1c
Merge pull request #1007 from nspcc-dev/update-dbft-2.x
consensus: update dbft to include CountFailed and RecoveryMessage changes
2020-06-02 22:44:46 +03:00
Roman Khimov
e4f413aa29 consensus: update dbft to include CountFailed and RecoveryMessage changes
dbft interface changed a little also because of Neo 3 changes, but it still is
compatible with Neo 2.
2020-06-02 11:12:47 +03:00
Roman Khimov
2f90a06db3
Merge pull request #990 from nspcc-dev/feature/mpt
Initial MPT implementation (2.x)
2020-06-01 18:58:35 +03:00
Evgenii Stratonikov
314430be1d mpt: implement NEO storage key conversion 2020-06-01 18:15:13 +03:00
Evgenii Stratonikov
103c45850a mpt: implement (*Trie).Collapse()
Because trie size is rather big, it can't be stored in memory.
Thus some form of caching should also be implemented. To avoid
marshaling/unmarshaling of items which are close to root and are used
very frequenly we can save them across the persists.
This commit implements pruning items at the specified depth,
replacing them by hash nodes.
2020-06-01 18:15:13 +03:00
Evgenii Stratonikov
9c478378e1 mpt: implement JSON marshaling/unmarshaling
Because there is no distinct type field in JSONized nodes, distinction
is made via payload itself, thus all unmarshaling is done via
NodeObject.
2020-06-01 18:15:13 +03:00
Evgenii Stratonikov
31d9aeddd2 mpt: implement MPT proof Get and Verify 2020-06-01 18:14:19 +03:00
Evgenii Stratonikov
861a1638e8 mpt: implement MPT trie
MPT is a trie with a branching factor = 16, i.e. it consists of sequences in
16-element alphabet.
2020-06-01 18:14:19 +03:00
Roman Khimov
806b28aab7 Makefile: add neox suffix to neox-2.x branch 2020-05-30 16:01:35 +03:00
Roman Khimov
9a0d7d3254 transaction: gofmt -s 2020-05-30 15:48:57 +03:00
Roman Khimov
0f6d01faa1 CHANGELOG: release v0.75.0 2020-05-28 18:54:14 +03:00
Roman Khimov
90536d533d
Merge pull request #996 from nspcc-dev/dont-change-cached-until-persist-2.x
dao: split GetStorageItem for Cached into external and internal versions
2020-05-28 11:50:09 +03:00
Roman Khimov
06bed2b4bf dao: split GetStorageItem for Cached into external and internal versions
Lower Cached state shouldn't be changed until Persist. Fixes #994.
2020-05-27 18:34:41 +03:00
Roman Khimov
0da1935ebb Merge pull request #992 from nspcc-dev/fix/state
vm,dao: return storage iterator from DAO in Storage.Find interop
2020-05-27 11:53:54 +03:00
Evgenii Stratonikov
b0d07c3031 vm: make Iterator interface public
There is nothing wrong with iterators being implemented in other parts
of code (e.g. Storage.Find). In this case type assertions can
prevent bugs at compile-time.
2020-05-27 11:40:46 +03:00
Evgenii Stratonikov
503442a60d dao: restrict GetStorageItems by prefix
All storage items can still be retrived via zero-length prefix.
2020-05-27 11:40:46 +03:00
Evgenii Stratonikov
776bd85ded vm,dao: return storage iterator from DAO in Storage.Find interop
Reproduce behavior of the reference realization:
- if item was Put in cache after it was encountered during
  Storage.Find, it must appear twice
- checking if item is in cache must be performed in real-time
  during `Iterator.Next()`
2020-05-27 11:40:46 +03:00
Roman Khimov
79c87ca8a5
Merge pull request #991 from nspcc-dev/fix/restore
cli: support DB restore from diff
2020-05-27 11:36:44 +03:00
Evgenii Stratonikov
78bd01db57 cli: support DB restore from diff
Diff dumps provided by NGD contain index of the first block.
Also do proper error handling.
2020-05-27 10:46:12 +03:00
Roman Khimov
7a783b64d7
Merge pull request #984 from nspcc-dev/fix/state
core: fix a typo in GetUnspentCoins interop
2020-05-25 12:28:39 +03:00
Evgenii Stratonikov
86ce234d5f core: fix a typo in GetUnspentCoins interop
Fix a bug introduced in 8ed77f0d.
2020-05-25 11:14:44 +03:00
Roman Khimov
ff0241ed07
Merge pull request #981 from aprasolova/feature/dockerfile
Clean build in Dockerfile
2020-05-22 17:35:36 +03:00
anastasia prasolova
7431263903 added make version 2020-05-22 14:59:51 +03:00
anastasia prasolova
fe2e501488 clean docker build 2020-05-22 13:56:26 +03:00
Roman Khimov
3675502927
Merge pull request #979 from nspcc-dev/fix/state
core: make GetUnspentCoins interop return array, fix #978.
2020-05-22 13:33:32 +03:00
Evgenii Stratonikov
eeeb05fc2c compiler: restore support for GetUnspentCoins
Revert a587274.
2020-05-22 13:24:44 +03:00
Evgenii Stratonikov
8ed77f0d25 core: make GetUnspentCoins interop return array, fix #978. 2020-05-22 13:24:43 +03:00
Roman Khimov
fb9b2ae982
Merge pull request #975 from nspcc-dev/clone-struct-in-setitem-2.x
vm: clone struct item on SETITEM, close #972
2020-05-21 18:02:06 +03:00
Roman Khimov
4fda492873
Merge pull request #974 from nspcc-dev/fix/state
vm: allow to convert Map item to bool

Fixes
2020-05-21T17:45:51.808+0300 WARN contract invocation failed {"tx": "71c43cc32ae5336622d7105d1c96387c8cca6c948beac29edfef46712ba5ffeb", "block": 3960417, "error": "error encountered at instruction 14802 (JMPIFNOT): can't convert to bool: Map"}
2020-05-21 17:59:19 +03:00
Roman Khimov
f8b41e486c vm: clone struct item on SETITEM, close #972
It's the same as with APPEND and VALUES.
2020-05-21 17:56:26 +03:00
Evgenii Stratonikov
a8fa68914a vm: allow to convert Map item to bool 2020-05-21 17:53:19 +03:00
Roman Khimov
e07fd4f9f0
Merge pull request #973 from nspcc-dev/fix/refcount
vm: update stack size in SETITEM properly
2020-05-21 16:47:27 +03:00
Roman Khimov
61c595909a
Merge pull request #971 from nspcc-dev/add-key-size-checks-2.x
Add key size checks 2.x
2020-05-21 15:38:40 +03:00
Evgenii Stratonikov
cd8445aa59 vm: update stack size in SETITEM properly 2020-05-21 15:20:37 +03:00
Roman Khimov
60bca03577 crypto: add input data length check in (*PublicKey).DecodeBytes
DecodeBinary works with streams, so it can't do that, but DecodeBytes can and
should. Also fix unmarshalled binary buffer that this check exposed.
2020-05-21 14:28:16 +03:00
Roman Khimov
0ce3a12e87 core: check for key length in CheckWitness, fix #968
Only one type of keys is allowed here.
2020-05-21 14:27:41 +03:00
Roman Khimov
d0853c0471
Merge pull request #967 from nspcc-dev/fix-pickitem-and-vm-cli-2.x
Fix pickitem and vm cli 2.x
2020-05-21 13:37:35 +03:00
Roman Khimov
1721360dd1 vm: limit types accepted for PICKITEM, fix #965
bafdb916a0 change was wrong (probably brought
from neo-vm 3.0 at the state at which it existed back then), neo-vm 2.x
doesn't allow PICKITEM for arbitrary types.
2020-05-21 12:16:38 +03:00
Roman Khimov
328c6d7ce5 vm/cli: fix step command
It was setting a (wrong) breakpoint and couldn't then break out of it.
2020-05-21 12:15:50 +03:00
Roman Khimov
318ca55982 vm/cli: add push command to push something to the estack 2020-05-21 12:15:24 +03:00
Roman Khimov
5ec70b9fc2
Merge pull request #930 from nspcc-dev/fix/keys
Cache storage operations across same block tx executions
2020-05-21 11:08:42 +03:00
Evgenii Stratonikov
b96fe8173c core,dao: implement Block-level storage caching
The order in which storage.Find items are returns depends on what items
were processed in previous transactions of the same block.
The easiest way to implement this sort of caching is to cache operations
with storage, flushing the only in `Persist()`.
2020-05-19 17:19:51 +03:00
Roman Khimov
03cc8c118f
Merge pull request #954 from nspcc-dev/fix/struct_fields
compiler: support using OP= with struct fields and slice elements
2020-05-19 16:03:42 +03:00
Roman Khimov
f0d6b0a639
Merge pull request #956 from nspcc-dev/doc-update-2.x
Documentation update for 2.x
2020-05-19 16:02:55 +03:00
Roman Khimov
ddbc9057c8 docs: update RPC document, add notifications spec 2020-05-19 13:13:15 +03:00
Roman Khimov
94d6b5466f docs: update compiler.md, bring it up to date
And add one more reference to it into the main README.
2020-05-19 13:13:15 +03:00
Roman Khimov
0079dfb695 interop: add some top-level doc.go 2020-05-19 13:13:15 +03:00
Roman Khimov
2c921f5277 docs: drop runtime.md
We have now way better godoc for interop functions, so this document makes
little sense and it's not referenced anywhere, so it's safe to drop it.
2020-05-19 13:13:15 +03:00
Roman Khimov
8c19b8f2a1 compiler/interop: add support for working with witnesses 2020-05-19 13:13:15 +03:00
Roman Khimov
085d50b430 interop/util: extend documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
ec2bf7d52e interop/transaction: update documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
a587274351 compiler|transaction: remove transaction.GetUnspentCoins support
It's useless. Even though there is Neo.Transaction.GetUnspentCoins syscall
that can be used, its return type is an interop structure that's not accepted
by any other syscall, so you can't really do anything with it. And there is no
such interface for the .net Framework.
2020-05-19 13:13:15 +03:00
Roman Khimov
514f862b81 compiler|transaction: fix transaction.GetScript build, add to interop
There is no such syscall as Neo.Transaction.GetScript and GetScript should be
available for contract's use.
2020-05-19 13:13:15 +03:00
Roman Khimov
d0a3ce25ff compiler/storage: add read-only related interops 2020-05-19 13:13:15 +03:00
Roman Khimov
07742bdf46 interop/storage: update documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
dff0f724cd interop/runtime: update documentation, fix Notify
Notify doesn't return anything!
2020-05-19 13:13:15 +03:00
Roman Khimov
eb82661f6b compiler/iterator: add missing iterator.Concat function
We have a syscall for it, so it should be exposed.
2020-05-19 13:13:15 +03:00
Roman Khimov
a17bd6176f interop/iterator: documentation update 2020-05-19 13:13:15 +03:00
Roman Khimov
31d7b07eb5 interop/input|output: update documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
6c0553da47 interop/header: update documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
d5d497ccd7 compiler: add support for enumerator interop package 2020-05-19 13:13:15 +03:00
Roman Khimov
8fb4bbca4e interop/enumerator: update doc, fix Next return value 2020-05-19 13:13:15 +03:00
Roman Khimov
5cebd4a7a2 compiler/engine: add dynamic APPCALL generation, fix #914
Previously we could generate dynamic appcall with a kludge of
    AppCall([]byte{/* 20 zeroes */, realScriptHash, args...)

Now there is a separate function for this.
2020-05-19 13:13:15 +03:00
Roman Khimov
78b2387640 interop/engine: update documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
10abac4362 interop/crypto: update documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
f0047b4055 interop/contract: update documentation, fix some interfaces
Some functions were just not correct in their interfaces.
2020-05-19 13:13:15 +03:00
Roman Khimov
a43f2234dd core: fix Neo.Contract.GetStorageContext security check
This syscall should only work for contracts created by current transaction and
that is what is supposed to be checked here. Do so by looking at the
differences between ic.dao and original lower DAO.
2020-05-19 13:13:15 +03:00
Roman Khimov
541d56a874 gitignore: don't ignore blockchain(s) directories
It interferes with interop/blockchain and it's not a default directory for
chain DBs.
2020-05-19 13:13:15 +03:00
Roman Khimov
5d941364bd compiler/interop: expose GetTransactionHeight
And sort syscall names as they change the indentation anyway.
2020-05-19 13:13:15 +03:00
Roman Khimov
80b8b50f02 core: fix Neo.Blockchain.GetValidators implementation
It should return keys, attempting to push []*state.Validator to the stack
would probably lead to failure.
2020-05-19 13:13:15 +03:00
Roman Khimov
9e94895bb0 interop/blockchain: update documentation 2020-05-19 13:13:15 +03:00
Roman Khimov
d09c3b1e27 interop/block: update documentation, fix GetTransaction
GetTransaction never worked with hash, it works with indexes.
2020-05-19 13:13:15 +03:00
Roman Khimov
7abd35b358 compiler: add support for attribute syscalls 2020-05-19 13:13:15 +03:00
Roman Khimov
77c5f28b09 interop/attribute: update documentation 2020-05-19 13:13:14 +03:00
Roman Khimov
f69e654260 compiler: add missing asset syscalls, sort them 2020-05-19 13:13:14 +03:00
Roman Khimov
cbcc8e160f asset: update documentation and fix Create/Renew
Both Create and Renew have things returned from them.
2020-05-19 13:13:14 +03:00
Evgenii Stratonikov
231a5189a2 compiler: allow using OP= with struct fields and slice elements
Do it in a generic way, there is no need in restricting to only
variables.
2020-05-19 12:42:37 +03:00
Roman Khimov
9c46e79745 compiler: add support for account syscalls
Turns out, they never functioned correctly.
2020-05-18 19:08:28 +03:00
Roman Khimov
83df376d17 account: add missing IsStandard interop function
There is a Neo.Account.IsStandard syscall, but we didn't have a wrapper for
it.
2020-05-18 19:08:28 +03:00
Roman Khimov
6dff01672c interop/account: update and extend documentation 2020-05-18 19:08:28 +03:00
Roman Khimov
bd51fe4c11
Merge pull request #955 from nspcc-dev/fix/ws-error-handle
rpc/client: handle client creation error in new wsclient
2020-05-18 16:48:58 +03:00
alexvanin
8db714785d rpc/client: handle client creation error in new wsclient
Client returns error if it can't parse endpoint string. WSClient
should check client error or there could be panic at `cl.cli = nil`
expression.
2020-05-18 16:25:36 +03:00
Roman Khimov
f0abbfd399
Merge pull request #951 from nspcc-dev/notification-filters-2.x
Notification filters 2.x
2020-05-14 20:57:43 +03:00
Roman Khimov
8cd7bc7e07 rpc/client: deduplicate block/header tests a bit
The same data is copied at least three times here.
2020-05-14 17:28:14 +03:00
Roman Khimov
9546e021a9 rpc/block: rework the way Block is JSONized
Our block.Block was JSONized in a bit different fashion than result.Block in
its Nonce and NextConsensus fields. It's not good for notifications because
third-party clients would probably expect to see the same format. Also, using
completely different Block representation in result is probably making our
client a bit weaker as this representation is harder to use with other neo-go
components.

So use the same approach we took for Transactions and wrap block.Base which is
to be serialized in proper way.
2020-05-14 17:28:14 +03:00
Roman Khimov
83febead59 transaction: add json.Unmarshaler to Attribute
It actually was missing and it might affect Transaction conversion to/from
JSON.
2020-05-14 17:28:14 +03:00
Roman Khimov
8f55f0ac76 rpc/server: add notification filters
And check state string correctness on unmarshaling.
2020-05-14 17:28:14 +03:00
Roman Khimov
78716c5335 rpc/client: add support for notification filters
Differing a bit from #895 draft specification, we won't add `verifier` (or
signer) for Neo 2, it's not worth doing so at the moment.
2020-05-14 00:56:26 +03:00
Roman Khimov
da32cff313 rpc/server: don't panic on test failure 2020-05-14 00:56:26 +03:00
Roman Khimov
966ff28091 rpc: add subscriber queue overflow check
It's not practical adding server-side tests for 2.0 (as it requires generating
more blocks), so we'll leave it for 3.0.
2020-05-14 00:56:26 +03:00
Roman Khimov
1d5734c882
Merge pull request #944 from nspcc-dev/notifications-2.x
Notifications 2.x
2020-05-13 17:22:55 +03:00
Roman Khimov
9454ef5c28 core: improve locking in storeBlock
Getting batch, updating Prometheus metrics and pushing events doesn't require
any locking: batch is a local cache batch that no one outside cares about,
Prometheus metrics are not critical to be in perfect sync and events are
asynchronous anyway.
2020-05-13 17:17:41 +03:00
Roman Khimov
5464f9c3ae rpc/client: add notifications support for WSClient
It differs from #895 design in that we have Notifications channel always
exposed as WSClient field, probably it simplifies things a little.
2020-05-13 17:17:41 +03:00
Roman Khimov
e1408b6525 rpc/server: add notification subscription
Note that the protocol differs a bit from #895 in its notifications format,
to avoid additional server-side processing we're omitting some metadata like:
 * block size and confirmations
 * transaction fees, confirmations, block hash and timestamp
 * application execution doesn't have ScriptHash populated

Some block fields may also differ in encoding compared to `getblock` results
(like nonce field).

I think these differences are unnoticieable for most use cases, so we can
leave them as is, but it can be changed in the future.
2020-05-13 17:17:41 +03:00
Roman Khimov
29ada4ca46 smartcontract: add JSON marshal/unmarshal for InteropType
We actually have to do that in order to answer getapplicationlog requests for
transactions that leave some interop items on the stack. It follows the same
logic our binary serializer/deserializes does leaving the type and stripping
the value (whatever that is).
2020-05-13 17:17:41 +03:00
Roman Khimov
d5df1212c2 rpc/server: start and shutdown Server in tests
It will be important for proper subscription testing and it doesn't hurt even
though technically we've got two http servers listening after this change (one
is a regular Server's http.Server and one is httptest's Server). Reusing
rpc.Server would be nice, but it requires some changes to Start sequence to
start Listener with net.Listen and then communicate back its resulting
Addr. It's not very convenient especially given that no other code needs it,
so doing these changes just for a bit cleaner testing seems like and
overkill.

Update config appropriately. Update Start comment along the way.
2020-05-12 17:42:34 +03:00
Roman Khimov
d686fe4e5d network: get blocks directly from the chain for rebroadcasting
Simplify network<->consensus relations, also broadcast blocks received by
other means like RPC.
2020-05-12 17:42:34 +03:00
Roman Khimov
dd8bcfae47 consensus: remove OnNewBlock(), use Blockchain subscription
Get new blocks directly from the Blockchain. It may lead to some duplications
(as we'll also receive our own blocks), but at the same time it's more
correct, because technically we can also get blocks via other means besides
network server like RPC (submitblock call). And it simplifies network server
at the same time.
2020-05-12 17:41:23 +03:00
Roman Khimov
1ac4f8528d core: add Blockchain event subscription mechanism
A deep internal part of #895. Blockchainer interface is also extended for
various uses of these methods.
2020-05-12 17:41:15 +03:00
Roman Khimov
2e58a14978 core: improve documentation a little 2020-05-12 17:20:31 +03:00
Roman Khimov
c85e77e1c9
Merge pull request #945 from nspcc-dev/examples/nep5-transfer-bug
examples: Use "or" operator in transfer check of NEP5 token
2020-05-12 16:44:15 +03:00
alexvanin
bb481e9712 examples: Use "or" operator in transfer check of NEP5 token
CanTransfer function checks if "to" and "from" values are
correct script hashes. If one of these values is correct and one
incorrect, then function returns false positive result. It uses "and"
operator which requires both "to" and "from" script hashes to be
incorrect to fail transaction.

Instead transaction must fail if at least one argument is incorrect,
so it should be "or" operator.
2020-05-12 16:33:24 +03:00
Roman Khimov
3db14b4699 wallet: check for t.GetSignedPart() result correctness
It can return nil easily and signing that is a big mistake.
2020-05-07 21:48:45 +03:00
Roman Khimov
943d435cd2 core: ensure we produce correct blocks for tests
Blocks must have at least one transaction and we should check for correct
merkle root generation.
2020-05-07 21:46:28 +03:00
Roman Khimov
a1d8206ef7
Merge pull request #935 from nspcc-dev/fix/slice
compiler: support implicit type in function arguments
2020-05-06 18:30:01 +03:00
Evgenii Stratonikov
37813f1020 compiler: support implicit type in function arguments
Go supports declaring multiple arguments of the same type without
duplicating type name. Now we support this too.
2020-05-06 18:20:13 +03:00
Roman Khimov
91a3b655b7
Merge pull request #931 from nspcc-dev/fix/voidcall
compiler: allow to use `return` with no arguments
2020-05-06 18:19:16 +03:00
Evgenii Stratonikov
922aff1fcc compiler: support named returns 2020-05-06 18:15:52 +03:00
Evgenii Stratonikov
b2a3cee451 compiler: support using return in some branches
When `return` is used in one codepath, but not the other,
we do not clean altstack properly. This commit fixes it.
2020-05-06 18:15:50 +03:00
Evgenii Stratonikov
2a6be8ceef compiler: allow to use return with no arguments 2020-05-06 17:23:32 +03:00
Roman Khimov
e097e86bfa
Merge pull request #921 from nspcc-dev/rpc-websocket-2.x
RPC over websocket (2.x)
2020-05-04 13:55:19 +03:00
Roman Khimov
a025838e52
Merge pull request #916 from nspcc-dev/compiler/generate_abi
compiler: add ability to generate .abi.json file
2020-05-04 12:02:50 +03:00
Anna Shaleva
99d0bafa2c compiler: add ability to generate .abi.json file
A part of integration with NEO Blockchain Toolkit (see #902). To be
able to deploy smart-contract compiled with neo-go compiler via NEO
Express, we have to generate additional .abi.json file. This file
contains the following information:
 - hash of the compiled contract
 - smart-contract metadata (title, description, version, author,
email, has-storage, has-dynamic-invoke, is-payable)
 - smart-contract entry point
 - functions
 - events

However, this .abi.json file is slightly different from the one,
described in manifest.go, so we have to add auxilaury stractures for
json marshalling. The .abi.json format used by NEO-Express is described
[here](https://github.com/neo-project/neo-devpack-dotnet/blob/master/src/Neo.Compiler.MSIL/FuncExport.cs#L66).
2020-05-04 08:31:14 +03:00
Anna Shaleva
592fd068b1 compiler: fix bug with missing methods parameters
Method `methodInfoFromScope(...)` always returned an empty parameters
set, so we were missing this information in both .abi.json and
.debug.json files. Fixed now.
2020-05-04 08:31:09 +03:00
Roman Khimov
556ab39a5a rpc/client: add minimalistic websocket client 2020-04-30 22:59:19 +03:00
Roman Khimov
6333060897 rpc/client: separate out http-related functionality 2020-04-30 22:59:19 +03:00
Roman Khimov
38f9b511ae rpc/client: drop Version from Options
It makes no sense at all, it's a JSON-RPC version.
2020-04-30 22:59:19 +03:00
Roman Khimov
e32367a1c2 rpc/client: fix some comments 2020-04-30 22:59:19 +03:00
Roman Khimov
a49f2bc47c client: remove Balancer getter/setter, make it an Option
Keep it internal to the client instance, it makes no sense exposing it to the
outside user.
2020-04-30 22:59:19 +03:00
Roman Khimov
00d6439eb2 client: make http.Client internal to the Client
Exposing it the outside users is strange, so incapsulate it completely.

Fix DialTimeout setting along the way, handle negative timeouts as invalid.
2020-04-30 22:59:01 +03:00
Roman Khimov
24b46c6041 rpc/server: add websockets support via '/ws' URL 2020-04-30 17:53:28 +03:00
Roman Khimov
3eb6d266ca rpc/server: use httptest.Server for testing
Which allows to reuse it for websockets.
2020-04-30 17:52:42 +03:00
Roman Khimov
3d2a2af575 rpc: shuffle handleHttpRequest/handleRequest responsibilities
Make handleRequest reusable in other contexts like websockets.
2020-04-30 17:52:15 +03:00
Roman Khimov
dbe7be5b00 rpc: change handlers to always return response.Error for errors
As it's expected by WriteErrorResponse() actually.
2020-04-30 17:52:15 +03:00
Roman Khimov
21c31d7d24 rpc/server: refactor handler methods a little
request.In is a natural request representation, one can always get
request.Params from it.
2020-04-30 17:52:13 +03:00
Anna Shaleva
87b0e76a8c rpc, smartcontract: move contract metadata to smartcontract package 2020-04-29 13:50:05 +03:00
Roman Khimov
8f417c8e8b
Merge pull request #910 from nspcc-dev/master-2.x-update-readme
README: update with workshop links and block import examples
2020-04-28 09:00:06 +03:00
Roman Khimov
92a99624c4 README: update with workshop links and block import examples
Fix some heading along the way.
2020-04-27 16:24:21 +03:00
Roman Khimov
24675d0688
Merge pull request #862 from nspcc-dev/neo2x/partial_consensus_message_decoding
consensus: partial consensus message decoding
2020-04-16 18:41:47 +03:00
Anna Shaleva
c813873577 consensus: added partial message decoding
closes #849
2020-04-16 17:16:29 +03:00
Roman Khimov
84edcf0b0e
Merge pull request #848 from nspcc-dev/cli-invoke-nomethod-check-2.x
cli: check for method presence for invokefunction commands
2020-04-13 11:48:56 +03:00
Roman Khimov
2b943b76ae cli: check for method presence for invokefunction commands
Fixes #828.
2020-04-13 11:21:11 +03:00
Roman Khimov
71fb153a69
Merge pull request #846 from nspcc-dev/fix/gomod2
*: go mod tidy
2020-04-10 17:51:43 +03:00
Evgenii Stratonikov
51512c73ed *: go mod tidy
Add `github.com/dgraph-io/badger` in go.mod .
2020-04-10 15:52:34 +03:00
Roman Khimov
4b5cc0b460
Merge pull request #843 from nspcc-dev/master-2.x-readme
README/docs: add 3.0 notice, update links to relative
2020-04-10 10:34:08 +03:00
Roman Khimov
1433c795c2 README/docs: add 3.0 notice, update links to relative
Goreportcard doesn't support branching, sorry: https://github.com/gojp/goreportcard/issues/96
And Godoc too: https://github.com/golang/gddo/issues/593
2020-04-09 20:01:55 +03:00
1153 changed files with 43492 additions and 160441 deletions

135
.circleci/config.yml Normal file
View file

@ -0,0 +1,135 @@
version: 2.1
orbs:
codecov: codecov/codecov@1.0.5
executors:
go1_13:
docker:
- image: circleci/golang:1.13
environment:
GO111MODULE: "on"
go1_14:
docker:
- image: circleci/golang:1.14
environment:
GO111MODULE: "on"
commands:
gomod:
steps:
- restore_cache:
keys: [deps-]
- run:
name: Download go module dependencies
command: go mod download
- save_cache:
key: deps-{{ checksum "go.sum" }}-{{ checksum "go.sum" }}
paths: [/go/pkg/mod]
jobs:
lint:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_14
steps:
- checkout
- gomod
- run:
name: go-lint
command: |
go get -u -v golang.org/x/lint/golint
golint -set_exit_status ./...
vet:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_14
steps:
- checkout
- gomod
- run:
name: go-vet
command: go vet ./...
test_1_13:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_13
steps:
- checkout
- run: git submodule sync
- run: git submodule update --init
- gomod
- run: go test -v -race ./...
test_1_14:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_14
steps:
- checkout
- run: git submodule sync
- run: git submodule update --init
- gomod
- run: go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic
- codecov/upload:
file: coverage.txt
build_cli:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_14
steps:
- checkout
- gomod
- run: make build
- store_artifacts:
path: bin
destination: /
build_image:
working_directory: /go/src/github.com/nspcc-dev/neo-go
executor: go1_14
docker:
- image: golang:1-alpine
steps:
- run: apk update && apk add git make curl tar
- checkout
- gomod
- setup_remote_docker
- run:
name: Install Docker client
command: |
set -x
VER="17.03.0-ce"
curl -L -o /tmp/docker-$VER.tgz https://get.docker.com/builds/Linux/x86_64/docker-$VER.tgz
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
mv /tmp/docker/* /usr/bin
- run: make image
workflows:
version: 2
workflow:
jobs:
- vet:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- lint:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- test_1_13:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- test_1_14:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- build_cli:
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/
- build_image:
requires:
- build_cli
filters:
tags:
only: v/[0-9]+\.[0-9]+\.[0-9]+/

Binary file not shown.

Binary file not shown.

View file

@ -1,7 +1,7 @@
version: '2.4'
networks:
default:
neo_go_network:
name: neo_go_network
ipam:
config:
@ -21,6 +21,9 @@ services:
- ../config/protocol.privnet.docker.one.yml:/config/protocol.privnet.yml
- ./wallets/wallet1.json:/wallet1.json
- volume_chain:/chains
networks:
neo_go_network:
ipv4_address: 172.200.0.1
ports:
- 20333:20333
- 30333:30333
@ -33,6 +36,9 @@ services:
- ../config/protocol.privnet.docker.two.yml:/config/protocol.privnet.yml
- ./wallets/wallet2.json:/wallet2.json
- volume_chain:/chains
networks:
neo_go_network:
ipv4_address: 172.200.0.2
ports:
- 20334:20334
- 30334:30334
@ -45,6 +51,9 @@ services:
- ../config/protocol.privnet.docker.three.yml:/config/protocol.privnet.yml
- ./wallets/wallet3.json:/wallet3.json
- volume_chain:/chains
networks:
neo_go_network:
ipv4_address: 172.200.0.3
ports:
- 20335:20335
- 30335:30335
@ -57,6 +66,9 @@ services:
- ../config/protocol.privnet.docker.four.yml:/config/protocol.privnet.yml
- ./wallets/wallet4.json:/wallet4.json
- volume_chain:/chains
networks:
neo_go_network:
ipv4_address: 172.200.0.4
ports:
- 20336:20336
- 30336:30336
@ -67,8 +79,12 @@ services:
command: "node --config-path /config --privnet"
volumes:
- ../config/protocol.privnet.docker.single.yml:/config/protocol.privnet.yml
- ./wallets/wallet1_solo.json:/wallet1.json
- ./wallets/wallet1.json:/wallet1.json
- volume_chain:/chains
- ./1600-privnet-blocks-single.acc.gz:/privnet-blocks.acc.gz
networks:
neo_go_network:
ipv4_address: 172.200.0.1
ports:
- 20333:20333
- 30333:30333

View file

@ -1,15 +0,0 @@
#!C:\Program Files\PowerShell\7\pwsh.EXE -File
$bin = '/usr/bin/neo-go.exe'
for ( $i = 0; $i -lt $args.count; $i++ ) {
if ($args[$i] -eq "node"){
Write-Host "=> Try to restore blocks before running node"
if (($Env:ACC -ne $null) -and (Test-Path $Env:ACC -PathType Leaf)) {
& $bin db restore -p --config-path /config -i $Env:ACC
}
break
}
}
& $bin $args

View file

@ -2,14 +2,18 @@
BIN=/usr/bin/neo-go
if [ -z "$ACC" ]; then
ACC=/6000-privnet-blocks.acc.gz
fi
case $@ in
"node"*)
echo "=> Try to restore blocks before running node"
if [ -f "$ACC" ]; then
gunzip --stdout "$ACC" >/privnet.acc
${BIN} db restore -p --config-path /config -i /privnet.acc
fi
;;
"node"*)
echo "=> Try to restore blocks before running node"
if test -f $ACC; then
gunzip --stdout /$ACC > /privnet.acc
${BIN} db restore -p --config-path /config -i /privnet.acc
fi
;;
esac
${BIN} "$@"

View file

@ -1,55 +1 @@
{
"version": "1.0",
"accounts": [
{
"address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn",
"key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY",
"label": "",
"contract": {
"script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
},
{
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
"key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY",
"label": "",
"contract": {
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
},
{
"name": "parameter1",
"type": "Signature"
},
{
"name": "parameter2",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
}
}
{"name":"wallet1","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}

View file

@ -1,72 +1 @@
{
"version": "1.0",
"accounts": [
{
"address": "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn",
"key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY",
"label": "",
"contract": {
"script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
},
{
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
"key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY",
"label": "",
"contract": {
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
},
{
"name": "parameter1",
"type": "Signature"
},
{
"name": "parameter2",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
},
{
"address": "NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP",
"key": "6PYM8VdX2BSm7BSXKzV4Fz6S3R9cDLLWNrD9nMjxW352jEv3fsC8N3wNLY",
"label": "",
"contract": {
"script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUGe0Nw6",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
}
}
{"name":"wallet1","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null},{"address":"AbU69m8WUZJSWanfr1Cy66cpEcsmMcX7BR","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"512102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc251ae","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}

View file

@ -1,55 +1 @@
{
"version": "1.0",
"accounts": [
{
"address": "NMUedC8TSV2rE17wGguSvPk9XcmHSaT275",
"key": "6PYSYoZaxqDu5vqvm7yUFT3sPJJFwyLyYDnp8zwj1YVPcBWxacz64bNX59",
"label": "",
"contract": {
"script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG5BVuezJw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
},
{
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
"key": "6PYSYoZaxqDu5vqvm7yUFT3sPJJFwyLyYDnp8zwj1YVPcBWxacz64bNX59",
"label": "",
"contract": {
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
},
{
"name": "parameter1",
"type": "Signature"
},
{
"name": "parameter2",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
}
}
{"name":"wallet2","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AWLYWXB8C9Lt1nHdDZJnC5cpYJjgRDLk17","label":null,"isDefault":false,"lock":false,"key":"6PYXHjPaNvW8YknSXaKsTWjf9FRxo1s4naV2jdmSQEgzaqKGX368rndN3L","contract":{"script":"2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406eac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYXHjPaNvW8YknSXaKsTWjf9FRxo1s4naV2jdmSQEgzaqKGX368rndN3L","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}

View file

@ -1,55 +1 @@
{
"version": "1.0",
"accounts": [
{
"address": "NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo",
"key": "6PYVxMnzPMFTYY16xRvXm2SJcvaChabLzaARAb1Mmej9U7rYLYWMSPtfam",
"label": "",
"contract": {
"script": "DCED2QwH32PmkM53kS4Qq1GsyUS2aGAje2CMT4+DCece5plBVuezJw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
},
{
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
"key": "6PYVxMnzPMFTYY16xRvXm2SJcvaChabLzaARAb1Mmej9U7rYLYWMSPtfam",
"label": "",
"contract": {
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
},
{
"name": "parameter1",
"type": "Signature"
},
{
"name": "parameter2",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
}
}
{"name":"wallet3","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AR3uEnLUdfm1tPMJmiJQurAXGL7h3EXQ2F","label":null,"isDefault":false,"lock":false,"key":"6PYX86vYiHfUbpD95hfN1xgnvcSxy5skxfWYKu3ztjecxk6ikYs2kcWbeh","contract":{"script":"2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYX86vYiHfUbpD95hfN1xgnvcSxy5skxfWYKu3ztjecxk6ikYs2kcWbeh","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}

View file

@ -1,55 +1 @@
{
"version": "1.0",
"accounts": [
{
"address": "NPrB7BmTMYxf9UVroJp4RQExM9tqKmsHTz",
"key": "6PYX8eELiDduPW3RGiZxZNmG6KtWtXkyRFi47f8w6quEBpRkpBPxH5u5AP",
"label": "",
"contract": {
"script": "DCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWJBVuezJw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
},
{
"address": "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq",
"key": "6PYX8eELiDduPW3RGiZxZNmG6KtWtXkyRFi47f8w6quEBpRkpBPxH5u5AP",
"label": "",
"contract": {
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEGe0Nw6",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
},
{
"name": "parameter1",
"type": "Signature"
},
{
"name": "parameter2",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isDefault": false
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
}
}
{"name":"wallet4","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AJmjUqf1jDenxYpuNS4i2NxD9FQYieDpBF","label":null,"isDefault":false,"lock":false,"key":"6PYRXVwHSqFSukL3CuXxdQ75VmsKpjeLgQLEjt83FrtHf1gCVphHzdD4nc","contract":{"script":"2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYRXVwHSqFSukL3CuXxdQ75VmsKpjeLgQLEjt83FrtHf1gCVphHzdD4nc","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @AnnaShaleva @roman-khimov

BIN
.github/logo_dark.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -1,152 +0,0 @@
name: Build
on:
pull_request:
branches:
- master
types: [opened, synchronize]
paths-ignore:
- 'scripts/**'
- '**/*.md'
push:
# Build for the master branch.
branches:
- master
release:
# Publish released commit as Docker `latest` and `git_revision` images.
types:
- published
workflow_dispatch:
inputs:
ref:
description: 'Ref to build CLI for Ubuntu and Windows Server Core [default: latest master; examples: v0.92.0, 0a4ff9d3e4a9ab432fd5812eb18c98e03b5a7432]'
required: false
default: ''
push_image:
description: 'Push images to DockerHub [default: false; examples: true, false]'
required: false
default: 'false'
use_latest_tag:
description: 'Use `latest` tag while pushing images to DockerHub (applied to Ubuntu image only) [default: false; examples: true, false]'
required: false
default: 'false'
jobs:
build_cli:
name: Build CLI
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 }]
arch: [amd64, arm64]
exclude:
- os: { name: windows-2022, bin-name: windows }
arch: 'arm64'
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
# Allows to fetch all history for all branches and tags. Need this for proper versioning.
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Build CLI
run: make build
env:
GOARCH: ${{ matrix.arch }}
- name: Rename CLI binary
run: mv ./bin/neo-go* ./bin/neo-go-${{ matrix.os.bin-name }}-${{ matrix.arch }}${{ (matrix.os.bin-name == 'windows' && '.exe') || '' }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: neo-go-${{ matrix.os.bin-name }}-${{ matrix.arch }}
path: ./bin/neo-go*
if-no-files-found: error
- name: Attach binary to the release as an asset
if: ${{ github.event_name == 'release' }}
run: gh release upload ${{ github.event.release.tag_name }} ./bin/neo-go-${{ matrix.os.bin-name }}-${{ matrix.arch }}${{ (matrix.os.bin-name == 'windows' && '.exe') || '' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_image:
needs: build_cli
name: Build and push docker image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Set vars
id: setvars
run: make gh-docker-vars >> $GITHUB_OUTPUT
- name: Set latest tag
id: setlatest
if: ${{ (github.event_name == 'release' && github.event.release.target_commitish == 'master') || (github.event_name == 'workflow_dispatch' && github.event.inputs.use_latest_tag == 'true') }}
run: echo "latest=,${{ steps.setvars.outputs.repo }}:latest" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
platforms: linux/amd64,linux/arm64
build-args: |
REPO=github.com/${{ github.repository }}
VERSION=${{ steps.setvars.outputs.version }}
tags: ${{ steps.setvars.outputs.repo }}:${{ steps.setvars.outputs.version }}${{ steps.setvars.outputs.suffix }}${{ steps.setlatest.outputs.latest }}
build_image_wsc:
needs: build_cli
name: Build and push docker image (Windows Server Core)
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
fetch-depth: 0
# For proper `deps` make target execution.
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Login to DockerHub
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build Docker image
run: make image
- name: Push image to registry
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_image == 'true') }}
run: make image-push

View file

@ -1,11 +0,0 @@
name: Contribution guidelines
on:
pull_request:
branches:
- master
jobs:
commits_check_job:
name: DCO check
uses: nspcc-dev/.github/.github/workflows/dco.yml@master

View file

@ -1,190 +0,0 @@
name: Tests
on:
push:
branches: [ master ]
pull_request:
branches:
- master
types: [opened, synchronize]
paths-ignore:
- 'scripts/*.sh'
- '**/*.md'
workflow_dispatch:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
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.
gomodcheck:
name: Check internal dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- 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
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: Install stringer
run: go install golang.org/x/tools/cmd/stringer@latest
- name: Run go generate
run: go generate ./...
- name: Check that autogenerated code is up-to-date
run: |
if [[ $(git diff --name-only | grep '' -c) != 0 ]]; then
echo "Fresh version of autogenerated code should be committed for the following files:";
git diff --name-only;
exit 1;
fi
codeql:
name: CodeQL
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
test_cover:
name: Coverage
runs-on: ubuntu-22.04
env:
CGO_ENABLED: 0
GOEXPERIMENT: nocoverageredesign
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: 'true'
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
- name: Write coverage profile
run: go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
- name: Upload coverage results to Codecov
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true # if something is wrong on uploading codecov results, then this job will fail
files: ./coverage.txt
slug: nspcc-dev/neo-go
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
tests:
name: Run tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-2022, macos-12, macos-14]
go_versions: [ '1.20', '1.21', '1.22' ]
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'
- os: macos-14
go_versions: '1.20'
- os: macos-14
go_versions: '1.21'
# Exclude latest Go version for Ubuntu as Coverage uses it.
- os: ubuntu-22.04
go_versions: '1.22'
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: 'true'
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '${{ matrix.go_versions }}'
- name: Run tests
run: go test -timeout 15m -v -race ./...

28
.gitignore vendored
View file

@ -7,6 +7,9 @@
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Added by CoZ developers
vendor/
bin/
@ -25,8 +28,9 @@ bin/
*~
TAGS
# storage
/chains
# leveldb
chains/
chain/
# patch
*.orig
@ -35,23 +39,3 @@ TAGS
# Coverage
coverage.txt
coverage.html
# Compiler output
examples/*/*.nef
examples/*/*.json
# Fuzzing testdata.
testdata/
!cli/testdata
!internal/basicchain/testdata
!pkg/compiler/testdata
!pkg/config/testdata
!pkg/consensus/testdata
!pkg/services/rpcsrv/testdata
!pkg/services/notary/testdata
!pkg/services/oracle/testdata
!pkg/smartcontract/testdata
!cli/smartcontract/testdata
pkg/vm/testdata/fuzz
!pkg/vm/testdata
!pkg/wallet/testdata

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
branch = master
branch = master-2.x

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"

16
.travis.yml Normal file
View file

@ -0,0 +1,16 @@
language: go
go:
- 1.14.x
env:
- GO111MODULE=on
install:
- go get -v golang.org/x/lint/golint
- go mod tidy -v
script:
- golint -set_exit_status ./...
- go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
matrix:
allow_failures:
- go: tip

File diff suppressed because it is too large Load diff

View file

@ -3,23 +3,15 @@
First, thank you for contributing! We love and encourage pull requests from everyone. Please
follow the guidelines:
1. Check open [issues](https://github.com/nspcc-dev/neo-go/issues) and
1. Check the open [issues](https://github.com/nspcc-dev/neo-go/issues) and
[pull requests](https://github.com/nspcc-dev/neo-go/pulls) for existing discussions.
1. Open an issue first to discuss a new feature or enhancement.
1. Write tests and make sure the test suite passes locally and on CI.
1. When optimizing something, write benchmarks and attach the results:
```
go test -run - -bench BenchmarkYourFeature -count=10 ./... >old // on master
go test -run - -bench BenchmarkYourFeature -count=10 ./... >new // on your branch
benchstat old new
```
`benchstat` is described here https://godocs.io/golang.org/x/perf/cmd/benchstat.
1. Open a pull request and reference the relevant issue(s).
1. Open an issue first, to discuss a new feature or enhancement.
1. Write tests, and make sure the test suite passes locally and on CI.
1. Open a pull request, and reference the relevant issue(s).
1. Make sure your commits are logically separated and have good comments
explaining the details of your change. Add a package/file prefix to your
commit if that's applicable (like 'vm: fix ADD miscalculation on full
moon').
1. After receiving a feedback, amend your commits or add new ones as
1. After receiving feedback, amend your commits or add new ones as
appropriate.
1. **Have fun!**

View file

@ -1,9 +1,5 @@
# Builder image
# Keep go version in sync with Build GA job.
FROM golang:1.22-alpine as builder
# Display go version for information purposes.
RUN go version
FROM golang:1-alpine as builder
RUN set -x \
&& apk add --no-cache git make \
@ -16,17 +12,19 @@ WORKDIR /neo-go
ARG REPO=repository
ARG VERSION=dev
RUN VERSION=$VERSION REPO=$REPO make build
RUN make
# Executable image
FROM alpine
ARG VERSION=dev
ARG VERSION
LABEL version=$VERSION
WORKDIR /
COPY --from=builder /neo-go/config /config
COPY --from=builder /neo-go/.docker/6000-privnet-blocks.acc.gz /6000-privnet-blocks.acc.gz
COPY --from=builder /neo-go/.docker/1600-privnet-blocks-single.acc.gz /1600-privnet-blocks-single.acc.gz
COPY --from=builder /neo-go/.docker/privnet-entrypoint.sh /usr/bin/privnet-entrypoint.sh
COPY --from=builder /neo-go/bin/neo-go /usr/bin/neo-go
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

View file

@ -1,37 +0,0 @@
# Builder image
# Keep go version in sync with Build GA job.
FROM golang:1.22.0-windowsservercore-ltsc2022 as builder
COPY . /neo-go
WORKDIR /neo-go
ARG REPO=repository
ARG VERSION=dev
SHELL ["cmd", "/S", "/C"]
RUN go env -w CGO_ENABLED=0
ENV GOGC=off
RUN go build -trimpath -v -o ./bin/neo-go.exe -ldflags="-X %REPO%/pkg/config.Version=%VERSION%" ./cli/main.go
# Executable image
FROM mcr.microsoft.com/windows/servercore:ltsc2022
ARG VERSION
LABEL version=%VERSION%
WORKDIR /
COPY --from=builder /neo-go/config /config
COPY --from=builder /neo-go/.docker/privnet-entrypoint.ps1 /usr/bin/privnet-entrypoint.ps1
COPY --from=builder /neo-go/bin/neo-go.exe /usr/bin/neo-go.exe
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';", "$ProgressPreference = 'SilentlyContinue';"]
# Check executable version.
RUN /usr/bin/neo-go.exe --version
ENTRYPOINT ["powershell", "-File", "/usr/bin/privnet-entrypoint.ps1"]
CMD ["node", "--config-path", "/config", "--privnet"]

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018-2023 NeoSPCC (@nspcc-dev), Anthony De Meulemeester (@anthdm), City of Zion community (@CityOfZion)
Copyright (c) 2018 Anthony De Meulemeester (@anthdm) & City of Zion community (@CityOfZion)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

100
Makefile
View file

@ -1,49 +1,32 @@
BRANCH = "master"
REPONAME = "neo-go"
NETMODE ?= "privnet"
BINARY=neo-go
BINARY_PATH=./bin/$(BINARY)$(shell go env GOEXE)
GO_VERSION ?= 1.20
BINARY = "./bin/neo-go"
DESTDIR = ""
SYSCONFIGDIR = "/etc"
BINDIR = "/usr/bin"
SYSTEMDUNIT_DIR = "/lib/systemd/system"
UNITWORKDIR = "/var/lib/neo-go"
IMAGE_SUFFIX="$(shell if [ "$(OS)" = Windows_NT ]; then echo "_WindowsServerCore"; fi)"
D_FILE ?= "$(shell if [ "$(OS)" = Windows_NT ]; then echo "Dockerfile.wsc"; else echo "Dockerfile"; fi)"
DC_FILE ?= ".docker/docker-compose.yml" # Single docker-compose for Ubuntu/WSC, should be kept in sync with ENV_IMAGE_TAG.
ENV_IMAGE_TAG="env_neo_go_image"
DC_FILE=.docker/docker-compose.yml
REPO ?= "$(shell go list -m)"
VERSION ?= "$(shell git describe --tags --match "v*" --abbrev=8 2>/dev/null | sed -r 's,^v([0-9]+\.[0-9]+)\.([0-9]+)(-.*)?$$,\1 \2 \3,' | while read mm patch suffix; do if [ -z "$$suffix" ]; then echo $$mm.$$patch; else patch=`expr $$patch + 1`; echo $$mm.$${patch}-pre$$suffix; fi; done)"
MODVERSION ?= "$(shell cat go.mod | cat go.mod | sed -r -n -e 's|.*pkg/interop (.*)|\1|p')"
BUILD_FLAGS = "-X '$(REPO)/pkg/config.Version=$(VERSION)' -X '$(REPO)/cli/smartcontract.ModVersion=$(MODVERSION)'"
VERSION ?= "$(shell git describe --tags 2>/dev/null | sed 's/^v//')"
BUILD_FLAGS = "-X '$(REPO)/pkg/config.Version=$(VERSION)'"
IMAGE_REPO=nspccdev/neo-go
# All of the targets are phony here because we don't really use make dependency
# tracking for files
.PHONY: build $(BINARY) deps image docker/$(BINARY) image-latest image-push image-push-latest clean-cluster \
test vet lint fmt cover version gh-docker-vars
.PHONY: build deps image check-version clean-cluster push-tag push-to-registry \
run run-cluster test vet lint fmt cover
build: deps
@echo "=> Building binary"
@set -x \
&& export GOGC=off \
&& export CGO_ENABLED=0 \
&& go build -trimpath -v -ldflags $(BUILD_FLAGS) -o ${BINARY_PATH} ./cli/main.go
$(BINARY): build
docker/$(BINARY):
@echo "=> Building binary using clean Docker environment"
@docker run --rm -t \
-v `pwd`:/src \
-w /src \
-u "$$(id -u):$$(id -g)" \
--env HOME=/src \
golang:$(GO_VERSION) make $(BINARY)
&& go build -trimpath -v -mod=vendor -ldflags $(BUILD_FLAGS) -o ${BINARY} ./cli/main.go
neo-go.service: neo-go.service.template
@sed -r -e 's_BINDIR_$(BINDIR)_' -e 's_UNITWORKDIR_$(UNITWORKDIR)_' -e 's_SYSCONFIGDIR_$(SYSCONFIGDIR)_' $< >$@
@ -56,7 +39,7 @@ install: build neo-go.service
&& cp ./config/protocol.mainnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \
&& cp ./config/protocol.privnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \
&& cp ./config/protocol.testnet.yml $(DESTDIR)$(SYSCONFIGDIR)/neo-go \
&& install -m 0755 -t $(BINDIR) $(BINARY_PATH) \
&& install -m 0755 -t $(BINDIR) $(BINARY) \
postinst: install
@echo "=> Preparing directories and configs"
@ -67,39 +50,38 @@ postinst: install
image: deps
@echo "=> Building image"
@echo " Dockerfile: $(D_FILE)"
@echo " Tag: $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)"
@docker build -f $(D_FILE) -t $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX) --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) .
image-latest: deps
@echo "=> Building image with 'latest' tag"
@echo " Dockerfile: Dockerfile" # Always use default Dockerfile for Ubuntu as `latest`.
@echo " Tag: $(IMAGE_REPO):latest"
@docker build -t $(IMAGE_REPO):latest --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) .
@docker build -t $(IMAGE_REPO):$(VERSION) --build-arg REPO=$(REPO) --build-arg VERSION=$(VERSION) .
image-push:
@echo "=> Publish image"
@echo " Tag: $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)"
@docker push $(IMAGE_REPO):$(VERSION)$(IMAGE_SUFFIX)
image-push-latest:
@echo "=> Publish image for Ubuntu with 'latest' tag"
@docker push $(IMAGE_REPO):latest
@docker push $(IMAGE_REPO):$(VERSION)
deps:
@CGO_ENABLED=0 \
go mod download
@CGO_ENABLED=0 \
go mod tidy -v
check-version:
git fetch && (! git rev-list ${VERSION})
version:
@echo $(VERSION)
@echo ${VERSION}
gh-docker-vars:
@echo "file=$(D_FILE)"
@echo "version=$(VERSION)"
@echo "repo=$(IMAGE_REPO)"
@echo "suffix=$(IMAGE_SUFFIX)"
deps:
@go mod tidy -v
@go mod vendor
push-tag:
git checkout ${BRANCH}
git pull origin ${BRANCH}
git tag ${VERSION}
git push origin ${VERSION}
push-to-registry:
@docker login -e ${DOCKER_EMAIL} -u ${DOCKER_USER} -p ${DOCKER_PASS}
@docker tag CityOfZion/${REPONAME}:latest CityOfZion/${REPONAME}:${CIRCLE_TAG}
@docker push CityOfZion/${REPONAME}:${CIRCLE_TAG}
@docker push CityOfZion/${REPONAME}
run: build
${BINARY} node -config-path ./config -${NETMODE}
test:
@go test ./... -cover
@ -108,29 +90,31 @@ vet:
@go vet ./...
lint:
@golangci-lint run
@go list ./... | xargs -L1 golint -set_exit_status
fmt:
@gofmt -l -w -s $$(find . -type f -name '*.go'| grep -v "/vendor/")
cover:
@go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./pkg/...,./cli/...
@go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic
@go tool cover -html=coverage.txt -o coverage.html
# --- Ubuntu/Windows environment ---
env_image:
# --- Environment ---
env_vendor:
@echo "=> Update vendor"
@go mod tidy
@go mod download
@go mod vendor
env_image: env_vendor
@echo "=> Building env image"
@echo " Dockerfile: $(D_FILE)"
@echo " Tag: $(ENV_IMAGE_TAG)"
@docker build \
-f $(D_FILE) \
-t $(ENV_IMAGE_TAG) \
-t env_neo_go_image \
--build-arg REPO=$(REPO) \
--build-arg VERSION=$(VERSION) .
env_up:
@echo "=> Bootup environment"
@echo " Docker-compose file: $(DC_FILE)"
@docker-compose -f $(DC_FILE) up -d node_one node_two node_three node_four
env_single:

164
README.md
View file

@ -1,93 +1,88 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./.github/logo_dark.png">
<source media="(prefers-color-scheme: light)" srcset="./.github/logo_light.png">
<img src="./.github/logo_light.png" width="300px" alt="NeoGo logo">
</picture>
<img src="./.github/neo_color_dark_gopher.png" width="300px" alt="logo">
</p>
<p align="center">
<b>Go</b> Node and SDK for the <a href="https://neo.org">Neo</a> blockchain.
<b>Go</b> Node and SDK for the <a href="https://neo.org">NEO</a> blockchain.
</p>
<hr />
[![codecov](https://codecov.io/gh/nspcc-dev/neo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/nspcc-dev/neo-go)
[![GithubWorkflows Tests](https://github.com/nspcc-dev/neo-go/actions/workflows/tests.yml/badge.svg)](https://github.com/nspcc-dev/neo-go/actions/workflows/tests.yml)
[![Report](https://goreportcard.com/badge/github.com/nspcc-dev/neo-go)](https://goreportcard.com/report/github.com/nspcc-dev/neo-go)
[![GoDoc](https://godoc.org/github.com/nspcc-dev/neo-go?status.svg)](https://godoc.org/github.com/nspcc-dev/neo-go)
[![codecov](https://codecov.io/gh/nspcc-dev/neo-go/branch/master-2.x/graph/badge.svg)](https://codecov.io/gh/nspcc-dev/neo-go/branch/master-2.x)
[![CircleCI](https://circleci.com/gh/nspcc-dev/neo-go/tree/master-2.x.svg?style=svg)](https://circleci.com/gh/nspcc-dev/neo-go/tree/master-2.x)
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nspcc-dev/neo-go?sort=semver)
![License](https://img.shields.io/github/license/nspcc-dev/neo-go.svg?style=popout)
# Overview
NeoGo is a complete platform for distributed application development built on
top of and compatible with the [Neo project](https://github.com/neo-project).
This includes, but not limited to (see documentation for more details):
This project aims to be a full port of the original C# [Neo project](https://github.com/neo-project).
A complete toolkit for the NEO blockchain, including:
- [Consensus node](docs/consensus.md)
- [RPC node & client](docs/rpc.md)
- [CLI tool](docs/cli.md)
- [Smart contract compiler](docs/compiler.md)
- [Neo virtual machine](docs/vm.md)
- [Smart contract examples](examples/README.md)
- [Oracle service](docs/oracle.md)
- [State validation service](docs/stateroots.md)
- [NEO virtual machine](docs/vm.md)
The protocol implemented here is Neo N3-compatible, however you can also find
an implementation of the Neo Legacy protocol in the [**master-2.x**
branch](https://github.com/nspcc-dev/neo-go/tree/master-2.x) and releases
before 0.80.0 (**0.7X.Y** track).
This branch (**master-2.x**) is a stable version of the project compatible
with Neo 2 (including [cross-chain neox support](docs/neox.md)), it only
receives bug fixes and minor updates. For Neo 3
development version please refer to the [**master**
branch](https://github.com/nspcc-dev/neo-go/tree/master) and releases
after 0.90.0. Releases before 0.80.0 (**0.7X.Y** track) are made from this
branch and only contain Neo 2 code.
# Getting started
## Installation
NeoGo is distributed as a single binary that includes all the functionality
provided (but smart contract compiler requires Go compiler to operate). You
can grab it from [releases
page](https://github.com/nspcc-dev/neo-go/releases), use a Docker image (see
[Docker Hub](https://hub.docker.com/r/nspccdev/neo-go) for various releases of
NeoGo, `:latest` points to the latest release) or build yourself.
Go: 1.13+
Install dependencies.
`neo-go` uses [GoModules](https://github.com/golang/go/wiki/Modules) as dependency manager:
```
make deps
```
## How to setup a node
### Docker
Each tagged build is built to docker hub and the `:latest` tag pointing at the latest tagged build.
By default the `CMD` is set to run a node on `testnet`, so to do this simply run:
```bash
docker run -d --name neo-go -p 20332:20332 -p 20333:20333 cityofzion/neo-go
```
Which will start a node on `testnet` and expose the nodes port `20333` and `20332` for the `JSON-RPC` server.
### Building
Building NeoGo requires Go 1.20+ and `make`:
Build the **neo-go** CLI:
```
make
make build
```
The resulting binary is `bin/neo-go`. Notice that using some random revision
from the `master` branch is not recommended (it can have any number of
incompatibilities and bugs depending on the development stage), please use
tagged released versions.
### Running
#### Building on Windows
To build NeoGo on Windows platform we recommend you to install `make` from [MinGW
package](https://osdn.net/projects/mingw/). Then, you can build NeoGo with:
Quick start a NEO node on the private network. This requires the [neo-privatenet](https://hub.docker.com/r/cityofzion/neo-privatenet/) Docker image running on your machine.
```
make
make run
```
The resulting binary is `bin/neo-go.exe`.
## Running a node
A node needs to connect to some network, either local one (usually referred to
as `privnet`) or public (like `mainnet` or `testnet`). Network configuration
is stored in a file and NeoGo allows you to store multiple files in one
directory (`./config` by default) and easily switch between them using network
flags.
To start Neo node on a private network, use:
To run the binary directly:
```
./bin/neo-go node
```
Or specify a different network with an appropriate flag like this:
By default the node will run on the `private network`, to change his:
```
./bin/neo-go node --mainnet
@ -98,27 +93,10 @@ Available network flags:
- `--privnet, -p`
- `--testnet, -t`
To run a consensus/committee node, refer to [consensus
documentation](docs/consensus.md).
If you're running a node on Windows, please turn off or configure Windows
Firewall appropriately (allowing inbound connections to the P2P port).
### Docker
By default, the `CMD` is set to run a node on `privnet`. So, to do this, simply run:
```bash
docker run -d --name neo-go -p 20332:20332 -p 20331:20331 nspccdev/neo-go
```
Which will start a node on `privnet` and expose node's ports `20332` (P2P
protocol) and `20331` (JSON-RPC server).
### Importing mainnet/testnet dump files
#### Importing mainnet/testnet dump files
If you want to jump-start your mainnet or testnet node with [chain archives
provided by NGD](https://sync.ngd.network/), follow these instructions:
provided by NGD](https://sync.ngd.network/) follow these instructions:
```
$ wget .../chain.acc.zip # chain dump file
$ unzip chain.acc.zip
@ -126,47 +104,27 @@ $ ./bin/neo-go db restore -m -i chain.acc # for testnet use '-t' flag instead of
```
The process differs from the C# node in that block importing is a separate
mode. After it ends, the node can be started normally.
## Running a private network
Refer to [consensus node documentation](docs/consensus.md).
mode, after it ends the node can be started normally.
## Smart contract development
Please refer to [NeoGo smart contract development
Please refer to [neo-go smart contract development
workshop](https://github.com/nspcc-dev/neo-go-sc-wrkshp) that shows some
simple contracts that can be compiled/deployed/run using NeoGo compiler, SDK
and a private network. For details on how Go code is translated to Neo VM
bytecode and what you can and can not do in a smart contract, please refer to the
simple contracts that can be compiled/deployed/run using neo-go compiler, SDK
and private network. For details on how Go code is translated to Neo VM
bytecode and what you can and can not do in smart contract please refer to the
[compiler documentation](docs/compiler.md).
Refer to [examples](examples/README.md) for more Neo smart contract examples
written in Go.
# Developer notes
Nodes have such features as [Prometheus](https://prometheus.io/docs/guides/go-application) and
[Pprof](https://golang.org/pkg/net/http/pprof/) in order to have additional information about them for debugging.
## Wallets
NeoGo wallet is just a
[NEP-6](https://github.com/neo-project/proposals/blob/68398d28b6932b8dd2b377d5d51bca7b0442f532/nep-6.mediawiki)
file that is used by CLI commands to sign various things. CLI commands are not
a direct part of the node, but rather a part of the NeoGo binary, their
implementations use RPC to query data from the blockchain and perform any
required actions. It's not required to open a wallet on an RPC node (unless
your node provides some service for the network like consensus or oracle nodes
do).
## Monitoring
NeoGo provides [Prometheus](https://prometheus.io/docs/guides/go-application) and
[Pprof](https://golang.org/pkg/net/http/pprof/) services that can be enabled
in the node in order to provide additional monitoring and debugging data.
Configuring any of the two services is easy, add the following section (`Pprof`
instead of `Prometheus` if you need that) to the respective `config/protocol.*.yml`:
How to configure Prometheus or Pprof:
In `config/protocol.*.yml` there is
```
Prometheus:
Enabled: true
Addresses:
- ":2112"
Port: 2112
```
where you can switch on/off and define port. Prometheus is enabled and Pprof is disabled by default.
@ -175,14 +133,14 @@ where you can switch on/off and define port. Prometheus is enabled and Pprof is
Feel free to contribute to this project after reading the
[contributing guidelines](CONTRIBUTING.md).
Before starting to work on a certain topic, create a new issue first
Before starting to work on a certain topic, create an new issue first,
describing the feature/topic you are going to implement.
# Contact
- [@AnnaShaleva](https://github.com/AnnaShaleva) on GitHub
- [@roman-khimov](https://github.com/roman-khimov) on GitHub
- Reach out to us on the [Neo Discord](https://discordapp.com/invite/R8v48YA) channel
- [@fyrchik](https://github.com/fyrchik) on Github
- Reach out to us on the [NEO Discord](https://discordapp.com/invite/R8v48YA) channel
# License

View file

@ -1,71 +0,0 @@
# Roadmap for neo-go
This defines approximate plan of neo-go releases and key features planned for
them. Things can change if there is a need to push a bugfix or some critical
functionality.
## Versions 0.7X.Y (as needed)
* Neo 2.0 support (bug fixes, minor functionality additions)
## Version 0.107.0 (~Jun-Jul 2024)
* protocol updates
* bug fixes
* node resynchronisation from local DB
* CLI library upgrade
## Version 1.0 (2024, TBD)
* stable version
# Deprecated functionality
As the node and the protocol evolve some external APIs can change. Usually we
try keeping backwards compatibility for some time (like half a year) unless
it's impossible to do for some reason. But eventually old
APIs/commands/configurations will be removed and here is a list of scheduled
breaking changes. Consider changing your code/scripts/configurations if you're
using anything mentioned here.
## 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

@ -1,41 +0,0 @@
package app
import (
"fmt"
"os"
"runtime"
"github.com/nspcc-dev/neo-go/cli/query"
"github.com/nspcc-dev/neo-go/cli/server"
"github.com/nspcc-dev/neo-go/cli/smartcontract"
"github.com/nspcc-dev/neo-go/cli/util"
"github.com/nspcc-dev/neo-go/cli/vm"
"github.com/nspcc-dev/neo-go/cli/wallet"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/urfave/cli"
)
func versionPrinter(c *cli.Context) {
_, _ = fmt.Fprintf(c.App.Writer, "NeoGo\nVersion: %s\nGoVersion: %s\n",
config.Version,
runtime.Version(),
)
}
// New creates a NeoGo instance of [cli.App] with all commands included.
func New() *cli.App {
cli.VersionPrinter = versionPrinter
ctl := cli.NewApp()
ctl.Name = "neo-go"
ctl.Version = config.Version
ctl.Usage = "Official Go client for Neo"
ctl.ErrWriter = os.Stdout
ctl.Commands = append(ctl.Commands, server.NewCommands()...)
ctl.Commands = append(ctl.Commands, smartcontract.NewCommands()...)
ctl.Commands = append(ctl.Commands, wallet.NewCommands()...)
ctl.Commands = append(ctl.Commands, vm.NewCommands()...)
ctl.Commands = append(ctl.Commands, util.NewCommands()...)
ctl.Commands = append(ctl.Commands, query.NewCommands()...)
return ctl
}

View file

@ -1,19 +0,0 @@
package app_test
import (
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/internal/versionutil"
"github.com/nspcc-dev/neo-go/pkg/config"
)
func TestCLIVersion(t *testing.T) {
config.Version = versionutil.TestVersion // Zero-length version string disables '--version' completely.
e := testcli.NewExecutor(t, false)
e.Run(t, "neo-go", "--version")
e.CheckNextLine(t, "^NeoGo")
e.CheckNextLine(t, "^Version:")
e.CheckNextLine(t, "^GoVersion:")
e.CheckEOF(t)
}

View file

@ -1,350 +0,0 @@
package cmdargs
import (
"errors"
"fmt"
"strings"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
)
const (
// CosignersSeparator marks the start of cosigners cli args.
CosignersSeparator = "--"
// ArrayStartSeparator marks the start of array cli arg.
ArrayStartSeparator = "["
// ArrayEndSeparator marks the end of array cli arg.
ArrayEndSeparator = "]"
)
const (
// ParamsParsingDoc is a documentation for parameters parsing.
ParamsParsingDoc = ` Arguments always do have regular Neo smart contract parameter types, either
specified explicitly or being inferred from the value. To specify the type
manually use "type:value" syntax where the type is one of the following:
'signature', 'bool', 'int', 'hash160', 'hash256', 'bytes', 'key' or 'string'.
Array types are also supported: use special space-separated '[' and ']'
symbols around array values to denote array bounds. Nested arrays are also
supported. Null parameter is supported via 'nil' keyword without additional
type specification.
There is ability to provide an argument of 'bytearray' type via file. Use a
special 'filebytes' argument type for this with a filepath specified after
the colon, e.g. 'filebytes:my_file.txt'.
Given values are type-checked against given types with the following
restrictions applied:
* 'signature' type values should be hex-encoded and have a (decoded)
length of 64 bytes.
* 'bool' type values are 'true' and 'false'.
* 'int' values are decimal integers that can be successfully converted
from the string.
* 'hash160' values are Neo addresses and hex-encoded 20-bytes long (after
decoding) strings.
* 'hash256' type values should be hex-encoded and have a (decoded)
length of 32 bytes.
* 'bytes' type values are any hex-encoded things.
* 'filebytes' type values are filenames with the argument value inside.
* 'key' type values are hex-encoded marshalled public keys.
* 'string' type values are any valid UTF-8 strings. In the value's part of
the string the colon looses it's special meaning as a separator between
type and value and is taken literally.
If no type is explicitly specified, it is inferred from the value using the
following logic:
- anything that can be interpreted as a decimal integer gets
an 'int' type
- 'nil' string gets 'Any' NEP-14 parameter type and nil value which corresponds
to Null stackitem
- 'true' and 'false' strings get 'bool' type
- valid Neo addresses and 20 bytes long hex-encoded strings get 'hash160'
type
- valid hex-encoded public keys get 'key' type
- 32 bytes long hex-encoded values get 'hash256' type
- 64 bytes long hex-encoded values get 'signature' type
- any other valid hex-encoded values get 'bytes' type
- anything else is a 'string'
Backslash character is used as an escape character and allows to use colon in
an implicitly typed string. For any other characters it has no special
meaning, to get a literal backslash in the string use the '\\' sequence.
Examples:
* 'int:42' is an integer with a value of 42
* '42' is an integer with a value of 42
* 'nil' is a parameter with Any NEP-14 type and nil value (corresponds to Null stackitem)
* 'bad' is a string with a value of 'bad'
* 'dead' is a byte array with a value of 'dead'
* 'string:dead' is a string with a value of 'dead'
* 'filebytes:my_data.txt' is bytes decoded from a content of my_data.txt
* 'NSiVJYZej4XsxG5CUpdwn7VRQk8iiiDMPM' is a hash160 with a value
of '682cca3ebdc66210e5847d7f8115846586079d4a'
* '\4\2' is an integer with a value of 42
* '\\4\2' is a string with a value of '\42'
* 'string:string' is a string with a value of 'string'
* 'string\:string' is a string with a value of 'string:string'
* '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c' is a
key with a value of '03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c'
* '[ a b c ]' is an array with strings values 'a', 'b' and 'c'
* '[ a b [ c d ] e ]' is an array with 4 values: string 'a', string 'b',
array of two strings 'c' and 'd', string 'e'
* '[ ]' is an empty array`
// SignersParsingDoc is a documentation for signers parsing.
SignersParsingDoc = ` Signers represent a set of Uint160 hashes with witness scopes and are used
to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated
as a sender. To specify signers use signer[:scope] syntax where
* 'signer' is a signer's address (as Neo address or hex-encoded 160 bit (20 byte)
LE value with or without '0x' prefix).
* 'scope' is a comma-separated set of cosigner's scopes, which could be:
- 'None' - default witness scope which may be used for the sender
to only pay fee for the transaction.
- 'Global' - allows this witness in all contexts. This cannot be combined
with other flags.
- 'CalledByEntry' - means that this condition must hold: EntryScriptHash
== CallingScriptHash. The witness/permission/signature
given on first invocation will automatically expire if
entering deeper internal invokes. This can be default
safe choice for native NEO/GAS.
- 'CustomContracts' - define valid custom contract hashes for witness check.
Hashes are be provided as hex-encoded LE value string.
At lest one hash must be provided. Multiple hashes
are separated by ':'.
- 'CustomGroups' - define custom public keys for group members. Public keys are
provided as short-form (1-byte prefix + 32 bytes) hex-encoded
values. At least one key must be provided. Multiple keys
are separated by ':'.
If no scopes were specified, 'CalledByEntry' used as default. If no signers were
specified, no array is passed. Note that scopes are properly handled by
neo-go RPC server only. C# implementation does not support scopes capability.
Examples:
* 'NNQk4QXsxvsrr3GSozoWBUxEmfag7B6hz5'
* 'NVquyZHoPirw6zAEPvY1ZezxM493zMWQqs:Global'
* '0x0000000009070e030d0f0e020d0c06050e030c02'
* '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` +
`CustomGroups:0206d7495ceb34c197093b5fc1cccf1996ada05e69ef67e765462a7f5d88ee14d0'
* '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` +
`CustomContracts:1011120009070e030d0f0e020d0c06050e030c02:0x1211100009070e030d0f0e020d0c06050e030c02'`
)
// GetSignersFromContext returns signers parsed from context args starting
// from the specified offset.
func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) {
args := ctx.Args()
var (
signers []transaction.Signer
err error
)
if args.Present() && len(args) > offset {
signers, err = ParseSigners(args[offset:])
if err != nil {
return nil, cli.NewExitError(err, 1)
}
}
return signers, nil
}
// ParseSigners returns array of signers parsed from their string representation.
func ParseSigners(args []string) ([]transaction.Signer, error) {
var signers []transaction.Signer
for i, c := range args {
cosigner, err := parseCosigner(c)
if err != nil {
return nil, fmt.Errorf("failed to parse signer #%d: %w", i, err)
}
signers = append(signers, cosigner)
}
return signers, nil
}
func parseCosigner(c string) (transaction.Signer, error) {
var (
err error
res = transaction.Signer{
Scopes: transaction.CalledByEntry,
}
)
data := strings.SplitN(c, ":", 2)
s := data[0]
res.Account, err = flags.ParseAddress(s)
if err != nil {
return res, err
}
if len(data) == 1 {
return res, nil
}
res.Scopes = 0
scopes := strings.Split(data[1], ",")
for _, s := range scopes {
sub := strings.Split(s, ":")
scope, err := transaction.ScopesFromString(sub[0])
if err != nil {
return transaction.Signer{}, err
}
if scope == transaction.Global && res.Scopes&^transaction.Global != 0 ||
scope != transaction.Global && res.Scopes&transaction.Global != 0 {
return transaction.Signer{}, errors.New("Global scope can not be combined with other scopes")
}
res.Scopes |= scope
switch scope {
case transaction.CustomContracts:
if len(sub) == 1 {
return transaction.Signer{}, errors.New("CustomContracts scope must refer to at least one contract")
}
for _, s := range sub[1:] {
addr, err := flags.ParseAddress(s)
if err != nil {
return transaction.Signer{}, err
}
res.AllowedContracts = append(res.AllowedContracts, addr)
}
case transaction.CustomGroups:
if len(sub) == 1 {
return transaction.Signer{}, errors.New("CustomGroups scope must refer to at least one group")
}
for _, s := range sub[1:] {
pub, err := keys.NewPublicKeyFromString(s)
if err != nil {
return transaction.Signer{}, err
}
res.AllowedGroups = append(res.AllowedGroups, pub)
}
}
}
return res, nil
}
// GetDataFromContext returns data parameter from context args.
func GetDataFromContext(ctx *cli.Context) (int, any, *cli.ExitError) {
var (
data any
offset int
params []smartcontract.Parameter
err error
)
args := ctx.Args()
if args.Present() {
offset, params, err = ParseParams(args, true)
if err != nil {
return offset, nil, cli.NewExitError(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)
}
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, data, nil
}
// EnsureNone returns an error if there are any positional arguments present.
// It can be used to check for them in commands that don't accept arguments.
func EnsureNone(ctx *cli.Context) *cli.ExitError {
if ctx.Args().Present() {
return cli.NewExitError("additional arguments given while this command expects none", 1)
}
return nil
}
// ParseParams extracts array of smartcontract.Parameter from the given args and
// returns the number of handled words, the array itself and an error.
// `calledFromMain` denotes whether the method was called from the outside or
// recursively and used to check if CosignersSeparator and ArrayEndSeparator are
// allowed to be in `args` sequence.
func ParseParams(args []string, calledFromMain bool) (int, []smartcontract.Parameter, error) {
res := []smartcontract.Parameter{}
for k := 0; k < len(args); {
s := args[k]
switch s {
case CosignersSeparator:
if calledFromMain {
return k + 1, res, nil // `1` to convert index to numWordsRead
}
return 0, []smartcontract.Parameter{}, errors.New("invalid array syntax: missing closing bracket")
case ArrayStartSeparator:
numWordsRead, array, err := ParseParams(args[k+1:], false)
if err != nil {
return 0, nil, fmt.Errorf("failed to parse array: %w", err)
}
res = append(res, smartcontract.Parameter{
Type: smartcontract.ArrayType,
Value: array,
})
k += 1 + numWordsRead // `1` for opening bracket
case ArrayEndSeparator:
if calledFromMain {
return 0, nil, errors.New("invalid array syntax: missing opening bracket")
}
return k + 1, res, nil // `1`to convert index to numWordsRead
default:
param, err := smartcontract.NewParameterFromString(s)
if err != nil {
// '--' argument is skipped by urfave/cli library, which leads
// to [--, addr:scope] being transformed to [addr:scope] and
// interpreted as a parameter if other positional arguments are not present.
// Here we fallback to parsing cosigners in this specific case to
// create a better user experience ('-- addr:scope' vs '-- -- addr:scope').
if k == 0 {
if _, err := parseCosigner(s); err == nil {
return 0, nil, nil
}
}
return 0, nil, fmt.Errorf("failed to parse argument #%d: %w", k+1, err)
}
res = append(res, *param)
k++
}
}
if calledFromMain {
return len(args), res, nil
}
return 0, []smartcontract.Parameter{}, errors.New("invalid array syntax: missing closing bracket")
}
// GetSignersAccounts returns the list of signers combined with the corresponding
// accounts from the provided wallet.
func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers []transaction.Signer, accScope transaction.WitnessScope) ([]actor.SignerAccount, error) {
signersAccounts := make([]actor.SignerAccount, 0, len(signers)+1)
sender := senderAcc.ScriptHash()
signersAccounts = append(signersAccounts, actor.SignerAccount{
Signer: transaction.Signer{
Account: sender,
Scopes: accScope,
},
Account: senderAcc,
})
for i, s := range signers {
if s.Account == sender {
signersAccounts[0].Signer = s
continue
}
signerAcc := wall.GetAccount(s.Account)
if signerAcc == nil {
return nil, fmt.Errorf("no account was found in the wallet for signer #%d (%s)", i, address.Uint160ToString(s.Account))
}
signersAccounts = append(signersAccounts, actor.SignerAccount{
Signer: s,
Account: signerAcc,
})
}
return signersAccounts, nil
}

View file

@ -1,379 +0,0 @@
package cmdargs
import (
"strings"
"testing"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestParseCosigner(t *testing.T) {
acc := util.Uint160{1, 3, 5, 7}
c1, c2 := random.Uint160(), random.Uint160()
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
testCases := map[string]transaction.Signer{
acc.StringLE(): {
Account: acc,
Scopes: transaction.CalledByEntry,
},
"0x" + acc.StringLE(): {
Account: acc,
Scopes: transaction.CalledByEntry,
},
acc.StringLE() + ":Global": {
Account: acc,
Scopes: transaction.Global,
},
acc.StringLE() + ":CalledByEntry": {
Account: acc,
Scopes: transaction.CalledByEntry,
},
acc.StringLE() + ":None": {
Account: acc,
Scopes: transaction.None,
},
acc.StringLE() + ":CalledByEntry,CustomContracts:" + c1.StringLE() + ":0x" + c2.StringLE(): {
Account: acc,
Scopes: transaction.CalledByEntry | transaction.CustomContracts,
AllowedContracts: []util.Uint160{c1, c2},
},
acc.StringLE() + ":CustomGroups:" + priv.PublicKey().StringCompressed(): {
Account: acc,
Scopes: transaction.CustomGroups,
AllowedGroups: keys.PublicKeys{priv.PublicKey()},
},
}
for s, expected := range testCases {
actual, err := parseCosigner(s)
require.NoError(t, err)
require.Equal(t, expected, actual, s)
}
errorCases := []string{
acc.StringLE() + "0",
acc.StringLE() + ":Unknown",
acc.StringLE() + ":Global,CustomContracts",
acc.StringLE() + ":Global,None",
acc.StringLE() + ":CustomContracts:" + acc.StringLE() + ",Global",
acc.StringLE() + ":CustomContracts",
acc.StringLE() + ":CustomContracts:xxx",
acc.StringLE() + ":CustomGroups",
acc.StringLE() + ":CustomGroups:xxx",
}
for _, s := range errorCases {
_, err := parseCosigner(s)
require.Error(t, err, s)
}
}
func TestParseParams_CalledFromItself(t *testing.T) {
testCases := map[string]struct {
WordsRead int
Value []smartcontract.Parameter
}{
"]": {
WordsRead: 1,
Value: []smartcontract.Parameter{},
},
"[ [ ] ] ]": {
WordsRead: 5,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{},
},
},
},
},
},
"a b c ]": {
WordsRead: 4,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
"a [ b [ [ c d ] e ] ] f ] extra items": {
WordsRead: 13, // the method should return right after the last bracket, as calledFromMain == false
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "c",
},
{
Type: smartcontract.StringType,
Value: "d",
},
},
},
{
Type: smartcontract.StringType,
Value: "e",
},
},
},
},
},
{
Type: smartcontract.StringType,
Value: "f",
},
},
},
}
for str, expected := range testCases {
input := strings.Split(str, " ")
offset, actual, err := ParseParams(input, false)
require.NoError(t, err)
require.Equal(t, expected.WordsRead, offset)
require.Equal(t, expected.Value, actual)
}
errorCases := []string{
"[ ]",
"[ a b [ c ] d ]",
"[ ] --",
"--",
"not-int:integer ]",
}
for _, str := range errorCases {
input := strings.Split(str, " ")
_, _, err := ParseParams(input, false)
require.Error(t, err)
}
}
func TestParseParams_CalledFromOutside(t *testing.T) {
testCases := map[string]struct {
WordsRead int
Parameters []smartcontract.Parameter
}{
"-- cosigner1": {
WordsRead: 1, // the `--` only
Parameters: []smartcontract.Parameter{},
},
"a b c": {
WordsRead: 3,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
"a b c -- cosigner1": {
WordsRead: 4,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
"a [ b [ [ c d ] e ] ] f": {
WordsRead: 12,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "c",
},
{
Type: smartcontract.StringType,
Value: "d",
},
},
},
{
Type: smartcontract.StringType,
Value: "e",
},
},
},
},
},
{
Type: smartcontract.StringType,
Value: "f",
},
},
},
"a [ b ] -- cosigner1 cosigner2": {
WordsRead: 5,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
},
},
},
},
"a [ b ]": {
WordsRead: 4,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
},
},
},
},
"a [ b ] [ [ c ] ] [ [ [ d ] ] ]": {
WordsRead: 16,
Parameters: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "a",
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "b",
},
},
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "c",
},
},
},
},
},
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.ArrayType,
Value: []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: "d",
},
},
},
},
},
},
},
},
},
}
for str, expected := range testCases {
input := strings.Split(str, " ")
offset, arr, err := ParseParams(input, true)
require.NoError(t, err)
require.Equal(t, expected.WordsRead, offset)
require.Equal(t, expected.Parameters, arr)
}
errorCases := []string{
"[",
"]",
"[ [ ]",
"[ [ ] --",
"[ -- ]",
}
for _, str := range errorCases {
input := strings.Split(str, " ")
_, _, err := ParseParams(input, true)
require.Error(t, err)
}
}

View file

@ -10,13 +10,13 @@ import (
"github.com/urfave/cli"
)
// Address is a wrapper for a Uint160 with flag.Value methods.
// Address is a wrapper for Uint160 with flag.Value methods.
type Address struct {
IsSet bool
Value util.Uint160
}
// AddressFlag is a flag with type string.
// AddressFlag is a flag with type string
type AddressFlag struct {
Name string
Usage string
@ -28,14 +28,14 @@ var (
_ cli.Flag = AddressFlag{}
)
// String implements the fmt.Stringer interface.
// String implements fmt.Stringer interface.
func (a Address) String() string {
return address.Uint160ToString(a.Value)
}
// Set implements the flag.Value interface.
// Set implements flag.Value interface.
func (a *Address) Set(s string) error {
addr, err := ParseAddress(s)
addr, err := address.StringToUint160(s)
if err != nil {
return cli.NewExitError(err, 1)
}
@ -44,7 +44,7 @@ func (a *Address) Set(s string) error {
return nil
}
// Uint160 casts an address to Uint160.
// Uint160 casts address to Uint160.
func (a *Address) Uint160() (u util.Uint160) {
if !a.IsSet {
// It is a programmer error to call this method without
@ -60,7 +60,7 @@ func (f AddressFlag) IsSet() bool {
}
// String returns a readable representation of this value
// (for usage defaults).
// (for usage defaults)
func (f AddressFlag) String() string {
var names []string
eachName(f.Name, func(name string) {
@ -77,26 +77,15 @@ func getNameHelp(name string) string {
return fmt.Sprintf("--%s value", name)
}
// GetName returns the name of the flag.
// GetName returns the name of the flag
func (f AddressFlag) GetName() string {
return f.Name
}
// Apply populates the flag given the flag set and environment.
// Ignores errors.
// 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) {
set.Var(&f.Value, name, f.Usage)
})
}
// ParseAddress parses a Uint160 from either an LE string or an address.
func ParseAddress(s string) (util.Uint160, error) {
const uint160size = 2 * util.Uint160Size
switch len(s) {
case uint160size, uint160size + 2:
return util.Uint160DecodeStringLE(strings.TrimPrefix(s, "0x"))
default:
return address.StringToUint160(s)
}
}

View file

@ -1,130 +0,0 @@
package flags
import (
"flag"
"io"
"testing"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestParseAddress(t *testing.T) {
expected := random.Uint160()
t.Run("simple LE", func(t *testing.T) {
u, err := ParseAddress(expected.StringLE())
require.NoError(t, err)
require.Equal(t, expected, u)
})
t.Run("with prefix", func(t *testing.T) {
u, err := ParseAddress("0x" + expected.StringLE())
require.NoError(t, err)
require.Equal(t, expected, u)
t.Run("bad", func(t *testing.T) {
_, err := ParseAddress("0s" + expected.StringLE())
require.Error(t, err)
})
})
t.Run("address", func(t *testing.T) {
addr := address.Uint160ToString(expected)
u, err := ParseAddress(addr)
require.NoError(t, err)
require.Equal(t, expected, u)
t.Run("bad", func(t *testing.T) {
_, err := ParseAddress(addr[1:])
require.Error(t, err)
})
})
}
func TestAddress_String(t *testing.T) {
value := util.Uint160{1, 2, 3}
addr := Address{
IsSet: true,
Value: value,
}
require.Equal(t, address.Uint160ToString(value), addr.String())
}
func TestAddress_Set(t *testing.T) {
value := util.Uint160{1, 2, 3}
addr := Address{}
t.Run("bad address", func(t *testing.T) {
require.Error(t, addr.Set("not an address"))
})
t.Run("positive", func(t *testing.T) {
require.NoError(t, addr.Set(address.Uint160ToString(value)))
require.Equal(t, true, addr.IsSet)
require.Equal(t, value, addr.Value)
})
}
func TestAddress_Uint160(t *testing.T) {
value := util.Uint160{4, 5, 6}
addr := Address{}
t.Run("not set", func(t *testing.T) {
require.Panics(t, func() { addr.Uint160() })
})
t.Run("success", func(t *testing.T) {
addr.IsSet = true
addr.Value = value
require.Equal(t, value, addr.Uint160())
})
}
func TestAddressFlag_IsSet(t *testing.T) {
flag := AddressFlag{}
t.Run("not set", func(t *testing.T) {
require.False(t, flag.IsSet())
})
t.Run("set", func(t *testing.T) {
flag.Value.IsSet = true
require.True(t, flag.IsSet())
})
}
func TestAddressFlag_String(t *testing.T) {
flag := AddressFlag{
Name: "myFlag",
Usage: "Address to pass",
Value: Address{},
}
require.Equal(t, "--myFlag value\tAddress to pass", flag.String())
}
func TestAddress_getNameHelp(t *testing.T) {
require.Equal(t, "-f value", getNameHelp("f"))
require.Equal(t, "--flag value", getNameHelp("flag"))
}
func TestAddressFlag_GetName(t *testing.T) {
flag := AddressFlag{
Name: "my flag",
}
require.Equal(t, "my flag", flag.GetName())
}
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)
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"}))
}

View file

@ -4,13 +4,13 @@ import (
"flag"
"strings"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli"
)
// Fixed8 is a wrapper for a Uint160 with flag.Value methods.
// Fixed8 is a wrapper for Uint160 with flag.Value methods.
type Fixed8 struct {
Value fixedn.Fixed8
Value util.Fixed8
}
// Fixed8Flag is a flag with type string.
@ -25,14 +25,14 @@ var (
_ cli.Flag = Fixed8Flag{}
)
// String implements the fmt.Stringer interface.
// String implements fmt.Stringer interface.
func (a Fixed8) String() string {
return a.Value.String()
}
// Set implements the flag.Value interface.
// Set implements flag.Value interface.
func (a *Fixed8) Set(s string) error {
f, err := fixedn.Fixed8FromString(s)
f, err := util.Fixed8FromString(s)
if err != nil {
return cli.NewExitError(err, 1)
}
@ -40,8 +40,8 @@ func (a *Fixed8) Set(s string) error {
return nil
}
// Fixed8 casts the address to util.Fixed8.
func (a *Fixed8) Fixed8() fixedn.Fixed8 {
// Fixed8 casts address to util.Fixed8.
func (a *Fixed8) Fixed8() util.Fixed8 {
return a.Value
}
@ -61,7 +61,7 @@ func (f Fixed8Flag) GetName() string {
return f.Name
}
// Apply populates the flag given the flag set and environment.
// 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) {
@ -69,7 +69,7 @@ func (f Fixed8Flag) Apply(set *flag.FlagSet) {
})
}
// Fixed8FromContext returns a parsed util.Fixed8 value provided flag name.
func Fixed8FromContext(ctx *cli.Context, name string) fixedn.Fixed8 {
// Fixed8FromContext returns parsed util.Fixed8 value provided flag name.
func Fixed8FromContext(ctx *cli.Context, name string) util.Fixed8 {
return ctx.Generic(name).(*Fixed8).Value
}

View file

@ -1,66 +0,0 @@
package flags
import (
"flag"
"io"
"testing"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/stretchr/testify/require"
)
func TestFixed8_String(t *testing.T) {
value := fixedn.Fixed8(123)
f := Fixed8{
Value: value,
}
require.Equal(t, "0.00000123", f.String())
}
func TestFixed8_Set(t *testing.T) {
value := fixedn.Fixed8(123)
f := Fixed8{}
require.Error(t, f.Set("not-a-fixed8"))
require.NoError(t, f.Set("0.00000123"))
require.Equal(t, value, f.Value)
}
func TestFixed8_Fixed8(t *testing.T) {
f := Fixed8{
Value: fixedn.Fixed8(123),
}
require.Equal(t, fixedn.Fixed8(123), f.Fixed8())
}
func TestFixed8Flag_String(t *testing.T) {
flag := Fixed8Flag{
Name: "myFlag",
Usage: "Gas amount",
}
require.Equal(t, "--myFlag value\tGas amount", flag.String())
}
func TestFixed8Flag_GetName(t *testing.T) {
flag := Fixed8Flag{
Name: "myFlag",
}
require.Equal(t, "myFlag", flag.GetName())
}
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)
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"}))
}

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,71 +0,0 @@
package input
import (
"errors"
"fmt"
"io"
"os"
"syscall"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"golang.org/x/term"
)
// Terminal is a terminal used for input. If `nil`, stdin is used.
var Terminal *term.Terminal
// ReadWriter combiner reader and writer.
type ReadWriter struct {
io.Reader
io.Writer
}
// ReadLine reads a line from the input without trailing '\n'.
func ReadLine(prompt string) (string, error) {
trm := Terminal
if trm == nil {
s, err := term.MakeRaw(int(syscall.Stdin))
if err != nil {
return "", err
}
defer func() { _ = term.Restore(int(syscall.Stdin), s) }()
trm = term.NewTerminal(ReadWriter{
Reader: os.Stdin,
Writer: os.Stdout,
}, "")
}
return readLine(trm, prompt)
}
func readLine(trm *term.Terminal, prompt string) (string, error) {
_, err := trm.Write([]byte(prompt))
if err != nil {
return "", err
}
return trm.ReadLine()
}
// ReadPassword reads the user's password with prompt.
func ReadPassword(prompt string) (string, error) {
trm := Terminal
if trm != nil {
return trm.ReadPassword(prompt)
}
return readSecurePassword(prompt)
}
// ConfirmTx asks for a confirmation to send the tx.
func ConfirmTx(w io.Writer, tx *transaction.Transaction) error {
fmt.Fprintf(w, "Network fee: %s\n", fixedn.Fixed8(tx.NetworkFee))
fmt.Fprintf(w, "System fee: %s\n", fixedn.Fixed8(tx.SystemFee))
fmt.Fprintf(w, "Total fee: %s\n", fixedn.Fixed8(tx.NetworkFee+tx.SystemFee))
ln, err := ReadLine("Relay transaction (y|N)> ")
if err != nil {
return err
}
if 0 < len(ln) && (ln[0] == 'y' || ln[0] == 'Y') {
return nil
}
return errors.New("cancelled")
}

View file

@ -1,29 +0,0 @@
//go:build !windows
package input
import (
"fmt"
"os"
"golang.org/x/term"
)
// readSecurePassword reads the user's password with prompt directly from /dev/tty.
func readSecurePassword(prompt string) (string, error) {
f, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return "", err
}
defer f.Close()
_, err = f.WriteString(prompt)
if err != nil {
return "", err
}
pass, err := term.ReadPassword(int(f.Fd()))
if err != nil {
return "", fmt.Errorf("failed to read password: %w", err)
}
_, err = f.WriteString("\n")
return string(pass), err
}

View file

@ -1,21 +0,0 @@
//go:build windows
package input
import (
"os"
"syscall"
"golang.org/x/term"
)
// readSecurePassword reads the user's password with prompt.
func readSecurePassword(prompt string) (string, error) {
s, err := term.MakeRaw(int(syscall.Stdin))
if err != nil {
return "", err
}
defer func() { _ = term.Restore(int(syscall.Stdin), s) }()
trm := term.NewTerminal(ReadWriter{os.Stdin, os.Stdout}, prompt)
return trm.ReadPassword(prompt)
}

View file

@ -3,11 +3,24 @@ package main
import (
"os"
"github.com/nspcc-dev/neo-go/cli/app"
"github.com/nspcc-dev/neo-go/cli/server"
"github.com/nspcc-dev/neo-go/cli/smartcontract"
"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"
)
func main() {
ctl := app.New()
ctl := cli.NewApp()
ctl.Name = "neo-go"
ctl.Version = config.Version
ctl.Usage = "Official Go client for Neo"
ctl.Commands = append(ctl.Commands, server.NewCommands()...)
ctl.Commands = append(ctl.Commands, smartcontract.NewCommands()...)
ctl.Commands = append(ctl.Commands, wallet.NewCommands()...)
ctl.Commands = append(ctl.Commands, vm.NewCommands()...)
if err := ctl.Run(os.Args); err != nil {
panic(err)

View file

@ -1,677 +0,0 @@
package nep_test
import (
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/big"
"os"
"path/filepath"
"strconv"
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/internal/versionutil"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
const (
// nftOwnerAddr is the owner of NFT-ND HASHY token (../examples/nft-nd/nft.go).
nftOwnerAddr = "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB"
nftOwnerWallet = "../../examples/my_wallet.json"
nftOwnerPass = "qwerty"
// Keep contract NEFs consistent between runs.
_ = versionutil.TestVersion
)
func TestNEP11Import(t *testing.T) {
e := testcli.NewExecutor(t, true)
tmpDir := t.TempDir()
walletPath := filepath.Join(tmpDir, "walletForImport.json")
// deploy NFT NeoNameService contract
nnsContractHash := deployNNSContract(t, e)
// deploy NFT-D NeoFS Object contract
nfsContractHash := deployNFSContract(t, e)
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
require.NoError(t, err)
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
args := []string{
"neo-go", "wallet", "nep11", "import",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", walletPath,
}
// missing token hash
e.RunWithError(t, args...)
// excessive parameters
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE(), "something")...)
// good: non-divisible
e.Run(t, append(args, "--token", nnsContractHash.StringLE())...)
// good: divisible
e.Run(t, append(args, "--token", nfsContractHash.StringLE())...)
// already exists
e.RunWithError(t, append(args, "--token", nnsContractHash.StringLE())...)
// not a NEP-11 token
e.RunWithError(t, append(args, "--token", neoContractHash.StringLE())...)
checkInfo := func(t *testing.T, h util.Uint160, name string, symbol string, decimals int) {
e.CheckNextLine(t, "^Name:\\s*"+name)
e.CheckNextLine(t, "^Symbol:\\s*"+symbol)
e.CheckNextLine(t, "^Hash:\\s*"+h.StringLE())
e.CheckNextLine(t, "^Decimals:\\s*"+strconv.Itoa(decimals))
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(h))
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP11StandardName))
}
t.Run("Info", func(t *testing.T) {
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath, "--token", nnsContractHash.StringLE(), "qwerty")
})
t.Run("WithToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath, "--token", nnsContractHash.StringLE())
checkInfo(t, nnsContractHash, "NameService", "NNS", 0)
})
t.Run("NoToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath)
checkInfo(t, nnsContractHash, "NameService", "NNS", 0)
e.CheckNextLine(t, "")
checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2)
})
})
t.Run("Remove", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep11", "remove",
"--wallet", walletPath, "--token", nnsContractHash.StringLE(), "parameter")
e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep11", "remove",
"--wallet", walletPath, "--token", nnsContractHash.StringLE())
e.Run(t, "neo-go", "wallet", "nep11", "info",
"--wallet", walletPath)
checkInfo(t, nfsContractHash, "NeoFS Object NFT", "NFSO", 2)
_, err := e.Out.ReadString('\n')
require.Equal(t, err, io.EOF)
})
}
func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
e := testcli.NewExecutor(t, true)
tmpDir := t.TempDir()
// copy wallet to temp dir in order not to overwrite the original file
bytesRead, err := os.ReadFile(nftOwnerWallet)
require.NoError(t, err)
wall := filepath.Join(tmpDir, "my_wallet.json")
err = os.WriteFile(wall, bytesRead, 0755)
require.NoError(t, err)
// transfer funds to contract owner
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--to", nftOwnerAddr,
"--token", "GAS",
"--amount", "10000",
"--force",
"--from", testcli.ValidatorAddr)
e.CheckTxPersisted(t)
// deploy NFT HASHY contract
h := deployNFTContract(t, e)
mint := func(t *testing.T) []byte {
// mint 1 HASHY token by transferring 10 GAS to HASHY contract
e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", wall,
"--to", h.StringLE(),
"--token", "GAS",
"--amount", "10",
"--force",
"--from", nftOwnerAddr)
txMint, _ := e.CheckTxPersisted(t)
// get NFT ID from AER
aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 1, len(aer))
require.Equal(t, 2, len(aer[0].Events))
hashyMintEvent := aer[0].Events[1]
require.Equal(t, "Transfer", hashyMintEvent.Name)
tokenID, err := hashyMintEvent.Item.Value().([]stackitem.Item)[3].TryBytes()
require.NoError(t, err)
require.NotNil(t, tokenID)
return tokenID
}
tokenID := mint(t)
var hashBeforeTransfer = e.Chain.CurrentHeaderHash()
// check the balance
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", wall,
"--address", nftOwnerAddr}
checkBalanceResult := func(t *testing.T, acc string, ids ...[]byte) {
e.CheckNextLine(t, "^\\s*Account\\s+"+acc)
e.CheckNextLine(t, "^\\s*HASHY:\\s+HASHY NFT \\("+h.StringLE()+"\\)")
// Hashes can be ordered in any way, so make a regexp for them.
var tokstring = "("
for i, id := range ids {
if i > 0 {
tokstring += "|"
}
tokstring += hex.EncodeToString(id)
}
tokstring += ")"
for range ids {
e.CheckNextLine(t, "^\\s*Token: "+tokstring+"\\s*$")
e.CheckNextLine(t, "^\\s*Amount: 1\\s*$")
e.CheckNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
}
e.CheckEOF(t)
}
// balance check: by symbol, token is not imported
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
checkBalanceResult(t, nftOwnerAddr, tokenID)
// balance check: excessive parameters
e.RunWithError(t, append(cmdCheckBalance, "--token", h.StringLE(), "neo-go")...)
// balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID)
// import token
e.Run(t, "neo-go", "wallet", "nep11", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", wall,
"--token", h.StringLE())
// balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "HASHY")...)
checkBalanceResult(t, nftOwnerAddr, tokenID)
// balance check: all accounts
e.Run(t, "neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", wall,
"--token", h.StringLE())
checkBalanceResult(t, nftOwnerAddr, tokenID)
// remove token from wallet
e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep11", "remove",
"--wallet", wall, "--token", h.StringLE())
// ownerOf: missing contract hash
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
// ownerOf: missing token ID
e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--id", hex.EncodeToString(tokenID))
// ownerOf: good
e.Run(t, cmdOwnerOf...)
e.CheckNextLine(t, nftOwnerAddr)
// tokensOf: missing contract hash
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", nftOwnerAddr)
// tokensOf: good
e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t))
// properties: no contract
cmdProperties := []string{
"neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE())
// properties: no token ID
e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(tokenID))
// properties: ok
e.Run(t, cmdProperties...)
require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.GetNextLine(t))
// tokensOf: good, several tokens
tokenID1 := mint(t)
e.Run(t, cmdTokensOf...)
fst, snd := tokenID, tokenID1
if bytes.Compare(tokenID, tokenID1) == 1 {
fst, snd = snd, fst
}
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
// tokens: missing contract hash
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: excessive parameters
e.RunWithError(t, append(cmdTokens, "additional")...)
// tokens: good, several tokens
e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
// balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID, tokenID1)
cmdTransfer := []string{
"neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", wall,
"--to", testcli.ValidatorAddr,
"--from", nftOwnerAddr,
"--force",
}
// transfer: unimported token with symbol id specified
e.In.WriteString(nftOwnerPass + "\r")
e.RunWithError(t, append(cmdTransfer,
"--token", "HASHY")...)
cmdTransfer = append(cmdTransfer, "--token", h.StringLE())
// transfer: no id specified
e.In.WriteString(nftOwnerPass + "\r")
e.RunWithError(t, cmdTransfer...)
// transfer: good
e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(tokenID))...)
e.CheckTxPersisted(t)
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID1)
// check --await flag
tokenID2 := mint(t)
e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, append(cmdTransfer, "--await", "--id", hex.EncodeToString(tokenID2))...)
e.CheckAwaitableTxPersisted(t)
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, tokenID1)
// transfer: good, to NEP-11-Payable contract, with data
verifyH := deployVerifyContract(t, e)
cmdTransfer = []string{
"neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", wall,
"--to", verifyH.StringLE(),
"--from", nftOwnerAddr,
"--token", h.StringLE(),
"--id", hex.EncodeToString(tokenID1),
"--force",
"string:some_data",
}
e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, cmdTransfer...)
tx, _ := e.CheckTxPersisted(t)
// check OnNEP11Payment event
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 2, len(aer[0].Events))
nftOwnerHash, err := address.StringToUint160(nftOwnerAddr)
require.NoError(t, err)
require.Equal(t, state.NotificationEvent{
ScriptHash: verifyH,
Name: "OnNEP11Payment",
Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(nftOwnerHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(1)),
stackitem.NewByteArray(tokenID1),
stackitem.NewByteArray([]byte("some_data")),
}),
}, aer[0].Events[1])
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr)
// historic calls still remember the good old days.
cmdOwnerOf = append(cmdOwnerOf, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdOwnerOf...)
e.CheckNextLine(t, nftOwnerAddr)
cmdTokensOf = append(cmdTokensOf, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t))
cmdTokens = append(cmdTokens, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(tokenID), e.GetNextLine(t))
// this one is not affected by transfer, but anyway
cmdProperties = append(cmdProperties, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdProperties...)
require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.GetNextLine(t))
}
func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {
e := testcli.NewExecutor(t, true)
tmpDir := t.TempDir()
// copy wallet to temp dir in order not to overwrite the original file
bytesRead, err := os.ReadFile(testcli.ValidatorWallet)
require.NoError(t, err)
wall := filepath.Join(tmpDir, "my_wallet.json")
err = os.WriteFile(wall, bytesRead, 0755)
require.NoError(t, err)
// deploy NeoFS Object contract
h := deployNFSContract(t, e)
mint := func(t *testing.T, containerID, objectID util.Uint256) []byte {
// mint 1.00 NFSO token by transferring 10 GAS to NFSO contract
e.In.WriteString(testcli.ValidatorPass + "\r")
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", wall,
"--to", h.StringLE(),
"--token", "GAS",
"--amount", "10",
"--force",
"--from", testcli.ValidatorAddr,
"--", "[", "hash256:"+containerID.StringLE(), "hash256:"+objectID.StringLE(), "]",
)
txMint, _ := e.CheckTxPersisted(t)
// get NFT ID from AER
aer, err := e.Chain.GetAppExecResults(txMint.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 1, len(aer))
require.Equal(t, 2, len(aer[0].Events))
nfsoMintEvent := aer[0].Events[1]
require.Equal(t, "Transfer", nfsoMintEvent.Name)
tokenID, err := nfsoMintEvent.Item.Value().([]stackitem.Item)[3].TryBytes()
require.NoError(t, err)
require.NotNil(t, tokenID)
return tokenID
}
container1ID := util.Uint256{1, 2, 3}
object1ID := util.Uint256{4, 5, 6}
token1ID := mint(t, container1ID, object1ID)
container2ID := util.Uint256{7, 8, 9}
object2ID := util.Uint256{10, 11, 12}
token2ID := mint(t, container2ID, object2ID)
// check properties
e.Run(t, "neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--token", h.StringLE(),
"--id", hex.EncodeToString(token1ID))
jProps := e.GetNextLine(t)
props := make(map[string]string)
require.NoError(t, json.Unmarshal([]byte(jProps), &props))
require.Equal(t, base64.StdEncoding.EncodeToString(container1ID.BytesBE()), props["containerID"])
require.Equal(t, base64.StdEncoding.EncodeToString(object1ID.BytesBE()), props["objectID"])
e.CheckEOF(t)
type idAmount struct {
id string
amount string
}
// check the balance
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", wall,
"--address", testcli.ValidatorAddr}
checkBalanceResult := func(t *testing.T, acc string, objs ...idAmount) {
e.CheckNextLine(t, "^\\s*Account\\s+"+acc)
e.CheckNextLine(t, "^\\s*NFSO:\\s+NeoFS Object NFT \\("+h.StringLE()+"\\)")
for _, o := range objs {
e.CheckNextLine(t, "^\\s*Token: "+o.id+"\\s*$")
e.CheckNextLine(t, "^\\s*Amount: "+o.amount+"\\s*$")
e.CheckNextLine(t, "^\\s*Updated: [0-9]+\\s*$")
}
e.CheckEOF(t)
}
tokz := []idAmount{
{hex.EncodeToString(token1ID), "1"},
{hex.EncodeToString(token2ID), "1"},
}
// balance check: by symbol, token is not imported
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
// overall NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
// particular NFSO balance check: by hash, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE(), "--id", hex.EncodeToString(token2ID))...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz[1])
// import token
e.Run(t, "neo-go", "wallet", "nep11", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", wall,
"--token", h.StringLE())
// overall balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO")...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
// particular balance check: by symbol, ok
e.Run(t, append(cmdCheckBalance, "--token", "NFSO", "--id", hex.EncodeToString(token1ID))...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz[0])
// remove token from wallet
e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep11", "remove",
"--wallet", wall, "--token", h.StringLE())
// ownerOfD: missing contract hash
cmdOwnerOf := []string{"neo-go", "wallet", "nep11", "ownerOfD",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--token", h.StringLE())
// ownerOfD: missing token ID
e.RunWithError(t, cmdOwnerOf...)
cmdOwnerOf = append(cmdOwnerOf, "--id", hex.EncodeToString(token1ID))
// ownerOfD: good
e.Run(t, cmdOwnerOf...)
e.CheckNextLine(t, testcli.ValidatorAddr)
// tokensOf: missing contract hash
cmdTokensOf := []string{"neo-go", "wallet", "nep11", "tokensOf",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--token", h.StringLE())
// tokensOf: missing owner address
e.RunWithError(t, cmdTokensOf...)
cmdTokensOf = append(cmdTokensOf, "--address", testcli.ValidatorAddr)
// tokensOf: good
e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(token1ID), e.GetNextLine(t))
require.Equal(t, hex.EncodeToString(token2ID), e.GetNextLine(t))
e.CheckEOF(t)
// properties: no contract
cmdProperties := []string{
"neo-go", "wallet", "nep11", "properties",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--token", h.StringLE())
// properties: no token ID
e.RunWithError(t, cmdProperties...)
cmdProperties = append(cmdProperties, "--id", hex.EncodeToString(token2ID))
// properties: additional parameter
e.RunWithError(t, append(cmdProperties, "additiona")...)
// properties: ok
e.Run(t, cmdProperties...)
jProps = e.GetNextLine(t)
props = make(map[string]string)
require.NoError(t, json.Unmarshal([]byte(jProps), &props))
require.Equal(t, base64.StdEncoding.EncodeToString(container2ID.BytesBE()), props["containerID"])
require.Equal(t, base64.StdEncoding.EncodeToString(object2ID.BytesBE()), props["objectID"])
e.CheckEOF(t)
// tokensOf: good, several tokens
e.Run(t, cmdTokensOf...)
fst, snd := token1ID, token2ID
if bytes.Compare(token1ID, token2ID) == 1 {
fst, snd = snd, fst
}
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
// tokens: missing contract hash
cmdTokens := []string{"neo-go", "wallet", "nep11", "tokens",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
}
e.RunWithError(t, cmdTokens...)
cmdTokens = append(cmdTokens, "--token", h.StringLE())
// tokens: good, several tokens
e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(fst), e.GetNextLine(t))
require.Equal(t, hex.EncodeToString(snd), e.GetNextLine(t))
// balance check: several tokens, ok
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz...)
cmdTransfer := []string{
"neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", wall,
"--to", nftOwnerAddr,
"--from", testcli.ValidatorAddr,
"--force",
}
// transfer: unimported token with symbol id specified
e.In.WriteString(testcli.ValidatorPass + "\r")
e.RunWithError(t, append(cmdTransfer,
"--token", "NFSO")...)
cmdTransfer = append(cmdTransfer, "--token", h.StringLE())
// transfer: no id specified
e.In.WriteString(testcli.ValidatorPass + "\r")
e.RunWithError(t, cmdTransfer...)
// transfer: good
e.In.WriteString(testcli.ValidatorPass + "\r")
e.Run(t, append(cmdTransfer, "--id", hex.EncodeToString(token1ID))...)
e.CheckTxPersisted(t)
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, testcli.ValidatorAddr, tokz[1]) // only token2ID expected to be on the balance
// transfer: good, 1/4 of the balance, to NEP-11-Payable contract, with data
verifyH := deployVerifyContract(t, e)
cmdTransfer = []string{
"neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", wall,
"--to", verifyH.StringLE(),
"--from", testcli.ValidatorAddr,
"--token", h.StringLE(),
"--id", hex.EncodeToString(token2ID),
"--amount", "0.25",
"--force",
"string:some_data",
}
e.In.WriteString(testcli.ValidatorPass + "\r")
e.Run(t, cmdTransfer...)
tx, _ := e.CheckTxPersisted(t)
// check OnNEP11Payment event
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 2, len(aer[0].Events))
validatorHash, err := address.StringToUint160(testcli.ValidatorAddr)
require.NoError(t, err)
require.Equal(t, state.NotificationEvent{
ScriptHash: verifyH,
Name: "OnNEP11Payment",
Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(validatorHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(25)),
stackitem.NewByteArray(token2ID),
stackitem.NewByteArray([]byte("some_data")),
}),
}, aer[0].Events[1])
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
tokz[1].amount = "0.75"
checkBalanceResult(t, testcli.ValidatorAddr, tokz[1])
}
func deployNFSContract(t *testing.T, e *testcli.Executor) util.Uint160 {
return testcli.DeployContract(t, e, "../../examples/nft-d/nft.go", "../../examples/nft-d/nft.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
}
func deployNFTContract(t *testing.T, e *testcli.Executor) util.Uint160 {
return testcli.DeployContract(t, e, "../../examples/nft-nd/nft.go", "../../examples/nft-nd/nft.yml", nftOwnerWallet, nftOwnerAddr, nftOwnerPass)
}
func deployNNSContract(t *testing.T, e *testcli.Executor) util.Uint160 {
return testcli.DeployContract(t, e, "../../examples/nft-nd-nns/", "../../examples/nft-nd-nns/nns.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
}
func deployVerifyContract(t *testing.T, e *testcli.Executor) util.Uint160 {
return testcli.DeployContract(t, e, "../smartcontract/testdata/verify.go", "../smartcontract/testdata/verify.yml", testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
}

View file

@ -1,398 +0,0 @@
package nep_test
import (
"io"
"math/big"
"path/filepath"
"strconv"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
func TestNEP17Balance(t *testing.T) {
e := testcli.NewExecutor(t, true)
args := []string{
"neo-go", "wallet", "nep17", "multitransfer",
"--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)
}
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)
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("zero balance of known token", func(t *testing.T) {
e.Run(t, append(cmdbase, []string{"--token", "NEO", "--address", testcli.TestWalletMultiAccount2}...)...)
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2)
e.CheckNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(0).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:")
e.CheckEOF(t)
})
t.Run("all accounts", func(t *testing.T) {
e.Run(t, cmdbase...)
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))
}
}
e.CheckNextLine(t, "^\\s*$")
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount2)
e.CheckNextLine(t, "^\\s*$")
e.CheckNextLine(t, "^Account "+testcli.TestWalletMultiAccount3)
e.CheckNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
balance := e.Chain.GetUtilityTokenBalance(testcli.TestWalletMultiAccount3Hash)
e.CheckNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String()+"$")
e.CheckNextLine(t, "^\\s*Updated:")
e.CheckEOF(t)
})
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) {
w, err := wallet.NewWalletFromFile("../testdata/testwallet.json")
require.NoError(t, err)
e := testcli.NewExecutor(t, true)
args := []string{
"neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--to", w.Accounts[0].Address,
"--token", "NEO",
"--amount", "1",
"--from", testcli.ValidatorAddr,
}
t.Run("missing receiver", func(t *testing.T) {
as := append([]string{}, args[:8]...)
as = append(as, args[10:]...)
e.In.WriteString("one\r")
e.RunWithError(t, as...)
e.In.Reset()
})
t.Run("InvalidPassword", func(t *testing.T) {
e.In.WriteString("onetwothree\r")
e.RunWithError(t, args...)
e.In.Reset()
})
t.Run("no confirmation", func(t *testing.T) {
e.In.WriteString("one\r")
e.RunWithError(t, args...)
e.In.Reset()
})
t.Run("cancel after prompt", func(t *testing.T) {
e.In.WriteString("one\r")
e.RunWithError(t, args...)
e.In.Reset()
})
e.In.WriteString("one\r")
e.In.WriteString("Y\r")
e.Run(t, args...)
e.CheckNextLine(t, `^Network fee:\s*(\d|\.)+`)
e.CheckNextLine(t, `^System fee:\s*(\d|\.)+`)
e.CheckNextLine(t, `^Total fee:\s*(\d|\.)+`)
e.CheckTxPersisted(t)
sh := w.Accounts[0].ScriptHash()
b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(1), b)
t.Run("with force", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(args, "--force")...)
e.CheckTxPersisted(t)
b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(2), b)
})
hVerify := deployVerifyContract(t, e)
const validatorDefault = "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn"
t.Run("default address", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--from", testcli.ValidatorAddr,
"--force",
"NEO:"+validatorDefault+":42",
"GAS:"+validatorDefault+":7")
e.CheckTxPersisted(t)
args := args[:len(args)-2] // cut '--from' argument
args = append(args, "--force")
e.In.WriteString("one\r")
e.Run(t, args...)
e.CheckTxPersisted(t)
b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(3), b)
sh, err = address.StringToUint160(validatorDefault)
require.NoError(t, err)
b, _ = e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(41), b)
})
t.Run("with signers", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--from", testcli.ValidatorAddr,
"--force",
"NEO:"+validatorDefault+":42",
"GAS:"+validatorDefault+":7",
"--", testcli.ValidatorAddr+":Global")
e.CheckTxPersisted(t)
})
validTil := e.Chain.BlockHeight() + 100
cmd := []string{
"neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--token", "GAS",
"--amount", "1",
"--force",
"--from", testcli.ValidatorAddr}
t.Run("with await", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, "--to", nftOwnerAddr, "--await")...)
e.CheckAwaitableTxPersisted(t)
})
cmd = append(cmd, "--to", address.Uint160ToString(e.Chain.GetNotaryContractScriptHash()),
"[", testcli.ValidatorAddr, strconv.Itoa(int(validTil)), "]")
t.Run("with data", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, cmd...)
e.CheckTxPersisted(t)
})
t.Run("with data and signers", func(t *testing.T) {
t.Run("invalid sender's scope", func(t *testing.T) {
e.In.WriteString("one\r")
e.RunWithError(t, append(cmd, "--", testcli.ValidatorAddr+":None")...)
})
t.Run("good", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, "--", testcli.ValidatorAddr+":Global")...) // CalledByEntry is enough, but it's the default value, so check something else
e.CheckTxPersisted(t)
})
t.Run("several signers", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(cmd, "--", testcli.ValidatorAddr, hVerify.StringLE())...)
e.CheckTxPersisted(t)
})
})
}
func TestNEP17MultiTransfer(t *testing.T) {
privs, _ := testcli.GenerateKeys(t, 3)
e := testcli.NewExecutor(t, true)
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
require.NoError(t, err)
args := []string{
"neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--from", testcli.ValidatorAddr,
"--force",
"NEO:" + privs[0].Address() + ":42",
"GAS:" + privs[1].Address() + ":7",
neoContractHash.StringLE() + ":" + privs[2].Address() + ":13",
}
hVerify := deployVerifyContract(t, e)
t.Run("no cosigners", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, args...)
e.CheckTxPersisted(t)
b, _ := e.Chain.GetGoverningTokenBalance(privs[0].GetScriptHash())
require.Equal(t, big.NewInt(42), b)
b = e.Chain.GetUtilityTokenBalance(privs[1].GetScriptHash())
require.Equal(t, big.NewInt(int64(fixedn.Fixed8FromInt64(7))), b)
b, _ = e.Chain.GetGoverningTokenBalance(privs[2].GetScriptHash())
require.Equal(t, big.NewInt(13), b)
})
t.Run("invalid sender scope", func(t *testing.T) {
e.In.WriteString("one\r")
e.RunWithError(t, append(args,
"--", testcli.ValidatorAddr+":None")...) // invalid sender scope
})
t.Run("Global sender scope", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(args,
"--", testcli.ValidatorAddr+":Global")...)
e.CheckTxPersisted(t)
})
t.Run("Several cosigners", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(args,
"--", testcli.ValidatorAddr, hVerify.StringLE())...)
e.CheckTxPersisted(t)
})
}
func TestNEP17ImportToken(t *testing.T) {
e := testcli.NewExecutor(t, true)
tmpDir := t.TempDir()
walletPath := filepath.Join(tmpDir, "walletForImport.json")
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
require.NoError(t, err)
gasContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Gas)
require.NoError(t, err)
nnsContractHash := deployNNSContract(t, e)
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
// missing token hash
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath)
// additional parameter
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath,
"--token", gasContractHash.StringLE(), "useless")
e.Run(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath,
"--token", gasContractHash.StringLE())
e.Run(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath,
"--token", address.Uint160ToString(neoContractHash)) // try address instead of sh
// not a NEP-17 token
e.RunWithError(t, "neo-go", "wallet", "nep17", "import",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", walletPath,
"--token", nnsContractHash.StringLE())
t.Run("Info", func(t *testing.T) {
checkGASInfo := func(t *testing.T) {
e.CheckNextLine(t, "^Name:\\s*GasToken")
e.CheckNextLine(t, "^Symbol:\\s*GAS")
e.CheckNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE())
e.CheckNextLine(t, "^Decimals:\\s*8")
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash))
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName))
}
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep17", "info",
"--wallet", walletPath, "--token", gasContractHash.StringLE(), "parameter")
})
t.Run("WithToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep17", "info",
"--wallet", walletPath, "--token", gasContractHash.StringLE())
checkGASInfo(t)
})
t.Run("NoToken", func(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep17", "info",
"--wallet", walletPath)
checkGASInfo(t)
_, err := e.Out.ReadString('\n')
require.NoError(t, err)
e.CheckNextLine(t, "^Name:\\s*NeoToken")
e.CheckNextLine(t, "^Symbol:\\s*NEO")
e.CheckNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE())
e.CheckNextLine(t, "^Decimals:\\s*0")
e.CheckNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash))
e.CheckNextLine(t, "^Standard:\\s*"+string(manifest.NEP17StandardName))
})
t.Run("Remove", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "nep17", "remove",
"--wallet", walletPath, "--token", neoContractHash.StringLE(), "add")
e.In.WriteString("y\r")
e.Run(t, "neo-go", "wallet", "nep17", "remove",
"--wallet", walletPath, "--token", neoContractHash.StringLE())
e.Run(t, "neo-go", "wallet", "nep17", "info",
"--wallet", walletPath)
checkGASInfo(t)
_, err := e.Out.ReadString('\n')
require.Equal(t, err, io.EOF)
})
})
}

View file

@ -1,33 +0,0 @@
package options_test
import (
"flag"
"testing"
"github.com/nspcc-dev/neo-go/cli/app"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestGetRPCClient(t *testing.T) {
e := testcli.NewExecutor(t, true)
t.Run("no endpoint", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(app.New(), set, nil)
gctx, _ := options.GetTimeoutContext(ctx)
_, ec := options.GetRPCClient(gctx, ctx)
require.Equal(t, 1, ec.ExitCode())
})
t.Run("success", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String(options.RPCEndpointFlag, "http://"+e.RPC.Addresses()[0], "")
ctx := cli.NewContext(app.New(), set, nil)
gctx, _ := options.GetTimeoutContext(ctx)
_, ec := options.GetRPCClient(gctx, ctx)
require.Nil(t, ec)
})
}

View file

@ -1,28 +0,0 @@
package options
import "go.uber.org/zap/zapcore"
// FilteringCore is custom implementation of zapcore.Core that allows to filter
// log entries using custom filtering function.
type FilteringCore struct {
zapcore.Core
filter FilterFunc
}
// FilterFunc is the filter function that is called to check whether the given
// entry together with the associated fields is to be written to a core or not.
type FilterFunc func(zapcore.Entry) bool
// NewFilteringCore returns a core middleware that uses the given filter function
// to decide whether to log this message or not.
func NewFilteringCore(next zapcore.Core, filter FilterFunc) zapcore.Core {
return &FilteringCore{next, filter}
}
// Check implements zapcore.Core interface and performs log entries filtering.
func (c *FilteringCore) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if c.filter(e) {
return c.Core.Check(e, ce)
}
return ce
}

View file

@ -1,410 +0,0 @@
/*
Package options contains a set of common CLI options and helper functions to use them.
*/
package options
import (
"context"
"errors"
"fmt"
"net/url"
"os"
"runtime"
"strconv"
"strings"
"time"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/term"
"gopkg.in/yaml.v3"
)
const (
// DefaultTimeout is the default timeout used for RPC requests.
DefaultTimeout = 10 * time.Second
// DefaultAwaitableTimeout is the default timeout used for RPC requests that
// require transaction awaiting. It is set to the approximate time of three
// Neo N3 mainnet blocks accepting.
DefaultAwaitableTimeout = 3 * 15 * time.Second
)
// 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"
// 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"},
}
// 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},
}
// 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.DurationFlag{
Name: "timeout, s",
Value: DefaultTimeout,
Usage: "Timeout for the operation",
},
}
// Historic is a flag for commands that can perform historic invocations.
var Historic = cli.StringFlag{
Name: "historic",
Usage: "Use historic state (height, block hash or state root hash)",
}
// Config is a flag for commands that use node configuration.
var Config = cli.StringFlag{
Name: "config-path",
Usage: "path to directory with per-network configuration files (may be overridden by --config-file option for the configuration file)",
}
// ConfigFile is a flag for commands that use node configuration and provide
// path to the specific config file instead of config path.
var ConfigFile = cli.StringFlag{
Name: "config-file",
Usage: "path to the node configuration file (overrides --config-path option)",
}
// RelativePath is a flag for commands that use node configuration and provide
// a prefix to all relative paths in config files.
var RelativePath = cli.StringFlag{
Name: "relative-path",
Usage: "a 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 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")
// GetNetwork examines Context's flags and returns the appropriate network. It
// defaults to PrivNet if no flags are given.
func GetNetwork(ctx *cli.Context) netmode.Magic {
var net = netmode.PrivNet
if ctx.Bool("testnet") {
net = netmode.TestNet
}
if ctx.Bool("mainnet") {
net = netmode.MainNet
}
if ctx.Bool("unittest") {
net = netmode.UnitTestNet
}
return net
}
// GetTimeoutContext returns a context.Context with the default or a user-set timeout.
func GetTimeoutContext(ctx *cli.Context) (context.Context, func()) {
dur := ctx.Duration("timeout")
if dur == 0 {
dur = DefaultTimeout
}
if !ctx.IsSet("timeout") && ctx.Bool("await") {
dur = DefaultAwaitableTimeout
}
return context.WithTimeout(context.Background(), dur)
}
// GetRPCClient returns an RPC client instance for the given Context.
func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cli.ExitCoder) {
endpoint := ctx.String(RPCEndpointFlag)
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)
}
err = c.Init()
if err != nil {
return nil, cli.NewExitError(err, 1)
}
return c, nil
}
// GetInvoker returns an invoker using the given RPC client, context and signers.
// It parses "--historic" parameter to adjust it.
func GetInvoker(c *rpcclient.Client, ctx *cli.Context, signers []transaction.Signer) (*invoker.Invoker, cli.ExitCoder) {
historic := ctx.String("historic")
if historic == "" {
return invoker.New(c, signers), nil
}
if index, err := strconv.ParseUint(historic, 10, 32); err == nil {
return invoker.NewHistoricAtHeight(uint32(index), c, signers), nil
}
if u256, err := util.Uint256DecodeStringLE(historic); err == nil {
// Might as well be a block hash, but it makes no practical difference.
return invoker.NewHistoricWithState(u256, c, signers), nil
}
return nil, cli.NewExitError(errInvalidHistoric, 1)
}
// GetRPCWithInvoker combines GetRPCClient with GetInvoker for cases where it's
// appropriate to do so.
func GetRPCWithInvoker(gctx context.Context, ctx *cli.Context, signers []transaction.Signer) (*rpcclient.Client, *invoker.Invoker, cli.ExitCoder) {
c, err := GetRPCClient(gctx, ctx)
if err != nil {
return nil, nil, err
}
inv, err := GetInvoker(c, ctx, signers)
if err != nil {
c.Close()
return nil, nil, err
}
return c, inv, err
}
// GetConfigFromContext looks at the path and the mode flags in the given config and
// returns an appropriate config.
func GetConfigFromContext(ctx *cli.Context) (config.Config, error) {
var (
configFile = ctx.String("config-file")
relativePath = ctx.String("relative-path")
)
if len(configFile) != 0 {
return config.LoadFile(configFile, relativePath)
}
var configPath = "./config"
if argCp := ctx.String("config-path"); argCp != "" {
configPath = argCp
}
return config.Load(configPath, GetNetwork(ctx), relativePath)
}
var (
// _winfileSinkRegistered denotes whether zap has registered
// user-supplied factory for all sinks with `winfile`-prefixed scheme.
_winfileSinkRegistered bool
_winfileSinkCloser func() error
)
// HandleLoggingParams reads logging parameters.
// If a user selected debug level -- function enables it.
// If logPath is configured -- function creates a dir and a file for logging.
// If logPath is configured on Windows -- function returns closer to be
// able to close sink for the opened log output file.
// If the program is run in TTY then logger adds timestamp to its entries.
func HandleLoggingParams(debug bool, cfg config.ApplicationConfiguration) (*zap.Logger, *zap.AtomicLevel, func() error, error) {
var (
level = zapcore.InfoLevel
err error
)
if len(cfg.LogLevel) > 0 {
level, err = zapcore.ParseLevel(cfg.LogLevel)
if err != nil {
return nil, nil, nil, fmt.Errorf("log setting: %w", err)
}
}
if debug {
level = zapcore.DebugLevel
}
cc := zap.NewProductionConfig()
cc.DisableCaller = true
cc.DisableStacktrace = true
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
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
if logPath := cfg.LogPath; logPath != "" {
if err := io.MakeDirForFile(logPath, "logger"); err != nil {
return nil, nil, nil, err
}
if runtime.GOOS == "windows" {
if !_winfileSinkRegistered {
// See https://github.com/uber-go/zap/issues/621.
err := zap.RegisterSink("winfile", func(u *url.URL) (zap.Sink, error) {
if u.User != nil {
return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u)
}
if u.Fragment != "" {
return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u)
}
if u.RawQuery != "" {
return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u)
}
// Error messages are better if we check hostname and port separately.
if u.Port() != "" {
return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u)
}
if hn := u.Hostname(); hn != "" && hn != "localhost" {
return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u)
}
switch u.Path {
case "stdout":
return os.Stdout, nil
case "stderr":
return os.Stderr, nil
}
f, err := os.OpenFile(u.Path[1:], // Remove leading slash left after url.Parse.
os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
_winfileSinkCloser = func() error {
_winfileSinkCloser = nil
return f.Close()
}
return f, err
})
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to register windows-specific sinc: %w", err)
}
_winfileSinkRegistered = true
}
logPath = "winfile:///" + logPath
}
cc.OutputPaths = []string{logPath}
}
log, err := cc.Build()
return log, &cc.Level, _winfileSinkCloser, err
}
// GetRPCWithActor returns an RPC client instance and Actor instance for the given context.
func GetRPCWithActor(gctx context.Context, ctx *cli.Context, signers []actor.SignerAccount) (*rpcclient.Client, *actor.Actor, cli.ExitCoder) {
c, err := GetRPCClient(gctx, ctx)
if err != nil {
return nil, nil, err
}
a, actorErr := actor.New(c, signers)
if actorErr != nil {
c.Close()
return nil, nil, cli.NewExitError(fmt.Errorf("failed to create Actor: %w", actorErr), 1)
}
return c, a, nil
}
// GetAccFromContext returns account and wallet from context. If address is not set, default address is used.
func GetAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error) {
var addr util.Uint160
wPath := ctx.String("wallet")
walletConfigPath := ctx.String("wallet-config")
if len(wPath) != 0 && len(walletConfigPath) != 0 {
return nil, nil, errConflictingWalletFlags
}
if len(wPath) == 0 && len(walletConfigPath) == 0 {
return nil, nil, errNoWallet
}
var pass *string
if len(walletConfigPath) != 0 {
cfg, err := ReadWalletConfig(walletConfigPath)
if err != nil {
return nil, nil, err
}
wPath = cfg.Path
pass = &cfg.Password
}
wall, err := wallet.NewWalletFromFile(wPath)
if err != nil {
return nil, nil, err
}
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
addr = addrFlag.Uint160()
} else {
addr = wall.GetChangeAddress()
if addr.Equals(util.Uint160{}) {
return nil, wall, errors.New("can't get default address")
}
}
acc, err := GetUnlockedAccount(wall, addr, pass)
return acc, wall, err
}
// GetUnlockedAccount returns account from wallet, address and uses pass to unlock specified account if given.
// If the password is not given, then it is requested from user.
func GetUnlockedAccount(wall *wallet.Wallet, addr util.Uint160, pass *string) (*wallet.Account, error) {
acc := wall.GetAccount(addr)
if acc == nil {
return nil, fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr))
}
if acc.CanSign() || acc.EncryptedWIF == "" {
return acc, nil
}
if pass == nil {
rawPass, err := input.ReadPassword(
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
if err != nil {
return nil, fmt.Errorf("Error reading password: %w", err)
}
trimmed := strings.TrimRight(string(rawPass), "\n")
pass = &trimmed
}
err := acc.Decrypt(*pass, wall.Scrypt)
if err != nil {
return nil, err
}
return acc, nil
}
// ReadWalletConfig reads wallet config from the given path.
func ReadWalletConfig(configPath string) (*config.Wallet, error) {
file, err := os.Open(configPath)
if err != nil {
return nil, err
}
defer file.Close()
configData, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("unable to read wallet config: %w", err)
}
cfg := &config.Wallet{}
err = yaml.Unmarshal(configData, &cfg)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal wallet config YAML: %w", err)
}
return cfg, nil
}

View file

@ -1,57 +0,0 @@
package options
import (
"flag"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestGetNetwork(t *testing.T) {
t.Run("privnet", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.Equal(t, netmode.PrivNet, GetNetwork(ctx))
})
t.Run("testnet", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Bool("testnet", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.Equal(t, netmode.TestNet, GetNetwork(ctx))
})
t.Run("mainnet", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Bool("mainnet", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.Equal(t, netmode.MainNet, GetNetwork(ctx))
})
}
func TestGetTimeoutContext(t *testing.T) {
t.Run("default", func(t *testing.T) {
start := time.Now()
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
actualCtx, _ := GetTimeoutContext(ctx)
end := time.Now().Add(DefaultTimeout)
dl, _ := actualCtx.Deadline()
require.True(t, start.Before(dl) && (dl.Before(end) || dl.Equal(end)))
})
t.Run("set", func(t *testing.T) {
start := time.Now()
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Duration("timeout", time.Duration(20), "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
actualCtx, _ := GetTimeoutContext(ctx)
end := time.Now().Add(time.Nanosecond * 20)
dl, _ := actualCtx.Deadline()
require.True(t, start.Before(dl) && (dl.Before(end) || dl.Equal(end)))
})
}

View file

@ -1,50 +0,0 @@
package paramcontext
import (
"encoding/json"
"fmt"
"os"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
// InitAndSave creates an incompletely signed transaction which can be used
// as an input to `multisig sign`. If a wallet.Account is given and can sign,
// it's signed as well using it.
func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Account, filename string) error {
scCtx := context.NewParameterContext(context.TransactionType, net, tx)
if acc != nil && acc.CanSign() {
sign := acc.SignHashable(net, tx)
if err := scCtx.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil {
return fmt.Errorf("can't add signature: %w", err)
}
}
return Save(scCtx, filename)
}
// Read reads the parameter context from the file.
func Read(filename string) (*context.ParameterContext, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("can't read input file: %w", err)
}
c := new(context.ParameterContext)
if err := json.Unmarshal(data, c); err != nil {
return nil, fmt.Errorf("can't parse transaction: %w", err)
}
return c, nil
}
// Save writes the parameter context to the file.
func Save(c *context.ParameterContext, filename string) error {
if data, err := json.Marshal(c); err != nil {
return fmt.Errorf("can't marshal transaction: %w", err)
} else if err := os.WriteFile(filename, data, 0644); err != nil {
return fmt.Errorf("can't write transaction to file: %w", err)
}
return nil
}

View file

@ -1,314 +0,0 @@
package query
import (
"bytes"
"encoding/base64"
"fmt"
"sort"
"strconv"
"strings"
"text/tabwriter"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/urfave/cli"
)
// NewCommands returns 'query' command.
func NewCommands() []cli.Command {
queryTxFlags := append([]cli.Flag{
cli.BoolFlag{
Name: "verbose, v",
Usage: "Output full tx info and execution logs",
},
}, options.RPC...)
return []cli.Command{{
Name: "query",
Usage: "Query data from RPC node",
Subcommands: []cli.Command{
{
Name: "candidates",
Usage: "Get candidates and votes",
UsageText: "neo-go query candidates -r endpoint [-s timeout]",
Action: queryCandidates,
Flags: options.RPC,
},
{
Name: "committee",
Usage: "Get committee list",
UsageText: "neo-go query committee -r endpoint [-s timeout]",
Action: queryCommittee,
Flags: options.RPC,
},
{
Name: "height",
Usage: "Get node height",
UsageText: "neo-go query height -r endpoint [-s timeout]",
Action: queryHeight,
Flags: options.RPC,
},
{
Name: "tx",
Usage: "Query transaction status",
UsageText: "neo-go query tx <hash> -r endpoint [-s timeout] [-v]",
Action: queryTx,
Flags: queryTxFlags,
},
{
Name: "voter",
Usage: "Print NEO holder account state",
UsageText: "neo-go query voter <address> -r endpoint [-s timeout]",
Action: queryVoter,
Flags: options.RPC,
},
},
}}
}
func queryTx(ctx *cli.Context) error {
args := ctx.Args()
if len(args) == 0 {
return cli.NewExitError("Transaction hash is missing", 1)
} else if len(args) > 1 {
return cli.NewExitError("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)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
}
txOut, err := c.GetRawTransactionVerbose(txHash)
if err != nil {
return cli.NewExitError(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)
}
}
err = DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose"))
if err != nil {
return cli.NewExitError(err, 1)
}
return nil
}
func DumpApplicationLog(
ctx *cli.Context,
res *result.ApplicationLog,
tx *transaction.Transaction,
txMeta *result.TransactionMetadata,
verbose bool) error {
var buf []byte
buf = fmt.Appendf(buf, "Hash:\t%s\n", tx.Hash().StringLE())
buf = fmt.Appendf(buf, "OnChain:\t%t\n", res != nil)
if res == nil {
buf = fmt.Appendf(buf, "ValidUntil:\t%s\n", strconv.FormatUint(uint64(tx.ValidUntilBlock), 10))
} else {
if txMeta != nil {
buf = fmt.Appendf(buf, "BlockHash:\t%s\n", txMeta.Blockhash.StringLE())
}
if len(res.Executions) != 1 {
buf = fmt.Appendf(buf, "Success:\tunknown (no execution data)\n")
} else {
buf = fmt.Appendf(buf, "Success:\t%t\n", res.Executions[0].VMState == vmstate.Halt)
}
}
if verbose {
for _, sig := range tx.Signers {
buf = fmt.Appendf(buf, "Signer:\t%s (%s)\n", address.Uint160ToString(sig.Account), sig.Scopes)
}
buf = fmt.Appendf(buf, "SystemFee:\t%s GAS\n", fixedn.Fixed8(tx.SystemFee).String())
buf = fmt.Appendf(buf, "NetworkFee:\t%s GAS\n", fixedn.Fixed8(tx.NetworkFee).String())
buf = fmt.Appendf(buf, "Script:\t%s\n", base64.StdEncoding.EncodeToString(tx.Script))
v := vm.New()
v.Load(tx.Script)
opts := bytes.NewBuffer(nil)
v.PrintOps(opts)
buf = append(buf, opts.Bytes()...)
if res != nil {
for _, e := range res.Executions {
if e.VMState != vmstate.Halt {
buf = fmt.Appendf(buf, "Exception:\t%s\n", e.FaultException)
}
}
}
}
tw := tabwriter.NewWriter(ctx.App.Writer, 0, 4, 4, '\t', 0)
_, err := tw.Write(buf)
if err != nil {
return err
}
return tw.Flush()
}
func queryCandidates(ctx *cli.Context) error {
var err error
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
}
vals, err := c.GetCandidates()
if err != nil {
return cli.NewExitError(err, 1)
}
comm, err := c.GetCommittee()
if err != nil {
return cli.NewExitError(err, 1)
}
sort.Slice(vals, func(i, j int) bool {
if vals[i].Active != vals[j].Active {
return vals[i].Active
}
if vals[i].Votes != vals[j].Votes {
return vals[i].Votes > vals[j].Votes
}
return vals[i].PublicKey.Cmp(&vals[j].PublicKey) == -1
})
var res []byte
res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n")
for _, val := range vals {
res = fmt.Appendf(res, "%s\t%d\t%t\t%t\n", val.PublicKey.StringCompressed(), val.Votes, comm.Contains(&val.PublicKey), val.Active)
}
tw := tabwriter.NewWriter(ctx.App.Writer, 0, 2, 2, ' ', 0)
_, err = tw.Write(res)
if err != nil {
return err
}
return tw.Flush()
}
func queryCommittee(ctx *cli.Context) error {
var err error
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
}
comm, err := c.GetCommittee()
if err != nil {
return cli.NewExitError(err, 1)
}
for _, k := range comm {
fmt.Fprintln(ctx.App.Writer, k.StringCompressed())
}
return nil
}
func queryHeight(ctx *cli.Context) error {
var err error
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return cli.NewExitError(err, 1)
}
blockCount, err := c.GetBlockCount()
if err != nil {
return cli.NewExitError(err, 1)
}
blockHeight := blockCount - 1 // GetBlockCount returns block count (including 0), not the highest block index.
fmt.Fprintf(ctx.App.Writer, "Latest block: %d\n", blockHeight)
stateHeight, err := c.GetStateHeight()
if err == nil { // We can be talking to a node without getstateheight request support.
fmt.Fprintf(ctx.App.Writer, "Validated state: %d\n", stateHeight.Validated)
}
return nil
}
func queryVoter(ctx *cli.Context) error {
args := ctx.Args()
if len(args) == 0 {
return cli.NewExitError("No address specified", 1)
} else if len(args) > 1 {
return cli.NewExitError("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)
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, exitErr := options.GetRPCClient(gctx, ctx)
if exitErr != nil {
return exitErr
}
neoToken := neo.NewReader(invoker.New(c, nil))
st, err := neoToken.GetAccountState(addr)
if err != nil {
return cli.NewExitError(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)
}
voted := "null"
if st.VoteTo != nil {
voted = fmt.Sprintf("%s (%s)", st.VoteTo.StringCompressed(), address.Uint160ToString(st.VoteTo.GetScriptHash()))
}
fmt.Fprintf(ctx.App.Writer, "\tVoted: %s\n", voted)
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec)))
fmt.Fprintf(ctx.App.Writer, "\tBlock: %d\n", st.BalanceHeight)
return nil
}

View file

@ -1,155 +0,0 @@
package query_test
import (
"encoding/base64"
"fmt"
"regexp"
"strconv"
"strings"
"testing"
"time"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
func TestQueryTx(t *testing.T) {
e := testcli.NewExecutorSuspended(t)
w, err := wallet.NewWalletFromFile("../testdata/testwallet.json")
require.NoError(t, err)
transferArgs := []string{
"neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--to", w.Accounts[0].Address,
"--token", "NEO",
"--from", testcli.ValidatorAddr,
"--force",
}
e.In.WriteString("one\r")
e.Run(t, append(transferArgs, "--amount", "1")...)
line := e.GetNextLine(t)
txHash, err := util.Uint256DecodeStringLE(line)
require.NoError(t, err)
tx, ok := e.Chain.GetMemPool().TryGetValue(txHash)
require.True(t, ok)
args := []string{"neo-go", "query", "tx", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
e.Run(t, append(args, txHash.StringLE())...)
e.CheckNextLine(t, `Hash:\s+`+txHash.StringLE())
e.CheckNextLine(t, `OnChain:\s+false`)
e.CheckNextLine(t, `ValidUntil:\s+`+strconv.FormatUint(uint64(tx.ValidUntilBlock), 10))
e.CheckEOF(t)
go e.Chain.Run()
require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50)
e.Run(t, append(args, txHash.StringLE())...)
e.CheckNextLine(t, `Hash:\s+`+txHash.StringLE())
e.CheckNextLine(t, `OnChain:\s+true`)
_, height, err := e.Chain.GetTransaction(txHash)
require.NoError(t, err)
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE())
e.CheckNextLine(t, `Success:\s+true`)
e.CheckEOF(t)
t.Run("verbose", func(t *testing.T) {
e.Run(t, append(args, "--verbose", txHash.StringLE())...)
compareQueryTxVerbose(t, e, tx)
t.Run("FAULT", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
"--wallet", testcli.ValidatorWallet,
"--address", testcli.ValidatorAddr,
"--force",
random.Uint160().StringLE(),
"randomMethod")
e.CheckNextLine(t, `Warning:`)
e.CheckNextLine(t, "Sending transaction")
line := strings.TrimPrefix(e.GetNextLine(t), "Sent invocation transaction ")
txHash, err := util.Uint256DecodeStringLE(line)
require.NoError(t, err)
require.Eventually(t, func() bool { _, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application); return aerErr == nil }, time.Second*2, time.Millisecond*50)
tx, _, err := e.Chain.GetTransaction(txHash)
require.NoError(t, err)
e.Run(t, append(args, "--verbose", txHash.StringLE())...)
compareQueryTxVerbose(t, e, tx)
})
})
t.Run("invalid", func(t *testing.T) {
t.Run("missing tx argument", func(t *testing.T) {
e.RunWithError(t, args...)
})
t.Run("excessive arguments", func(t *testing.T) {
e.RunWithError(t, append(args, txHash.StringLE(), txHash.StringLE())...)
})
t.Run("invalid hash", func(t *testing.T) {
e.RunWithError(t, append(args, "notahash")...)
})
t.Run("good hash, missing tx", func(t *testing.T) {
e.RunWithError(t, append(args, random.Uint256().StringLE())...)
})
})
}
func compareQueryTxVerbose(t *testing.T, e *testcli.Executor, tx *transaction.Transaction) {
e.CheckNextLine(t, `Hash:\s+`+tx.Hash().StringLE())
e.CheckNextLine(t, `OnChain:\s+true`)
_, height, err := e.Chain.GetTransaction(tx.Hash())
require.NoError(t, err)
e.CheckNextLine(t, `BlockHash:\s+`+e.Chain.GetHeaderHash(height).StringLE())
res, _ := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
e.CheckNextLine(t, fmt.Sprintf(`Success:\s+%t`, res[0].Execution.VMState == vmstate.Halt))
for _, s := range tx.Signers {
e.CheckNextLine(t, fmt.Sprintf(`Signer:\s+%s\s*\(%s\)`, address.Uint160ToString(s.Account), s.Scopes.String()))
}
e.CheckNextLine(t, `SystemFee:\s+`+fixedn.Fixed8(tx.SystemFee).String()+" GAS$")
e.CheckNextLine(t, `NetworkFee:\s+`+fixedn.Fixed8(tx.NetworkFee).String()+" GAS$")
e.CheckNextLine(t, `Script:\s+`+regexp.QuoteMeta(base64.StdEncoding.EncodeToString(tx.Script)))
c := vm.NewContext(tx.Script)
n := 0
for ; c.NextIP() < c.LenInstr(); _, _, err = c.Next() {
require.NoError(t, err)
n++
}
e.CheckScriptDump(t, n)
if res[0].Execution.VMState != vmstate.Halt {
e.CheckNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
}
e.CheckEOF(t)
}
func TestQueryHeight(t *testing.T) {
e := testcli.NewExecutor(t, true)
args := []string{"neo-go", "query", "height", "--rpc-endpoint", "http://" + e.RPC.Addresses()[0]}
e.Run(t, args...)
e.CheckNextLine(t, `^Latest block: [0-9]+$`)
e.CheckNextLine(t, `^Validated state: [0-9]+$`)
e.CheckEOF(t)
t.Run("excessive arguments", func(t *testing.T) {
e.RunWithError(t, append(args, "something")...)
})
}

View file

@ -1,158 +0,0 @@
package server_test
import (
"os"
"path/filepath"
"strconv"
"testing"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
// generated via `go run ./scripts/gendump/main.go --out ./cli/server/testdata/chain50x2.acc --blocks 50 --txs 2`.
const inDump = "./testdata/chain50x2.acc"
func TestDBRestoreDump(t *testing.T) {
tmpDir := t.TempDir()
loadConfig := func(t *testing.T) config.Config {
chainPath := filepath.Join(tmpDir, "neogotestchain")
cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
return cfg
}
cfg := loadConfig(t)
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e := testcli.NewExecutor(t, false)
stateDump := filepath.Join(tmpDir, "neogo.teststate")
baseArgs := []string{"neo-go", "db", "restore", "--unittest",
"--config-path", tmpDir, "--in", inDump, "--dump", stateDump}
t.Run("excessive restore parameters", func(t *testing.T) {
e.RunWithError(t, append(baseArgs, "something")...)
})
// First 15 blocks.
e.Run(t, append(baseArgs, "--count", "15")...)
// Big count.
e.RunWithError(t, append(baseArgs, "--count", "1000")...)
// Continue 15..25
e.Run(t, append(baseArgs, "--count", "10")...)
// Continue till end.
e.Run(t, baseArgs...)
// Dump and compare.
dumpPath := filepath.Join(tmpDir, "testdump.acc")
t.Run("missing config", func(t *testing.T) {
e.RunWithError(t, "neo-go", "db", "dump", "--privnet",
"--config-path", tmpDir, "--out", dumpPath)
})
t.Run("bad logger config", func(t *testing.T) {
badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg = loadConfig(t)
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
out, err = yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e.RunWithError(t, "neo-go", "db", "dump", "--unittest",
"--config-path", badConfigDir, "--out", dumpPath)
})
t.Run("bad storage config", func(t *testing.T) {
badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg = loadConfig(t)
cfg.ApplicationConfiguration.DBConfiguration.Type = ""
out, err = yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath = filepath.Join(badConfigDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e.RunWithError(t, "neo-go", "db", "dump", "--unittest",
"--config-path", badConfigDir, "--out", dumpPath)
})
baseCmd := []string{"neo-go", "db", "dump", "--unittest",
"--config-path", tmpDir, "--out", dumpPath}
t.Run("invalid start/count", func(t *testing.T) {
e.RunWithError(t, append(baseCmd, "--start", "5", "--count", strconv.Itoa(50-5+1+1))...)
})
t.Run("excessive dump parameters", func(t *testing.T) {
e.RunWithError(t, append(baseCmd, "something")...)
})
e.Run(t, baseCmd...)
d1, err := os.ReadFile(inDump)
require.NoError(t, err)
d2, err := os.ReadFile(dumpPath)
require.NoError(t, err)
require.Equal(t, d1, d2, "dumps differ")
}
func TestDBDumpRestoreIncremental(t *testing.T) {
tmpDir := t.TempDir()
chainPath := filepath.Join(tmpDir, "neogotestchain")
nonincDump := filepath.Join(tmpDir, "nonincDump.acc")
incDump := filepath.Join(tmpDir, "incDump.acc")
cfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
e := testcli.NewExecutor(t, false)
// Create DB from dump.
e.Run(t, "neo-go", "db", "restore", "--unittest", "--config-path", tmpDir, "--in", inDump)
// Create two dumps: non-incremental and incremental.
dumpBaseArgs := []string{"neo-go", "db", "dump", "--unittest",
"--config-path", tmpDir}
// Dump first 15 blocks to a non-incremental dump.
e.Run(t, append(dumpBaseArgs, "--out", nonincDump, "--count", "15")...)
// Dump second 15 blocks to an incremental dump.
e.Run(t, append(dumpBaseArgs, "--out", incDump, "--start", "15", "--count", "15")...)
// Clean the DB.
require.NoError(t, os.RemoveAll(chainPath))
// Restore chain from two dumps.
restoreBaseArgs := []string{"neo-go", "db", "restore", "--unittest", "--config-path", tmpDir}
// Restore first 15 blocks from non-incremental dump.
e.Run(t, append(restoreBaseArgs, "--in", nonincDump)...)
// Restore second 15 blocks from incremental dump.
e.Run(t, append(restoreBaseArgs, "--in", incDump, "-n", "--count", "15")...)
}

View file

@ -1,133 +0,0 @@
package server_test
import (
"errors"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/nspcc-dev/neo-go/cli/server"
"github.com/nspcc-dev/neo-go/internal/testcli"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
func TestServerStart(t *testing.T) {
tmpDir := t.TempDir()
goodCfg, err := config.LoadFile(filepath.Join("..", "..", "config", "protocol.unit_testnet.yml"))
require.NoError(t, err, "could not load config")
ptr := &goodCfg
saveCfg := func(t *testing.T, f func(cfg *config.Config)) string {
cfg := *ptr
chainPath := filepath.Join(t.TempDir(), "neogotestchain")
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath = chainPath
f(&cfg)
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
cfgPath := filepath.Join(tmpDir, "protocol.unit_testnet.yml")
require.NoError(t, os.WriteFile(cfgPath, out, os.ModePerm))
t.Cleanup(func() {
require.NoError(t, os.Remove(cfgPath))
})
return cfgPath
}
baseCmd := []string{"neo-go", "node", "--unittest", "--config-path", tmpDir}
e := testcli.NewExecutor(t, false)
t.Run("invalid config path", func(t *testing.T) {
e.RunWithError(t, baseCmd...)
})
t.Run("bad logger config", func(t *testing.T) {
badConfigDir := t.TempDir()
logfile := filepath.Join(badConfigDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
saveCfg(t, func(cfg *config.Config) {
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
})
e.RunWithError(t, baseCmd...)
})
t.Run("invalid storage", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) {
cfg.ApplicationConfiguration.DBConfiguration.Type = ""
})
e.RunWithError(t, baseCmd...)
})
t.Run("stateroot service is on && StateRootInHeader=true", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) {
cfg.ApplicationConfiguration.StateRoot.Enabled = true
cfg.ProtocolConfiguration.StateRootInHeader = true
})
e.RunWithError(t, baseCmd...)
})
t.Run("invalid Oracle config", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) {
cfg.ApplicationConfiguration.Oracle.Enabled = true
cfg.ApplicationConfiguration.Oracle.UnlockWallet.Path = "bad_orc_wallet.json"
})
e.RunWithError(t, baseCmd...)
})
t.Run("invalid consensus config", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) {
cfg.ApplicationConfiguration.Consensus.Enabled = true
cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path = "bad_consensus_wallet.json"
})
e.RunWithError(t, baseCmd...)
})
t.Run("invalid Notary config", func(t *testing.T) {
t.Run("malformed config", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) {
cfg.ProtocolConfiguration.P2PSigExtensions = false
cfg.ApplicationConfiguration.P2PNotary.Enabled = true
})
e.RunWithError(t, baseCmd...)
})
t.Run("invalid wallet", func(t *testing.T) {
saveCfg(t, func(cfg *config.Config) {
cfg.ProtocolConfiguration.P2PSigExtensions = true
cfg.ApplicationConfiguration.P2PNotary.Enabled = true
cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path = "bad_notary_wallet.json"
})
e.RunWithError(t, baseCmd...)
})
})
// We can't properly shutdown server on windows and release the resources.
// Also, windows doesn't support SIGHUP and SIGINT.
if runtime.GOOS != "windows" {
saveCfg(t, func(cfg *config.Config) {})
t.Run("excessive parameters", func(t *testing.T) {
e.RunWithError(t, append(baseCmd, "something")...)
})
t.Run("good", func(t *testing.T) {
go func() {
e.Run(t, baseCmd...)
}()
var line string
require.Eventually(t, func() bool {
line, err = e.Out.ReadString('\n')
if err != nil && !errors.Is(err, io.EOF) {
t.Fatalf("unexpected error while reading CLI output: %s", err)
}
return err == nil
}, 2*time.Second, 100*time.Millisecond)
lines := strings.Split(server.Logo(), "\n")
for _, expected := range lines {
// It should be regexp, so escape all backslashes.
expected = strings.ReplaceAll(expected, `\`, `\\`)
e.CheckLine(t, line, expected)
line = e.GetNextLine(t)
}
e.CheckNextLine(t, "")
e.CheckEOF(t)
})
}
}

View file

@ -1,21 +1,84 @@
package server
import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"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/core/storage/dboper"
"github.com/nspcc-dev/neo-go/pkg/util"
)
type dump []blockDump
type blockDump struct {
Block uint32 `json:"block"`
Size int `json:"size"`
Storage []dboper.Operation `json:"storage"`
Block uint32 `json:"block"`
Size int `json:"size"`
Storage []storageOp `json:"storage"`
}
type storageOp struct {
State string `json:"state"`
Key string `json:"key"`
Value string `json:"value,omitempty"`
}
// NEO has some differences of key storing.
// out format: script hash in LE + key
// neo format: script hash in BE + byte(0) + key with 0 between every 16 bytes, padded to len 16.
func toNeoStorageKey(key []byte) []byte {
if len(key) < util.Uint160Size {
panic("invalid key in storage")
}
return mpt.ToNeoStorageKey(key)
}
// batchToMap converts batch to a map so that JSON is compatible
// with https://github.com/NeoResearch/neo-storage-audit/
func batchToMap(index uint32, batch *storage.MemBatch) blockDump {
size := len(batch.Put) + len(batch.Deleted)
ops := make([]storageOp, 0, size)
for i := range batch.Put {
key := batch.Put[i].Key
if len(key) == 0 || key[0] != byte(storage.STStorage) {
continue
}
op := "Added"
if batch.Put[i].Exists {
op = "Changed"
}
key = toNeoStorageKey(key[1:])
ops = append(ops, storageOp{
State: op,
Key: hex.EncodeToString(key),
Value: "00" + hex.EncodeToString(batch.Put[i].Value),
})
}
for i := range batch.Deleted {
key := batch.Deleted[i].Key
if len(key) == 0 || key[0] != byte(storage.STStorage) || !batch.Deleted[i].Exists {
continue
}
key = toNeoStorageKey(key[1:])
ops = append(ops, storageOp{
State: "Deleted",
Key: hex.EncodeToString(key),
})
}
return blockDump{
Block: index,
Size: len(ops),
Storage: ops,
}
}
func newDump() *dump {
@ -23,12 +86,8 @@ func newDump() *dump {
}
func (d *dump) add(index uint32, batch *storage.MemBatch) {
ops := storage.BatchToOperations(batch)
*d = append(*d, blockDump{
Block: index,
Size: len(ops),
Storage: ops,
})
m := batchToMap(index, batch)
*d = append(*d, m)
}
func (d *dump) tryPersist(prefix string, index uint32) error {
@ -63,7 +122,7 @@ func (d *dump) tryPersist(prefix string, index uint32) error {
}
func readFile(path string) (*dump, error) {
data, err := os.ReadFile(path)
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
@ -82,8 +141,8 @@ func readFile(path string) (*dump, error) {
// File dump-block-$FILENO.json contains blocks from $FILENO-999, $FILENO
// Example: file `BlockStorage_100000/dump-block-6000.json` contains blocks from 5001 to 6000.
func getPath(prefix string, index uint32) (string, error) {
dirN := ((index + 99999) / 100000) * 100000
dir := fmt.Sprintf("BlockStorage_%d", dirN)
dirN := (index-1)/100000 + 1
dir := fmt.Sprintf("BlockStorage_%d00000", dirN)
path := filepath.Join(prefix, dir)
info, err := os.Stat(path)
@ -96,7 +155,7 @@ func getPath(prefix string, index uint32) (string, error) {
return "", fmt.Errorf("file `%s` is not a directory", path)
}
fileN := ((index + 999) / 1000) * 1000
file := fmt.Sprintf("dump-block-%d.json", fileN)
fileN := (index-1)/1000 + 1
file := fmt.Sprintf("dump-block-%d000.json", fileN)
return filepath.Join(path, file), nil
}

View file

@ -1,23 +0,0 @@
package server
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
func TestGetPath(t *testing.T) {
testPath := t.TempDir()
actual, err := getPath(testPath, 123)
require.NoError(t, err)
require.Equal(t, filepath.Join(testPath, "BlockStorage_100000", "dump-block-1000.json"), actual)
actual, err = getPath(testPath, 1230)
require.NoError(t, err)
require.Equal(t, filepath.Join(testPath, "BlockStorage_100000", "dump-block-2000.json"), actual)
actual, err = getPath(testPath, 123000)
require.NoError(t, err)
require.Equal(t, filepath.Join(testPath, "BlockStorage_200000", "dump-block-123000.json"), actual)
}

View file

@ -2,31 +2,21 @@ package server
import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/consensus"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
corestate "github.com/nspcc-dev/neo-go/pkg/core/stateroot"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/services/metrics"
"github.com/nspcc-dev/neo-go/pkg/services/notary"
"github.com/nspcc-dev/neo-go/pkg/services/oracle"
"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv"
"github.com/nspcc-dev/neo-go/pkg/services/stateroot"
"github.com/nspcc-dev/neo-go/pkg/network/metrics"
"github.com/nspcc-dev/neo-go/pkg/rpc/server"
"github.com/pkg/errors"
"github.com/urfave/cli"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -34,29 +24,36 @@ import (
// NewCommands returns 'node' command.
func NewCommands() []cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
cfgFlags = append(cfgFlags, options.Network...)
var cfgFlags = []cli.Flag{
cli.StringFlag{Name: "config-path"},
cli.BoolFlag{Name: "privnet, p"},
cli.BoolFlag{Name: "mainnet, m"},
cli.BoolFlag{Name: "testnet, t"},
cli.BoolFlag{Name: "debug, d"},
}
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
copy(cfgWithCountFlags, 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)",
},
)
var cfgCountOutFlags = make([]cli.Flag, len(cfgWithCountFlags))
copy(cfgCountOutFlags, cfgWithCountFlags)
cfgCountOutFlags = append(cfgCountOutFlags,
cli.UintFlag{
Name: "start, s",
Usage: "block number to start from (default: 0)",
},
)
var cfgCountOutFlags = make([]cli.Flag, len(cfgWithCountFlags))
copy(cfgCountOutFlags, cfgWithCountFlags)
cfgCountOutFlags = append(cfgCountOutFlags,
cli.StringFlag{
Name: "out, o",
Usage: "Output file (stdout if not given)",
},
cli.StringFlag{
Name: "state, r",
Usage: "File to export state roots to",
},
)
var cfgCountInFlags = make([]cli.Flag, len(cfgWithCountFlags))
copy(cfgCountInFlags, cfgWithCountFlags)
@ -70,49 +67,38 @@ func NewCommands() []cli.Command {
Usage: "directory for storing JSON dumps",
},
cli.BoolFlag{
Name: "incremental, n",
Usage: "use if dump is incremental",
Name: "diff, k",
Usage: "Use if DB is restore from diff and not full dump",
},
cli.StringFlag{
Name: "state, r",
Usage: "File to import state roots from",
},
)
var cfgHeightFlags = make([]cli.Flag, len(cfgFlags)+1)
copy(cfgHeightFlags, cfgFlags)
cfgHeightFlags[len(cfgHeightFlags)-1] = cli.UintFlag{
Name: "height",
Usage: "Height of the state to reset DB to",
Required: true,
}
return []cli.Command{
{
Name: "node",
Usage: "start a NeoGo node",
UsageText: "neo-go node [--config-path path] [-d] [-p/-m/-t] [--config-file file]",
Action: startServer,
Flags: cfgFlags,
Name: "node",
Usage: "start a NEO node",
Action: startServer,
Flags: cfgFlags,
},
{
Name: "db",
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]",
Action: dumpDB,
Flags: cfgCountOutFlags,
Name: "dump",
Usage: "dump blocks (starting with block #1) to the file",
UsageText: "When --start option is provided format is different because " +
"index of the first block is written first.",
Action: dumpDB,
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]",
Action: restoreDB,
Flags: cfgCountInFlags,
},
{
Name: "reset",
Usage: "reset database to the previous state",
UsageText: "neo-go db reset --height height [--config-path path] [-p/-m/-t] [--config-file file]",
Action: resetDB,
Flags: cfgHeightFlags,
Name: "restore",
Usage: "restore blocks from the file",
Action: restoreDB,
Flags: cfgCountInFlags,
},
},
},
@ -123,7 +109,6 @@ func newGraceContext() context.Context {
ctx, cancel := context.WithCancel(context.Background())
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
signal.Notify(stop, syscall.SIGTERM)
go func() {
<-stop
cancel()
@ -131,42 +116,78 @@ func newGraceContext() context.Context {
return ctx
}
// getConfigFromContext looks at path and mode flags in the given config and
// returns appropriate config.
func getConfigFromContext(ctx *cli.Context) (config.Config, error) {
var net = config.ModePrivNet
if ctx.Bool("testnet") {
net = config.ModeTestNet
}
if ctx.Bool("mainnet") {
net = config.ModeMainNet
}
configPath := "./config"
if argCp := ctx.String("config-path"); argCp != "" {
configPath = argCp
}
return config.Load(configPath, net)
}
// handleLoggingParams reads logging parameters.
// If user selected debug level -- function enables it.
// If logPath is configured -- function creates dir and file for logging.
func handleLoggingParams(ctx *cli.Context, cfg config.ApplicationConfiguration) (*zap.Logger, error) {
level := zapcore.InfoLevel
if ctx.Bool("debug") {
level = zapcore.DebugLevel
}
cc := zap.NewProductionConfig()
cc.DisableCaller = true
cc.DisableStacktrace = true
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
cc.Encoding = "console"
cc.Level = zap.NewAtomicLevelAt(level)
cc.Sampling = nil
if logPath := cfg.LogPath; logPath != "" {
if err := io.MakeDirForFile(logPath, "logger"); err != nil {
return nil, err
}
cc.OutputPaths = []string{logPath}
}
return cc.Build()
}
func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *metrics.Service, *metrics.Service, error) {
chain, _, err := initBlockChain(cfg, log)
chain, err := initBlockChain(cfg, log)
if err != nil {
return nil, nil, nil, cli.NewExitError(err, 1)
}
configureAddresses(cfg.ApplicationConfiguration)
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log)
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log)
go chain.Run()
err = prometheus.Start()
if err != nil {
return nil, nil, nil, cli.NewExitError(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)
}
go prometheus.Start()
go pprof.Start()
return chain, prometheus, pprof, nil
}
func dumpDB(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
cfg, err := options.GetConfigFromContext(ctx)
cfg, err := getConfigFromContext(ctx)
if err != nil {
return cli.NewExitError(err, 1)
}
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
log, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
}
count := uint32(ctx.Uint("count"))
start := uint32(ctx.Uint("start"))
@ -184,11 +205,6 @@ func dumpDB(ctx *cli.Context) error {
if err != nil {
return err
}
defer func() {
pprof.ShutDown()
prometheus.ShutDown()
chain.Close()
}()
chainCount := chain.BlockHeight() + 1
if start+count > chainCount {
@ -197,33 +213,76 @@ func dumpDB(ctx *cli.Context) error {
if count == 0 {
count = chainCount - start
}
var rootsWriter *io.BinWriter
if out := ctx.String("state"); out != "" {
rootsStream, err := os.Create(out)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't create file for state roots: %w", err), 1)
}
defer rootsStream.Close()
rootsWriter = io.NewBinWriterFromIO(rootsStream)
}
if start != 0 {
writer.WriteU32LE(start)
}
writer.WriteU32LE(count)
err = chaindump.Dump(chain, writer, start, count)
if err != nil {
return cli.NewExitError(err.Error(), 1)
rootsCount := count
if rootsWriter != nil {
rootsWriter.WriteU32LE(start)
if h := chain.StateHeight() + 1; start+rootsCount > h {
log.Info("state height is low, state root dump will be cut", zap.Uint32("height", h))
rootsCount = h - start
}
rootsWriter.WriteU32LE(rootsCount)
}
for i := start; i < start+count; i++ {
if rootsWriter != nil && i < start+rootsCount {
r, err := chain.GetStateRoot(i)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get stateroot %d: %w", i, err), 1)
}
if err := writeSizedItem(&r.MPTRoot, rootsWriter); err != nil {
return cli.NewExitError(err, 1)
}
}
bh := chain.GetHeaderHash(int(i))
b, err := chain.GetBlock(bh)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to get block %d: %s", i, err), 1)
}
if err := writeSizedItem(b, writer); err != nil {
return cli.NewExitError(err, 1)
}
}
pprof.ShutDown()
prometheus.ShutDown()
chain.Close()
return nil
}
func writeSizedItem(item io.Serializable, w *io.BinWriter) error {
buf := io.NewBufBinWriter()
item.EncodeBinary(buf.BinWriter)
bytes := buf.Bytes()
w.WriteU32LE(uint32(len(bytes)))
w.WriteBytes(bytes)
return w.Err
}
func restoreDB(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
cfg, err := options.GetConfigFromContext(ctx)
cfg, err := getConfigFromContext(ctx)
if err != nil {
return err
}
log, _, logCloser, err := options.HandleLoggingParams(ctx.Bool("debug"), cfg.ApplicationConfiguration)
log, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
}
count := uint32(ctx.Uint("count"))
start := uint32(ctx.Uint("start"))
var inStream = os.Stdin
if in := ctx.String("in"); in != "" {
@ -235,50 +294,84 @@ func restoreDB(ctx *cli.Context) error {
defer inStream.Close()
reader := io.NewBinReaderFromIO(inStream)
var rootsIn *io.BinReader
if in := ctx.String("state"); in != "" {
inStream, err := os.Open(in)
if err != nil {
return cli.NewExitError(err, 1)
}
defer inStream.Close()
rootsIn = io.NewBinReaderFromIO(inStream)
}
dumpDir := ctx.String("dump")
if dumpDir != "" {
cfg.ApplicationConfiguration.SaveStorageBatch = true
cfg.ProtocolConfiguration.SaveStorageBatch = true
}
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
if err != nil {
return err
}
defer func() {
pprof.ShutDown()
prometheus.ShutDown()
chain.Close()
}()
defer chain.Close()
defer prometheus.ShutDown()
defer pprof.ShutDown()
var start uint32
if ctx.Bool("incremental") {
start = reader.ReadU32LE()
if chain.BlockHeight()+1 < start {
return cli.NewExitError(fmt.Errorf("expected height: %d, dump starts at %d",
chain.BlockHeight()+1, start), 1)
dumpStart := uint32(0)
dumpSize := reader.ReadU32LE()
if ctx.Bool("diff") {
// in diff first uint32 is the index of the first block
dumpStart = dumpSize
dumpSize = reader.ReadU32LE()
}
if reader.Err != nil {
return cli.NewExitError(reader.Err, 1)
}
if start < dumpStart {
return cli.NewExitError(fmt.Errorf("input file start from %d block, can't import %d", dumpStart, start), 1)
}
lastBlock := dumpStart + dumpSize
if start+count > lastBlock {
return cli.NewExitError(fmt.Errorf("input file has blocks up until %d, can't read %d starting from %d", lastBlock, count, start), 1)
}
if count == 0 {
count = lastBlock - start
}
var rootStart, rootSize uint32
rootCount := count
if rootsIn != nil {
rootStart = rootsIn.ReadU32LE()
rootSize = rootsIn.ReadU32LE()
if rootsIn.Err != nil {
return cli.NewExitError(fmt.Errorf("error while reading roots file: %w", rootsIn.Err), 1)
}
if start < rootStart {
return cli.NewExitError(fmt.Errorf("roots file start from %d root, can't import %d", rootStart, start), 1)
}
lastRoot := rootStart + rootSize
if rootStart+rootCount > lastRoot {
log.Info("state root height is low", zap.Uint32("height", lastRoot))
rootCount = lastRoot - rootStart
}
}
var skip uint32
if chain.BlockHeight() != 0 {
skip = chain.BlockHeight() + 1 - start
i := dumpStart
for ; i < start; i++ {
_, err := readBytes(reader)
if err != nil {
return cli.NewExitError(err, 1)
}
}
var allBlocks = reader.ReadU32LE()
if reader.Err != nil {
return cli.NewExitError(err, 1)
if rootsIn != nil {
for j := rootStart; j < start; j++ {
_, err := readBytes(rootsIn)
if err != nil {
return cli.NewExitError(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)
}
if count == 0 {
count = allBlocks - skip
}
log.Info("initialize restore",
zap.Uint32("start", start),
zap.Uint32("height", chain.BlockHeight()),
zap.Uint32("skip", skip),
zap.Uint32("count", count))
gctx := newGraceContext()
var lastIndex uint32
@ -287,354 +380,125 @@ func restoreDB(ctx *cli.Context) error {
_ = dump.tryPersist(dumpDir, lastIndex)
}()
var f = func(b *block.Block) error {
for ; i < start+count; i++ {
select {
case <-gctx.Done():
return gctx.Err()
return cli.NewExitError("cancelled", 1)
default:
return nil
}
}
if dumpDir != "" {
f = func(b *block.Block) error {
select {
case <-gctx.Done():
return gctx.Err()
default:
block := new(block.Block)
if err := readSizedItem(block, reader); err != nil {
return cli.NewExitError(err, 1)
}
var skipBlock bool
if block.Index == 0 && i == 0 && start == 0 {
genesis, err := chain.GetBlock(block.Hash())
if err == nil && genesis.Index == 0 {
log.Info("skipped genesis block", zap.String("hash", block.Hash().StringLE()))
skipBlock = true
}
batch := chain.LastBatch()
// The genesis block may already be persisted, so LastBatch() will return nil.
if batch == nil && b.Index == 0 {
return nil
}
if !skipBlock {
err = chain.AddBlock(block)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to add block %d: %s", i, err), 1)
}
dump.add(b.Index, batch)
lastIndex = b.Index
if b.Index%1000 == 0 {
if err := dump.tryPersist(dumpDir, b.Index); err != nil {
return fmt.Errorf("can't dump storage to file: %w", err)
if dumpDir != "" {
batch := chain.LastBatch()
dump.add(block.Index, batch)
lastIndex = block.Index
if block.Index%1000 == 0 {
if err := dump.tryPersist(dumpDir, block.Index); err != nil {
return cli.NewExitError(fmt.Errorf("can't dump storage to file: %v", err), 1)
}
}
}
return nil
}
}
err = chaindump.Restore(chain, reader, skip, count, f)
if err != nil {
return cli.NewExitError(err, 1)
if rootsIn != nil && i < rootStart+rootCount {
sr := new(state.MPTRoot)
if err := readSizedItem(sr, rootsIn); err != nil {
return cli.NewExitError(err, 1)
}
err = chain.AddStateRoot(sr)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't add state root: %w", err), 1)
}
}
}
return nil
}
func resetDB(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
func readSizedItem(item io.Serializable, r *io.BinReader) error {
bytes, err := readBytes(r)
if err != nil {
return err
}
cfg, err := options.GetConfigFromContext(ctx)
if err != nil {
return cli.NewExitError(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)
}
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)
}
err = chain.Reset(h)
if err != nil {
return cli.NewExitError(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 nil
newReader := io.NewBinReaderFromBuf(bytes)
item.DecodeBinary(newReader)
return newReader.Err
}
// oracleService is an interface representing Oracle service with network.Service
// capabilities and ability to submit oracle responses.
type oracleService interface {
rpcsrv.OracleHandler
network.Service
}
func mkOracle(config config.OracleConfiguration, magic netmode.Magic, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (oracleService, error) {
if !config.Enabled {
return nil, nil
// readBytes performs reading of block size and then bytes with the length equal to that size.
func readBytes(reader *io.BinReader) ([]byte, error) {
var size = reader.ReadU32LE()
bytes := make([]byte, size)
reader.ReadBytes(bytes)
if reader.Err != nil {
return nil, reader.Err
}
orcCfg := oracle.Config{
Log: log,
Network: magic,
MainCfg: config,
Chain: chain,
OnTransaction: serv.RelayTxn,
}
orc, err := oracle.NewOracle(orcCfg)
if err != nil {
return nil, fmt.Errorf("can't initialize Oracle module: %w", err)
}
chain.SetOracle(orc)
serv.AddService(orc)
return orc, nil
}
func mkConsensus(config config.Consensus, tpb time.Duration, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (consensus.Service, error) {
if !config.Enabled {
return nil, nil
}
srv, err := consensus.NewService(consensus.Config{
Logger: log,
Broadcast: serv.BroadcastExtensible,
Chain: chain,
BlockQueue: serv.GetBlockQueue(),
ProtocolConfiguration: chain.GetConfig().ProtocolConfiguration,
RequestTx: serv.RequestTx,
StopTxFlow: serv.StopTxFlow,
Wallet: config.UnlockWallet,
TimePerBlock: tpb,
})
if err != nil {
return nil, fmt.Errorf("can't initialize Consensus module: %w", err)
}
serv.AddConsensusService(srv, srv.OnPayload, srv.OnTransaction)
return srv, nil
}
func mkP2PNotary(config config.P2PNotary, chain *core.Blockchain, serv *network.Server, log *zap.Logger) (*notary.Notary, error) {
if !config.Enabled {
return nil, nil
}
if !chain.P2PSigExtensionsEnabled() {
return nil, errors.New("P2PSigExtensions are disabled, but Notary service is enabled")
}
cfg := notary.Config{
MainCfg: config,
Chain: chain,
Log: log,
}
n, err := notary.NewNotary(cfg, serv.Net, serv.GetNotaryPool(), func(tx *transaction.Transaction) error {
err := serv.RelayTxn(tx)
if err != nil && !errors.Is(err, core.ErrAlreadyExists) && !errors.Is(err, core.ErrAlreadyInPool) {
return fmt.Errorf("can't relay completed notary transaction: hash %s, error: %w", tx.Hash().StringLE(), err)
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to create Notary module: %w", err)
}
serv.AddService(n)
chain.SetNotary(n)
return n, nil
return bytes, nil
}
func startServer(ctx *cli.Context) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
cfg, err := getConfigFromContext(ctx)
if err != nil {
return err
}
cfg, err := options.GetConfigFromContext(ctx)
log, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
}
var logDebug = ctx.Bool("debug")
log, logLevel, logCloser, err := options.HandleLoggingParams(logDebug, cfg.ApplicationConfiguration)
if err != nil {
return cli.NewExitError(err, 1)
}
if logCloser != nil {
defer func() { _ = logCloser() }()
return err
}
grace, cancel := context.WithCancel(newGraceContext())
defer cancel()
serverConfig, err := network.NewServerConfig(cfg)
if err != nil {
return cli.NewExitError(err, 1)
}
serverConfig := network.NewServerConfig(cfg)
chain, prometheus, pprof, err := initBCWithMetrics(cfg, log)
if err != nil {
return cli.NewExitError(err, 1)
return err
}
defer func() {
pprof.ShutDown()
prometheus.ShutDown()
chain.Close()
}()
serv, err := network.NewServer(serverConfig, chain, chain.GetStateSyncModule(), log)
serv, err := network.NewServer(serverConfig, chain, log)
if err != nil {
return cli.NewExitError(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)
}
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)
}
dbftSrv, err := mkConsensus(cfg.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
if err != nil {
return cli.NewExitError(err, 1)
}
p2pNotary, err := mkP2PNotary(cfg.ApplicationConfiguration.P2PNotary, chain, serv, log)
if err != nil {
return cli.NewExitError(err, 1)
return cli.NewExitError(fmt.Errorf("failed to create network server: %v", err), 1)
}
rpcServer := server.New(chain, cfg.ApplicationConfiguration.RPC, serv, log)
errChan := make(chan error)
rpcServer := rpcsrv.New(chain, cfg.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
serv.AddService(&rpcServer)
serv.Start()
if !cfg.ApplicationConfiguration.RPC.StartWhenSynchronized {
// Run RPC server in a separate routine. This is necessary to avoid a potential
// deadlock: Start() can write errors to errChan which is not yet read in the
// current execution context (see for-loop below).
go rpcServer.Start()
}
go serv.Start(errChan)
go rpcServer.Start(errChan)
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, sighup)
signal.Notify(sigCh, sigusr1)
signal.Notify(sigCh, sigusr2)
fmt.Fprintln(ctx.App.Writer, Logo())
fmt.Fprintln(ctx.App.Writer, serv.UserAgent)
fmt.Fprintln(ctx.App.Writer)
fmt.Println(logo())
fmt.Println(serv.UserAgent)
fmt.Println()
var shutdownErr error
Main:
for {
select {
case err := <-errChan:
shutdownErr = fmt.Errorf("server error: %w", err)
shutdownErr = errors.Wrap(err, "Error encountered by server")
cancel()
case sig := <-sigCh:
var newLogLevel = zapcore.InvalidLevel
log.Info("signal received", zap.Stringer("name", sig))
cfgnew, err := options.GetConfigFromContext(ctx)
if err != nil {
log.Warn("can't reread the config file, signal ignored", zap.Error(err))
break // Continue working.
}
if !cfg.ProtocolConfiguration.Equals(&cfgnew.ProtocolConfiguration) {
log.Warn("ProtocolConfiguration changed, signal ignored")
break // Continue working.
}
if !cfg.ApplicationConfiguration.EqualsButServices(&cfgnew.ApplicationConfiguration) {
log.Warn("ApplicationConfiguration changed in incompatible way, signal ignored")
break // Continue working.
}
if !logDebug && cfgnew.ApplicationConfiguration.LogLevel != cfg.ApplicationConfiguration.LogLevel {
newLogLevel, err = zapcore.ParseLevel(cfgnew.ApplicationConfiguration.LogLevel)
if err != nil {
log.Warn("wrong LogLevel in ApplicationConfiguration, signal ignored", zap.Error(err))
break // Continue working.
}
}
switch sig {
case sighup:
if newLogLevel != zapcore.InvalidLevel {
logLevel.SetLevel(newLogLevel)
log.Warn("using new logging level", zap.Stringer("level", newLogLevel))
}
serv.DelService(&rpcServer)
rpcServer.Shutdown()
rpcServer = rpcsrv.New(chain, cfgnew.ApplicationConfiguration.RPC, serv, oracleSrv, log, errChan)
serv.AddService(&rpcServer)
if !cfgnew.ApplicationConfiguration.RPC.StartWhenSynchronized || serv.IsInSync() {
// Here similar to the initial run (see above for-loop), so async.
go rpcServer.Start()
}
pprof.ShutDown()
pprof = metrics.NewPprofService(cfgnew.ApplicationConfiguration.Pprof, log)
err = pprof.Start()
if err != nil {
shutdownErr = fmt.Errorf("failed to start Pprof service: %w", err)
cancel() // Fatal error, like for RPC server.
}
prometheus.ShutDown()
prometheus = metrics.NewPrometheusService(cfgnew.ApplicationConfiguration.Prometheus, log)
err = prometheus.Start()
if err != nil {
shutdownErr = fmt.Errorf("failed to start Prometheus service: %w", err)
cancel() // Fatal error, like for RPC server.
}
case sigusr1:
if oracleSrv != nil {
serv.DelService(oracleSrv)
chain.SetOracle(nil)
rpcServer.SetOracleHandler(nil)
oracleSrv.Shutdown()
}
oracleSrv, err = mkOracle(cfgnew.ApplicationConfiguration.Oracle, cfgnew.ProtocolConfiguration.Magic, chain, serv, log)
if err != nil {
log.Error("failed to create oracle service", zap.Error(err))
break // Keep going.
}
if oracleSrv != nil {
rpcServer.SetOracleHandler(oracleSrv)
if serv.IsInSync() {
oracleSrv.Start()
}
}
if p2pNotary != nil {
serv.DelService(p2pNotary)
chain.SetNotary(nil)
p2pNotary.Shutdown()
}
p2pNotary, err = mkP2PNotary(cfgnew.ApplicationConfiguration.P2PNotary, chain, serv, log)
if err != nil {
log.Error("failed to create notary service", zap.Error(err))
break // Keep going.
}
if p2pNotary != nil && serv.IsInSync() {
p2pNotary.Start()
}
serv.DelExtensibleService(sr, stateroot.Category)
srMod.SetUpdateValidatorsCallback(nil)
sr.Shutdown()
sr, err = stateroot.New(cfgnew.ApplicationConfiguration.StateRoot, srMod, log, chain, serv.BroadcastExtensible)
if err != nil {
log.Error("failed to create state validation service", zap.Error(err))
break // The show must go on.
}
serv.AddExtensibleService(sr, stateroot.Category, sr.OnPayload)
if serv.IsInSync() {
sr.Start()
}
case sigusr2:
if dbftSrv != nil {
serv.DelConsensusService(dbftSrv)
dbftSrv.Shutdown()
}
dbftSrv, err = mkConsensus(cfgnew.ApplicationConfiguration.Consensus, serverConfig.TimePerBlock, chain, serv, log)
if err != nil {
log.Error("failed to create consensus service", zap.Error(err))
break // Whatever happens, I'll leave it all to chance.
}
if dbftSrv != nil && serv.IsInSync() {
dbftSrv.Start()
}
}
cfg = cfgnew
case <-grace.Done():
signal.Stop(sigCh)
serv.Shutdown()
if serverErr := rpcServer.Shutdown(); serverErr != nil {
shutdownErr = errors.Wrap(serverErr, "Error encountered whilst shutting down server")
}
prometheus.ShutDown()
pprof.ShutDown()
chain.Close()
break Main
}
}
@ -646,30 +510,42 @@ Main:
return nil
}
// initBlockChain initializes BlockChain with preselected DB.
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, storage.Store, error) {
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
if err != nil {
return nil, nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %w", err), 1)
}
chain, err := core.NewBlockchain(store, cfg.Blockchain(), log)
if err != nil {
errText := "could not initialize blockchain: %w"
errArgs := []any{err}
closeErr := store.Close()
if closeErr != nil {
errText += "; failed to close the DB: %w"
errArgs = append(errArgs, closeErr)
// configureAddresses sets up addresses for RPC, Prometheus and Pprof depending from the provided config.
// In case RPC or Prometheus or Pprof Address provided each of them will use it.
// In case global Address (of the node) provided and RPC/Prometheus/Pprof don't have configured addresses they will
// use global one. So Node and RPC and Prometheus and Pprof will run on one address.
func configureAddresses(cfg config.ApplicationConfiguration) {
if cfg.Address != "" {
if cfg.RPC.Address == "" {
cfg.RPC.Address = cfg.Address
}
if cfg.Prometheus.Address == "" {
cfg.Prometheus.Address = cfg.Address
}
if cfg.Pprof.Address == "" {
cfg.Pprof.Address = cfg.Address
}
return nil, nil, cli.NewExitError(fmt.Errorf(errText, errArgs...), 1)
}
return chain, store, nil
}
// Logo returns NeoGo logo.
func Logo() string {
// initBlockChain initializes BlockChain with preselected DB.
func initBlockChain(cfg config.Config, log *zap.Logger) (*core.Blockchain, error) {
store, err := storage.NewStore(cfg.ApplicationConfiguration.DBConfiguration)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize storage: %s", err), 1)
}
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration, log)
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %s", err), 1)
}
if cfg.ProtocolConfiguration.AddressVersion != 0 {
address.Prefix = cfg.ProtocolConfiguration.AddressVersion
}
return chain, nil
}
func logo() string {
return `
_ ____________ __________
/ | / / ____/ __ \ / ____/ __ \

View file

@ -1,392 +0,0 @@
package server
import (
"encoding/binary"
"flag"
"os"
"path/filepath"
"runtime"
"testing"
"go.uber.org/zap/zapcore"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"gopkg.in/yaml.v3"
)
// serverTestWD is the default working directory for server tests.
var serverTestWD string
func init() {
var err error
serverTestWD, err = os.Getwd()
if err != nil {
panic("can't get current working directory")
}
}
func TestGetConfigFromContext(t *testing.T) {
t.Run("config-path", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../config", "")
set.Bool("testnet", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
})
t.Run("config-file", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../config", "")
set.Bool("testnet", true, "")
set.String("config-file", "../../config/protocol.testnet.yml", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
})
t.Run("relative-path windows", func(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("skipping Windows specific test")
}
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("relative-path", "..\\..\\config", "")
set.Bool("testnet", true, "")
set.String("config-file", ".\\testdata\\protocol.testnet.windows.yml", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, filepath.Join("..", "..", "config", "chains", "testnet"), cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
require.Equal(t, "C:\\someFolder\\cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path)
require.Equal(t, "C:\\someFolder\\notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
})
t.Run("relative-path non-windows", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping non-Windows specific test")
}
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("relative-path", "../../config", "")
set.Bool("testnet", true, "")
set.String("config-file", "../../config/protocol.testnet.yml", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, filepath.Join("..", "..", "config", "chains", "testnet"), cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
require.Equal(t, "/cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path)
require.Equal(t, "/notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
})
}
func TestHandleLoggingParams(t *testing.T) {
d := t.TempDir()
testLog := filepath.Join(d, "file.log")
t.Run("logdir is a file", func(t *testing.T) {
logfile := filepath.Join(d, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg := config.ApplicationConfiguration{
LogPath: filepath.Join(logfile, "file.log"),
}
_, lvl, closer, err := options.HandleLoggingParams(false, cfg)
require.Error(t, err)
require.Nil(t, lvl)
require.Nil(t, closer)
})
t.Run("broken level", func(t *testing.T) {
cfg := config.ApplicationConfiguration{
LogPath: testLog,
LogLevel: "qwerty",
}
_, lvl, closer, err := options.HandleLoggingParams(false, cfg)
require.Error(t, err)
require.Nil(t, lvl)
require.Nil(t, closer)
})
t.Run("default", func(t *testing.T) {
cfg := config.ApplicationConfiguration{
LogPath: testLog,
}
logger, lvl, closer, err := options.HandleLoggingParams(false, cfg)
require.NotNil(t, lvl)
require.NoError(t, err)
t.Cleanup(func() {
if closer != nil {
require.NoError(t, closer())
}
})
require.Equal(t, zapcore.InfoLevel, lvl.Level())
require.True(t, logger.Core().Enabled(zapcore.InfoLevel))
require.False(t, logger.Core().Enabled(zapcore.DebugLevel))
})
t.Run("warn", func(t *testing.T) {
cfg := config.ApplicationConfiguration{
LogPath: testLog,
LogLevel: "warn",
}
logger, lvl, closer, err := options.HandleLoggingParams(false, cfg)
require.NoError(t, err)
t.Cleanup(func() {
if closer != nil {
require.NoError(t, closer())
}
})
require.Equal(t, zapcore.WarnLevel, lvl.Level())
require.True(t, logger.Core().Enabled(zapcore.WarnLevel))
require.False(t, logger.Core().Enabled(zapcore.InfoLevel))
})
t.Run("debug", func(t *testing.T) {
cfg := config.ApplicationConfiguration{
LogPath: testLog,
}
logger, lvl, closer, err := options.HandleLoggingParams(true, cfg)
require.NoError(t, err)
t.Cleanup(func() {
if closer != nil {
require.NoError(t, closer())
}
})
require.Equal(t, zapcore.DebugLevel, lvl.Level())
require.True(t, logger.Core().Enabled(zapcore.InfoLevel))
require.True(t, logger.Core().Enabled(zapcore.DebugLevel))
})
}
func TestInitBCWithMetrics(t *testing.T) {
d := t.TempDir()
err := os.Chdir(d)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) })
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "")
set.Bool("testnet", true, "")
set.Bool("debug", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
logger, _, closer, err := options.HandleLoggingParams(true, cfg.ApplicationConfiguration)
require.NoError(t, err)
t.Cleanup(func() {
if closer != nil {
require.NoError(t, closer())
}
})
t.Run("bad store", func(t *testing.T) {
_, _, _, err = initBCWithMetrics(config.Config{}, logger)
require.Error(t, err)
})
chain, prometheus, pprof, err := initBCWithMetrics(cfg, logger)
require.NoError(t, err)
t.Cleanup(func() {
chain.Close()
prometheus.ShutDown()
pprof.ShutDown()
})
require.Equal(t, netmode.TestNet, chain.GetConfig().Magic)
}
func TestDumpDB(t *testing.T) {
testDump := "file.acc"
t.Run("too low chain", func(t *testing.T) {
d := t.TempDir()
err := os.Chdir(d)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) })
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("start", 0, "")
set.Int("count", 5, "")
set.String("out", testDump, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = dumpDB(ctx)
require.Error(t, err)
})
t.Run("positive", func(t *testing.T) {
d := t.TempDir()
err := os.Chdir(d)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) })
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("start", 0, "")
set.Int("count", 1, "")
set.String("out", testDump, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = dumpDB(ctx)
require.NoError(t, err)
})
}
func TestRestoreDB(t *testing.T) {
d := t.TempDir()
testDump := "file1.acc"
saveDump := "file2.acc"
err := os.Chdir(d)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) })
// dump first
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
goodCfg := filepath.Join(serverTestWD, "..", "..", "config")
cfgPath := set.String("config-path", goodCfg, "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("start", 0, "")
set.Int("count", 1, "")
set.String("out", testDump, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = dumpDB(ctx)
require.NoError(t, err)
// and then restore
t.Run("invalid config", func(t *testing.T) {
*cfgPath = filepath.Join(serverTestWD, "..", "..", "config_invalid")
require.Error(t, restoreDB(ctx))
})
t.Run("invalid logger path", func(t *testing.T) {
badCfgDir := t.TempDir()
logfile := filepath.Join(badCfgDir, "logdir")
require.NoError(t, os.WriteFile(logfile, []byte{1, 2, 3}, os.ModePerm))
cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.LogPath = filepath.Join(logfile, "file.log")
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml")
require.NoError(t, os.WriteFile(badCfgPath, out, os.ModePerm))
*cfgPath = badCfgDir
require.Error(t, restoreDB(ctx))
*cfgPath = goodCfg
})
t.Run("invalid bc config", func(t *testing.T) {
badCfgDir := t.TempDir()
cfg, err := config.LoadFile(filepath.Join(goodCfg, "protocol.privnet.yml"))
require.NoError(t, err, "could not load config")
cfg.ApplicationConfiguration.DBConfiguration.Type = ""
out, err := yaml.Marshal(cfg)
require.NoError(t, err)
badCfgPath := filepath.Join(badCfgDir, "protocol.privnet.yml")
require.NoError(t, os.WriteFile(badCfgPath, out, os.ModePerm))
*cfgPath = badCfgDir
require.Error(t, restoreDB(ctx))
*cfgPath = goodCfg
})
in := set.String("in", testDump, "")
incremental := set.Bool("incremental", false, "")
t.Run("invalid in", func(t *testing.T) {
*in = "unknown-file"
require.Error(t, restoreDB(ctx))
*in = testDump
})
t.Run("corrupted in: invalid block count", func(t *testing.T) {
inPath := filepath.Join(t.TempDir(), "file3.acc")
require.NoError(t, os.WriteFile(inPath, []byte{1, 2, 3}, // file is expected to start from uint32
os.ModePerm))
*in = inPath
require.Error(t, restoreDB(ctx))
*in = testDump
})
t.Run("corrupted in: corrupted block", func(t *testing.T) {
inPath := filepath.Join(t.TempDir(), "file3.acc")
b, err := os.ReadFile(testDump)
require.NoError(t, err)
b[5] = 0xff // file is expected to start from uint32 (4 bytes) followed by the first block, so corrupt the first block bytes
require.NoError(t, os.WriteFile(inPath, b, os.ModePerm))
*in = inPath
require.Error(t, restoreDB(ctx))
*in = testDump
})
t.Run("incremental dump", func(t *testing.T) {
inPath := filepath.Join(t.TempDir(), "file1_incremental.acc")
b, err := os.ReadFile(testDump)
require.NoError(t, err)
start := make([]byte, 4)
t.Run("good", func(t *testing.T) {
binary.LittleEndian.PutUint32(start, 1) // start from the first block
require.NoError(t, os.WriteFile(inPath, append(start, b...),
os.ModePerm))
*in = inPath
*incremental = true
require.NoError(t, restoreDB(ctx))
})
t.Run("dump is too high", func(t *testing.T) {
binary.LittleEndian.PutUint32(start, 2) // start from the second block
require.NoError(t, os.WriteFile(inPath, append(start, b...),
os.ModePerm))
*in = inPath
*incremental = true
require.Error(t, restoreDB(ctx))
})
*in = testDump
*incremental = false
})
set.String("dump", saveDump, "")
require.NoError(t, restoreDB(ctx))
}
func TestInitBlockChain(t *testing.T) {
t.Run("bad storage", func(t *testing.T) {
_, _, err := initBlockChain(config.Config{}, nil)
require.Error(t, err)
})
t.Run("empty logger", func(t *testing.T) {
_, _, err := initBlockChain(config.Config{
ApplicationConfiguration: config.ApplicationConfiguration{
DBConfiguration: dbconfig.DBConfiguration{
Type: dbconfig.InMemoryDB,
},
},
}, nil)
require.Error(t, err)
})
}
func TestResetDB(t *testing.T) {
d := t.TempDir()
err := os.Chdir(d)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(serverTestWD)) })
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", filepath.Join(serverTestWD, "..", "..", "config"), "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("height", 0, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = resetDB(ctx)
require.NoError(t, err)
}

View file

@ -1,11 +0,0 @@
//go:build !windows
package server
import "syscall"
const (
sighup = syscall.SIGHUP
sigusr1 = syscall.SIGUSR1
sigusr2 = syscall.SIGUSR2
)

View file

@ -1,12 +0,0 @@
//go:build windows
package server
import "syscall"
const (
// Doesn't really matter, Windows can't do it.
sighup = syscall.SIGHUP
sigusr1 = syscall.Signal(0xa)
sigusr2 = syscall.Signal(0xc)
)

Binary file not shown.

View file

@ -1,96 +0,0 @@
ProtocolConfiguration:
Magic: 894710606
MaxBlockSize: 2097152
MaxBlockSystemFee: 150000000000
MaxTraceableBlocks: 2102400
MaxTransactionsPerBlock: 5000
InitialGASSupply: 52000000
TimePerBlock: 15s
MemPoolSize: 50000
StandbyCommittee:
- 023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d
- 03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2
- 02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd
- 03408dcd416396f64783ac587ea1e1593c57d9fea880c8a6a1920e92a259477806
- 02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b
- 0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01
- 030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba
- 025831cee3708e87d78211bec0d1bfee9f4c85ae784762f042e7f31c0d40c329b8
- 02cf9dc6e85d581480d91e88e8cbeaa0c153a046e89ded08b4cefd851e1d7325b5
- 03840415b0a0fcf066bcc3dc92d8349ebd33a6ab1402ef649bae00e5d9f5840828
- 026328aae34f149853430f526ecaa9cf9c8d78a4ea82d08bdf63dd03c4d0693be6
- 02c69a8d084ee7319cfecf5161ff257aa2d1f53e79bf6c6f164cff5d94675c38b3
- 0207da870cedb777fceff948641021714ec815110ca111ccc7a54c168e065bda70
- 035056669864feea401d8c31e447fb82dd29f342a9476cfd449584ce2a6165e4d7
- 0370c75c54445565df62cfe2e76fbec4ba00d1298867972213530cae6d418da636
- 03957af9e77282ae3263544b7b2458903624adc3f5dee303957cb6570524a5f254
- 03d84d22b8753cf225d263a3a782a4e16ca72ef323cfde04977c74f14873ab1e4c
- 02147c1b1d5728e1954958daff2f88ee2fa50a06890a8a9db3fa9e972b66ae559f
- 03c609bea5a4825908027e4ab217e7efc06e311f19ecad9d417089f14927a173d5
- 0231edee3978d46c335e851c76059166eb8878516f459e085c0dd092f0f1d51c21
- 03184b018d6b2bc093e535519732b3fd3f7551c8cffaf4621dd5a0b89482ca66c9
ValidatorsCount: 7
SeedList:
- seed1t5.neo.org:20333
- seed2t5.neo.org:20333
- seed3t5.neo.org:20333
- seed4t5.neo.org:20333
- seed5t5.neo.org:20333
VerifyTransactions: false
P2PSigExtensions: false
Hardforks:
Aspidochelone: 210000
Basilisk: 2680000
ApplicationConfiguration:
SkipBlockVerification: false
DBConfiguration:
Type: "leveldb" #other options: 'inmemory','boltdb'
# DB type options. Uncomment those you need in case you want to switch DB type.
LevelDBOptions:
DataDirectoryPath: ".\\chains\\testnet"
P2P:
Addresses:
- ":20333" # in form of "[host]:[port][:announcedPort]"
DialTimeout: 3s
ProtoTickInterval: 2s
PingInterval: 30s
PingTimeout: 90s
MaxPeers: 100
AttemptConnPeers: 20
MinPeers: 10
Relay: true
Consensus:
Enabled: false
UnlockWallet:
Path: "C:\\someFolder\\cn_wallet.json"
Password: "pass"
Oracle:
Enabled: false
AllowedContentTypes:
- application/json
P2PNotary:
Enabled: false
UnlockWallet:
Path: "C:\\someFolder\\notary_wallet.json"
Password: "pass"
RPC:
Enabled: true
Addresses:
- ":20332"
MaxGasInvoke: 15
EnableCORSWorkaround: false
TLSConfig:
Enabled: false
Addresses:
- ":20331"
CertFile: serv.crt
KeyFile: serv.key
Prometheus:
Enabled: true
Addresses:
- ":2112"
Pprof:
Enabled: false
Addresses:
- ":2113"

File diff suppressed because it is too large Load diff

View file

@ -1,114 +0,0 @@
package smartcontract
import (
"fmt"
"os"
"strings"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli"
"gopkg.in/yaml.v3"
)
var generatorFlags = []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "Configuration file to use",
},
cli.StringFlag{
Name: "manifest, m",
Required: true,
Usage: "Read contract manifest (*.manifest.json) file",
},
cli.StringFlag{
Name: "out, o",
Required: true,
Usage: "Output of the compiled wrapper",
},
cli.StringFlag{
Name: "hash",
Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage",
},
}
var generateWrapperCmd = cli.Command{
Name: "generate-wrapper",
Usage: "generate wrapper to use in other contracts",
UsageText: "neo-go contract generate-wrapper --manifest <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
invocation as an optimization of the wrapper contract code. If omitted, the
generated wrapper will be designed for dynamic hash usage, allowing
the hash to be specified at runtime.
`,
Action: contractGenerateWrapper,
Flags: generatorFlags,
}
var generateRPCWrapperCmd = cli.Command{
Name: "generate-rpcwrapper",
Usage: "generate RPC wrapper to use for data reads",
UsageText: "neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
Action: contractGenerateRPCWrapper,
Flags: generatorFlags,
}
func contractGenerateWrapper(ctx *cli.Context) error {
return contractGenerateSomething(ctx, binding.Generate)
}
func contractGenerateRPCWrapper(ctx *cli.Context) error {
return contractGenerateSomething(ctx, rpcbinding.Generate)
}
// contractGenerateSomething reads generator parameters and calls the given callback.
func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error) error {
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
var (
h util.Uint160
err error
)
if hStr := ctx.String("hash"); len(hStr) != 0 {
h, err = util.Uint160DecodeStringLE(strings.TrimPrefix(hStr, "0x"))
if err != nil {
return cli.NewExitError(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)
}
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)
}
err = yaml.Unmarshal(bs, &cfg)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't parse config file: %w", err), 1)
}
}
cfg.Manifest = m
cfg.Hash = h
f, err := os.Create(ctx.String("out"))
if err != nil {
return cli.NewExitError(fmt.Errorf("can't create output file: %w", err), 1)
}
defer f.Close()
cfg.Output = f
err = cb(cfg)
if err != nil {
return cli.NewExitError(fmt.Errorf("error during generation: %w", err), 1)
}
return nil
}

View file

@ -1,712 +0,0 @@
package smartcontract
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"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) {
m := manifest.NewManifest("MyContract")
m.ABI.Methods = append(m.ABI.Methods,
manifest.Method{
Name: manifest.MethodDeploy,
ReturnType: smartcontract.VoidType,
},
manifest.Method{
Name: "sum",
Parameters: []manifest.Parameter{
manifest.NewParameter("first", smartcontract.IntegerType),
manifest.NewParameter("second", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
manifest.Method{
Name: "sum", // overloaded method
Parameters: []manifest.Parameter{
manifest.NewParameter("first", smartcontract.IntegerType),
manifest.NewParameter("second", smartcontract.IntegerType),
manifest.NewParameter("third", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
manifest.Method{
Name: "sum3",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
Safe: true,
},
manifest.Method{
Name: "zum",
Parameters: []manifest.Parameter{
manifest.NewParameter("type", smartcontract.IntegerType),
manifest.NewParameter("typev", smartcontract.IntegerType),
manifest.NewParameter("func", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
},
manifest.Method{
Name: "justExecute",
Parameters: []manifest.Parameter{
manifest.NewParameter("arr", smartcontract.ArrayType),
},
ReturnType: smartcontract.VoidType,
},
manifest.Method{
Name: "getPublicKey",
Parameters: nil,
ReturnType: smartcontract.PublicKeyType,
},
manifest.Method{
Name: "otherTypes",
Parameters: []manifest.Parameter{
manifest.NewParameter("ctr", smartcontract.Hash160Type),
manifest.NewParameter("tx", smartcontract.Hash256Type),
manifest.NewParameter("sig", smartcontract.SignatureType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.BoolType,
},
manifest.Method{
Name: "searchStorage",
Parameters: []manifest.Parameter{
manifest.NewParameter("ctx", smartcontract.InteropInterfaceType),
},
ReturnType: smartcontract.InteropInterfaceType,
},
manifest.Method{
Name: "getFromMap",
Parameters: []manifest.Parameter{
manifest.NewParameter("intMap", smartcontract.MapType),
manifest.NewParameter("indices", smartcontract.ArrayType),
},
ReturnType: smartcontract.ArrayType,
},
manifest.Method{
Name: "doSomething",
Parameters: []manifest.Parameter{
manifest.NewParameter("bytes", smartcontract.ByteArrayType),
manifest.NewParameter("str", smartcontract.StringType),
},
ReturnType: smartcontract.InteropInterfaceType,
},
manifest.Method{
Name: "getBlockWrapper",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.InteropInterfaceType,
},
manifest.Method{
Name: "myFunc",
Parameters: []manifest.Parameter{
manifest.NewParameter("in", smartcontract.MapType),
},
ReturnType: smartcontract.ArrayType,
})
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
outFile := filepath.Join(t.TempDir(), "out.go")
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
h := util.Uint160{
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd}
rawCfg := `package: wrapper
hash: ` + h.StringLE() + `
overrides:
searchStorage.ctx: storage.Context
searchStorage: iterator.Iterator
getFromMap.intMap: "map[string]int"
getFromMap.indices: "[]string"
getFromMap: "[]int"
getBlockWrapper: ledger.Block
myFunc.in: "map[int]github.com/heyitsme/mycontract.Input"
myFunc: "[]github.com/heyitsme/mycontract.Output"
callflags:
doSomething: ReadStates
`
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
require.NoError(t, app.Run([]string{"", "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.
// Package wrapper contains wrappers for MyContract contract.
package wrapper
import (
"github.com/heyitsme/mycontract"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Hash contains contract hash in big-endian form.
const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04"
// Sum invokes ` + "`sum`" + ` method of contract.
func Sum(first int, second int) int {
return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second).(int)
}
// Sum2 invokes ` + "`sum`" + ` method of contract.
func Sum2(first int, second int, third int) int {
return neogointernal.CallWithToken(Hash, "sum", int(contract.All), first, second, third).(int)
}
// Sum3 invokes ` + "`sum3`" + ` method of contract.
func Sum3() int {
return neogointernal.CallWithToken(Hash, "sum3", int(contract.ReadOnly)).(int)
}
// Zum invokes ` + "`zum`" + ` method of contract.
func Zum(typev int, typev_ int, funcv int) int {
return neogointernal.CallWithToken(Hash, "zum", int(contract.All), typev, typev_, funcv).(int)
}
// JustExecute invokes ` + "`justExecute`" + ` method of contract.
func JustExecute(arr []any) {
neogointernal.CallWithTokenNoRet(Hash, "justExecute", int(contract.All), arr)
}
// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract.
func GetPublicKey() interop.PublicKey {
return neogointernal.CallWithToken(Hash, "getPublicKey", int(contract.All)).(interop.PublicKey)
}
// OtherTypes invokes ` + "`otherTypes`" + ` method of contract.
func OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data any) bool {
return neogointernal.CallWithToken(Hash, "otherTypes", int(contract.All), ctr, tx, sig, data).(bool)
}
// SearchStorage invokes ` + "`searchStorage`" + ` method of contract.
func SearchStorage(ctx storage.Context) iterator.Iterator {
return neogointernal.CallWithToken(Hash, "searchStorage", int(contract.All), ctx).(iterator.Iterator)
}
// GetFromMap invokes ` + "`getFromMap`" + ` method of contract.
func GetFromMap(intMap map[string]int, indices []string) []int {
return neogointernal.CallWithToken(Hash, "getFromMap", int(contract.All), intMap, indices).([]int)
}
// DoSomething invokes ` + "`doSomething`" + ` method of contract.
func DoSomething(bytes []byte, str string) any {
return neogointernal.CallWithToken(Hash, "doSomething", int(contract.ReadStates), bytes, str).(any)
}
// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract.
func GetBlockWrapper() ledger.Block {
return neogointernal.CallWithToken(Hash, "getBlockWrapper", int(contract.All)).(ledger.Block)
}
// MyFunc invokes ` + "`myFunc`" + ` method of contract.
func MyFunc(in map[int]mycontract.Input) []mycontract.Output {
return neogointernal.CallWithToken(Hash, "myFunc", int(contract.All), in).([]mycontract.Output)
}
`
data, err := os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, expected, string(data))
require.NoError(t, app.Run([]string{"", "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.
package wrapper
import (
"github.com/heyitsme/mycontract"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Contract represents the MyContract smart contract.
type Contract struct {
Hash interop.Hash160
}
// NewContract returns a new Contract instance with the specified hash.
func NewContract(hash interop.Hash160) Contract {
return Contract{Hash: hash}
}
// Sum invokes ` + "`sum`" + ` method of contract.
func (c Contract) Sum(first int, second int) int {
return contract.Call(c.Hash, "sum", contract.All, first, second).(int)
}
// Sum2 invokes ` + "`sum`" + ` method of contract.
func (c Contract) Sum2(first int, second int, third int) int {
return contract.Call(c.Hash, "sum", contract.All, first, second, third).(int)
}
// Sum3 invokes ` + "`sum3`" + ` method of contract.
func (c Contract) Sum3() int {
return contract.Call(c.Hash, "sum3", contract.ReadOnly).(int)
}
// Zum invokes ` + "`zum`" + ` method of contract.
func (c Contract) Zum(typev int, typev_ int, funcv int) int {
return contract.Call(c.Hash, "zum", contract.All, typev, typev_, funcv).(int)
}
// JustExecute invokes ` + "`justExecute`" + ` method of contract.
func (c Contract) JustExecute(arr []any) {
contract.Call(c.Hash, "justExecute", contract.All, arr)
}
// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract.
func (c Contract) GetPublicKey() interop.PublicKey {
return contract.Call(c.Hash, "getPublicKey", contract.All).(interop.PublicKey)
}
// OtherTypes invokes ` + "`otherTypes`" + ` method of contract.
func (c Contract) OtherTypes(ctr interop.Hash160, tx interop.Hash256, sig interop.Signature, data any) bool {
return contract.Call(c.Hash, "otherTypes", contract.All, ctr, tx, sig, data).(bool)
}
// SearchStorage invokes ` + "`searchStorage`" + ` method of contract.
func (c Contract) SearchStorage(ctx storage.Context) iterator.Iterator {
return contract.Call(c.Hash, "searchStorage", contract.All, ctx).(iterator.Iterator)
}
// GetFromMap invokes ` + "`getFromMap`" + ` method of contract.
func (c Contract) GetFromMap(intMap map[string]int, indices []string) []int {
return contract.Call(c.Hash, "getFromMap", contract.All, intMap, indices).([]int)
}
// DoSomething invokes ` + "`doSomething`" + ` method of contract.
func (c Contract) DoSomething(bytes []byte, str string) any {
return contract.Call(c.Hash, "doSomething", contract.ReadStates, bytes, str).(any)
}
// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract.
func (c Contract) GetBlockWrapper() ledger.Block {
return contract.Call(c.Hash, "getBlockWrapper", contract.All).(ledger.Block)
}
// MyFunc invokes ` + "`myFunc`" + ` method of contract.
func (c Contract) MyFunc(in map[int]mycontract.Input) []mycontract.Output {
return contract.Call(c.Hash, "myFunc", contract.All, in).([]mycontract.Output)
}
`
data, err = os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, expectedWithDynamicHash, string(data))
}
func TestGenerateValidPackageName(t *testing.T) {
m := manifest.NewManifest("My space\tcontract")
m.ABI.Methods = append(m.ABI.Methods,
manifest.Method{
Name: "get",
Parameters: []manifest.Parameter{},
ReturnType: smartcontract.IntegerType,
Safe: true,
},
)
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
outFile := filepath.Join(t.TempDir(), "out.go")
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
h := util.Uint160{
0x04, 0x08, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x00, 0x01,
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
}
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
require.NoError(t, app.Run([]string{"", "generate-wrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
data, err := os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, `// Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package myspacecontract contains wrappers for My space contract contract.
package myspacecontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
)
// Hash contains contract hash in big-endian form.
const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04"
// Get invokes `+"`get`"+` method of contract.
func Get() int {
return neogointernal.CallWithToken(Hash, "get", int(contract.ReadOnly)).(int)
}
`, string(data))
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
"--manifest", manifestFile,
"--out", outFile,
"--hash", "0x" + h.StringLE(),
}))
data, err = os.ReadFile(outFile)
require.NoError(t, err)
require.Equal(t, `// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package myspacecontract contains RPC wrappers for My space contract contract.
package myspacecontract
import (
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x0, 0x1, 0xca, 0xfe, 0xba, 0xbe, 0xde, 0xad, 0xbe, 0xef, 0x3, 0x4}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{invoker, hash}
}
// Get invokes `+"`get`"+` method of contract.
func (c *ContractReader) Get() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "get"))
}
`, string(data))
}
// rewriteExpectedOutputs denotes whether expected output files should be rewritten
// for TestGenerateRPCBindings and TestAssistedRPCBindings.
const rewriteExpectedOutputs = false
func TestGenerateRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
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",
"--manifest", manifest,
"--out", outFile,
"--hash", hash,
}))
data, err := os.ReadFile(outFile)
require.NoError(t, err)
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
if rewriteExpectedOutputs {
require.NoError(t, os.WriteFile(good, data, os.ModePerm))
} else {
expected, err := os.ReadFile(good)
require.NoError(t, err)
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
require.Equal(t, string(expected), string(data))
}
})
}
checkBinding(filepath.Join("testdata", "nex", "nex.manifest.json"),
"0xa2a67f09e8cf22c6bfd5cea24adc0f4bf0a11aa8",
filepath.Join("testdata", "nex", "nex.go"))
checkBinding(filepath.Join("testdata", "nameservice", "nns.manifest.json"),
"0x50ac1c37690cc2cfc594472833cf57505d5f46de",
filepath.Join("testdata", "nameservice", "nns.go"))
checkBinding(filepath.Join("testdata", "gas", "gas.manifest.json"),
"0xd2a4cff31913016155e38e474a2c06d08be276cf",
filepath.Join("testdata", "gas", "gas.go"))
checkBinding(filepath.Join("testdata", "verifyrpc", "verify.manifest.json"),
"0x00112233445566778899aabbccddeeff00112233",
filepath.Join("testdata", "verifyrpc", "verify.go"))
checkBinding(filepath.Join("testdata", "nonepiter", "iter.manifest.json"),
"0x00112233445566778899aabbccddeeff00112233",
filepath.Join("testdata", "nonepiter", "iter.go"))
require.False(t, rewriteExpectedOutputs)
}
func TestAssistedRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
app := cli.NewApp()
app.Commands = NewCommands()
var checkBinding = func(source string, hasDefinedHash bool, guessEventTypes bool, suffix ...string) {
testName := source
if len(suffix) != 0 {
testName += suffix[0]
}
testName += fmt.Sprintf(", predefined hash: %t", hasDefinedHash)
t.Run(testName, func(t *testing.T) {
outFile := filepath.Join(tmpDir, "out.go")
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")
}
manifestF := filepath.Join(tmpDir, "manifest.json")
bindingF := filepath.Join(tmpDir, "binding.yml")
nefF := filepath.Join(tmpDir, "out.nef")
cmd := []string{"", "contract", "compile",
"--in", source,
"--config", configFile,
"--manifest", manifestF,
"--bindings", bindingF,
"--out", nefF,
}
if guessEventTypes {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(cmd))
cmds := []string{"", "contract", "generate-rpcwrapper",
"--config", bindingF,
"--manifest", manifestF,
"--out", outFile,
}
if hasDefinedHash {
cmds = append(cmds, "--hash", "0x00112233445566778899aabbccddeeff00112233")
}
require.NoError(t, app.Run(cmds))
data, err := os.ReadFile(outFile)
require.NoError(t, err)
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
if rewriteExpectedOutputs {
require.NoError(t, os.WriteFile(expectedFile, data, os.ModePerm))
} else {
expected, err := os.ReadFile(expectedFile)
require.NoError(t, err)
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
require.Equal(t, string(expected), string(data))
}
})
}
for _, hasDefinedHash := range []bool{true, false} {
checkBinding(filepath.Join("testdata", "rpcbindings", "types"), hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "structs"), hasDefinedHash, false)
}
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false, "_extended")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, true, "_guessed")
require.False(t, rewriteExpectedOutputs)
}
func TestGenerate_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd}
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(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")
})
t.Run("missing manifest argument", func(t *testing.T) {
checkError(t, "Required flag \"manifest\" not set", "--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")
})
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")
})
t.Run("invalid manifest", func(t *testing.T) {
manifestFile := filepath.Join(t.TempDir(), "invalid.json")
m := manifest.NewManifest("MyContract") // no methods
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
checkError(t, "ABI: no methods", "--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(), "--out", "zzz")
})
manifestFile := filepath.Join(t.TempDir(), "manifest.json")
m := manifest.NewManifest("MyContract")
m.ABI.Methods = append(m.ABI.Methods, manifest.Method{
Name: "method0",
Offset: 0,
ReturnType: smartcontract.AnyType,
Safe: true,
})
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
require.NoError(t, os.WriteFile(manifestFile, rawManifest, os.ModePerm))
t.Run("missing config", func(t *testing.T) {
checkError(t, "can't read config file",
"--manifest", manifestFile, "--hash", util.Uint160{}.StringLE(),
"--config", filepath.Join(t.TempDir(), "not.exists.yml"), "--out", "zzz")
})
t.Run("invalid config", func(t *testing.T) {
rawCfg := `package: wrapper
callflags:
someFunc: ReadSometimes
`
cfgPath := filepath.Join(t.TempDir(), "binding.yml")
require.NoError(t, os.WriteFile(cfgPath, []byte(rawCfg), os.ModePerm))
checkError(t, "can't parse config file",
"--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)
}
check := func(t *testing.T, source string, expectedErrText string) {
tmpDir := t.TempDir()
configFile := filepath.Join(source, "invalid.yml")
manifestF := filepath.Join(tmpDir, "invalid.manifest.json")
bindingF := filepath.Join(tmpDir, "invalid.binding.yml")
nefF := filepath.Join(tmpDir, "invalid.out.nef")
cmd := []string{"", "contract", "compile",
"--in", source,
"--config", configFile,
"--manifest", manifestF,
"--bindings", bindingF,
"--out", nefF,
"--guess-eventtypes",
}
checkError(t, expectedErrText, cmd...)
}
t.Run("not declared in manifest", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid1"), "inconsistent usages of event `Non declared event`: not declared in the contract config")
})
t.Run("invalid number of params", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid2"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1")
})
/*
// TODO: this on is a controversial one. If event information is provided in the config file, then conversion code
// will be emitted by the compiler according to the parameter type provided via config. Thus, we can be sure that
// either event parameter has the type specified in the config file or the execution of the contract will fail.
// Thus, this testcase is always failing (no compilation error occures).
// Question: do we want to compare `RealType` of the emitted parameter with the one expected in the manifest?
t.Run("SC parameter type mismatch", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid3"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1")
})
*/
t.Run("extended types mismatch", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid4"), "inconsistent usages of event `SomeEvent`: extended type of param #0 mismatch")
})
t.Run("named types redeclare", func(t *testing.T) {
check(t, filepath.Join("testdata", "rpcbindings", "invalid5"), "configured declared named type intersects with the contract's one: `invalid5.NamedStruct`")
})
}
func TestGenerateRPCBindings_Errors(t *testing.T) {
app := cli.NewApp()
app.Commands = NewCommands()
app.ExitErrHandler = func(*cli.Context, error) {}
t.Run("duplicating resulting fields", func(t *testing.T) {
check := func(t *testing.T, packageName string, autogen bool, expectedError string) {
tmpDir := t.TempDir()
source := filepath.Join("testdata", "rpcbindings", packageName)
configFile := filepath.Join(source, "invalid.yml")
out := filepath.Join(tmpDir, "rpcbindings.out")
manifestF := filepath.Join(tmpDir, "manifest.json")
bindingF := filepath.Join(tmpDir, "binding.yml")
nefF := filepath.Join(tmpDir, "out.nef")
cmd := []string{"", "contract", "compile",
"--in", source,
"--config", configFile,
"--manifest", manifestF,
"--bindings", bindingF,
"--out", nefF,
}
if autogen {
cmd = append(cmd, "--guess-eventtypes")
}
require.NoError(t, app.Run(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())
}
t.Run("event", func(t *testing.T) {
check(t, "invalid6", false, "error during generation: named type `SomeStruct` has two fields with identical resulting binding name `Field`")
})
t.Run("autogen event", func(t *testing.T) {
check(t, "invalid7", true, "error during generation: named type `invalid7.SomeStruct` has two fields with identical resulting binding name `Field`")
})
t.Run("struct", func(t *testing.T) {
check(t, "invalid8", false, "error during generation: named type `invalid8.SomeStruct` has two fields with identical resulting binding name `Field`")
})
})
}

View file

@ -1,117 +0,0 @@
package smartcontract
import (
"encoding/json"
"errors"
"fmt"
"os"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli"
)
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)
}
nf, _, err := readNEFFile(ctx.String("nef"))
if err != nil {
return cli.NewExitError(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)
}
h := state.CreateContractHash(sender, 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)
}
defer w.Close()
var found bool
sig := gAcc.PrivateKey().Sign(h.BytesBE())
pub := gAcc.PublicKey()
for i := range m.Groups {
if m.Groups[i].PublicKey.Equal(pub) {
m.Groups[i].Signature = sig
found = true
break
}
}
if !found {
m.Groups = append(m.Groups, manifest.Group{
PublicKey: pub,
Signature: sig,
})
}
rawM, err := json.Marshal(m)
if err != nil {
return cli.NewExitError(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 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
}
nefFile, err := nef.FileFromBytes(f)
if err != nil {
return nil, nil, fmt.Errorf("can't parse NEF file: %w", err)
}
return &nefFile, f, nil
}
// readManifest unmarshalls manifest got from the provided filename and checks
// it for validness against the provided contract hash. If empty hash is specified
// then no hash-related manifest groups check is performed.
func readManifest(filename string, hash util.Uint160) (*manifest.Manifest, []byte, error) {
if len(filename) == 0 {
return nil, nil, errNoManifestFile
}
manifestBytes, err := os.ReadFile(filename)
if err != nil {
return nil, nil, err
}
m := new(manifest.Manifest)
err = json.Unmarshal(manifestBytes, m)
if err != nil {
return nil, nil, err
}
if err := m.IsValid(hash, true); err != nil {
return nil, nil, fmt.Errorf("manifest is invalid: %w", err)
}
return m, manifestBytes, nil
}

View file

@ -1,130 +0,0 @@
package smartcontract
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"gopkg.in/yaml.v3"
)
type permission manifest.Permission
const (
permHashKey = "hash"
permGroupKey = "group"
permMethodKey = "methods"
)
func (p permission) MarshalYAML() (any, error) {
m := yaml.Node{Kind: yaml.MappingNode}
switch p.Contract.Type {
case manifest.PermissionWildcard:
case manifest.PermissionHash:
m.Content = append(m.Content,
&yaml.Node{Kind: yaml.ScalarNode, Value: permHashKey},
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(util.Uint160).StringLE()})
case manifest.PermissionGroup:
m.Content = append(m.Content,
&yaml.Node{Kind: yaml.ScalarNode, Value: permGroupKey},
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(*keys.PublicKey).StringCompressed()})
default:
return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type)
}
var val any = "*"
if !p.Methods.IsWildcard() {
val = p.Methods.Value
}
n := &yaml.Node{Kind: yaml.ScalarNode}
err := n.Encode(val)
if err != nil {
return nil, err
}
m.Content = append(m.Content,
&yaml.Node{Kind: yaml.ScalarNode, Value: permMethodKey},
n)
return m, nil
}
func (p *permission) UnmarshalYAML(unmarshal func(any) error) error {
var m map[string]any
if err := unmarshal(&m); err != nil {
return err
}
if err := p.fillType(m); err != nil {
return err
}
return p.fillMethods(m)
}
func (p *permission) fillType(m map[string]any) error {
vh, ok1 := m[permHashKey]
vg, ok2 := m[permGroupKey]
switch {
case ok1 && ok2:
return errors.New("permission must have either 'hash' or 'group' field")
case ok1:
s, ok := vh.(string)
if !ok {
return errors.New("invalid 'hash' type")
}
u, err := util.Uint160DecodeStringLE(s)
if err != nil {
return err
}
p.Contract.Type = manifest.PermissionHash
p.Contract.Value = u
case ok2:
s, ok := vg.(string)
if !ok {
return errors.New("invalid 'hash' type")
}
pub, err := keys.NewPublicKeyFromString(s)
if err != nil {
return err
}
p.Contract.Type = manifest.PermissionGroup
p.Contract.Value = pub
default:
p.Contract.Type = manifest.PermissionWildcard
}
return nil
}
func (p *permission) fillMethods(m map[string]any) error {
methods, ok := m[permMethodKey]
if !ok {
return errors.New("'methods' field is missing from permission")
}
switch mt := methods.(type) {
case string:
if mt == "*" {
p.Methods.Value = nil
return nil
}
case []any:
ms := make([]string, len(mt))
for i := range mt {
ms[i], ok = mt[i].(string)
if !ok {
return errors.New("invalid permission method name")
}
}
p.Methods.Value = ms
return nil
default:
}
return errors.New("'methods' field is invalid")
}

File diff suppressed because it is too large Load diff

View file

@ -1,138 +0,0 @@
package smartcontract
import (
"flag"
"os"
"testing"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"gopkg.in/yaml.v3"
)
func TestInitSmartContract(t *testing.T) {
d := t.TempDir()
testWD, err := os.Getwd()
require.NoError(t, err)
err = os.Chdir(d)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(testWD)) })
contractName := "testContract"
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("name", contractName, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.NoError(t, initSmartContract(ctx))
dirInfo, err := os.Stat(contractName)
require.NoError(t, err)
require.True(t, dirInfo.IsDir())
files, err := os.ReadDir(contractName)
require.NoError(t, err)
require.Equal(t, 3, len(files))
require.Equal(t, "go.mod", files[0].Name())
require.Equal(t, "main.go", files[1].Name())
require.Equal(t, "neo-go.yml", files[2].Name())
main, err := os.ReadFile(contractName + "/" + files[1].Name())
require.NoError(t, err)
require.Equal(t,
`package `+contractName+`
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
var notificationName string
// init initializes notificationName before calling any other smart-contract method
func init() {
notificationName = "Hello world!"
}
// RuntimeNotify sends runtime notification with "Hello world!" name
func RuntimeNotify(args []any) {
runtime.Notify(notificationName, args)
}`, string(main))
manifest, err := os.ReadFile(contractName + "/" + files[2].Name())
require.NoError(t, err)
expected := `name: testContract
sourceurl: http://example.com/
safemethods: []
supportedstandards: []
events:
- name: Hello world!
parameters:
- name: args
type: Array
permissions:
- methods: '*'
`
require.Equal(t, expected, string(manifest))
}
func testPermissionMarshal(t *testing.T, p *manifest.Permission, expected string) {
out, err := yaml.Marshal((*permission)(p))
require.NoError(t, err)
require.Equal(t, expected, string(out))
t.Run("Unmarshal", func(t *testing.T) {
actual := new(permission)
require.NoError(t, yaml.Unmarshal(out, actual))
require.Equal(t, p, (*manifest.Permission)(actual))
})
}
func TestPermissionMarshal(t *testing.T) {
t.Run("Wildcard", func(t *testing.T) {
p := manifest.NewPermission(manifest.PermissionWildcard)
testPermissionMarshal(t, p, "methods: '*'\n")
})
t.Run("no allowed methods", func(t *testing.T) {
p := manifest.NewPermission(manifest.PermissionWildcard)
p.Methods.Restrict()
testPermissionMarshal(t, p, "methods: []\n")
})
t.Run("hash", func(t *testing.T) {
h := random.Uint160()
p := manifest.NewPermission(manifest.PermissionHash, h)
testPermissionMarshal(t, p,
"hash: "+h.StringLE()+"\n"+
"methods: '*'\n")
})
t.Run("group with some methods", func(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
p := manifest.NewPermission(manifest.PermissionGroup, priv.PublicKey())
p.Methods.Add("abc")
p.Methods.Add("lamao")
testPermissionMarshal(t, p,
"group: "+priv.PublicKey().StringCompressed()+"\n"+
"methods:\n - abc\n - lamao\n")
})
}
func TestPermissionUnmarshalInvalid(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey().StringCompressed()
u160 := random.Uint160().StringLE()
testCases := []string{
"hash: []\nmethods: '*'\n", // invalid hash type
"hash: notahex\nmethods: '*'\n", // invalid hash
"group: []\nmethods: '*'\n", // invalid group type
"group: notahex\nmethods: '*'\n", // invalid group
"hash: " + u160 + "\n", // missing methods
"group: " + pub + "\nhash: " + u160 + "\nmethods: '*'", // hash/group conflict
"hash: " + u160 + "\nmethods:\n a: b\n", // invalid methods type
"hash: " + u160 + "\nmethods:\n- []\n", // methods array, invalid single
}
for _, tc := range testCases {
t.Run(tc, func(t *testing.T) {
require.Error(t, yaml.Unmarshal([]byte(tc), new(permission)))
})
}
}

View file

@ -1,84 +0,0 @@
package deploy
import (
"github.com/nspcc-dev/neo-go/cli/smartcontract/testdata/deploy/sub"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
var key = "key"
const mgmtKey = "mgmt"
func _deploy(data any, isUpdate bool) {
var value string
ctx := storage.GetContext()
if isUpdate {
value = "on update"
} else {
value = "on create"
sh := runtime.GetCallingScriptHash()
storage.Put(ctx, mgmtKey, sh)
if data != nil {
arr := data.([]any)
for i := 0; i < len(arr)-1; i += 2 {
storage.Put(ctx, arr[i], arr[i+1])
}
}
}
storage.Put(ctx, key, value)
}
// Fail just fails.
func Fail() {
panic("as expected")
}
// CheckSenderWitness checks sender's witness.
func CheckSenderWitness() {
tx := runtime.GetScriptContainer()
if !runtime.CheckWitness(tx.Sender) {
panic("not witnessed")
}
}
// Update updates the contract with a new one.
func Update(script, manifest []byte) {
ctx := storage.GetReadOnlyContext()
mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160)
contract.Call(mgmt, "update", contract.All, script, manifest)
}
// GetValue returns the stored value.
func GetValue() string {
ctx := storage.GetReadOnlyContext()
val1 := storage.Get(ctx, key)
val2 := storage.Get(ctx, sub.Key)
return val1.(string) + "|" + val2.(string)
}
// GetValueWithKey returns the stored value with the specified key.
func GetValueWithKey(key string) string {
ctx := storage.GetReadOnlyContext()
return storage.Get(ctx, key).(string)
}
// TestFind finds items with the specified prefix.
func TestFind(f storage.FindFlags) []any {
ctx := storage.GetContext()
storage.Put(ctx, "findkey1", "value1")
storage.Put(ctx, "findkey2", "value2")
var result []any
iter := storage.Find(ctx, "findkey", f)
for iterator.Next(iter) {
result = append(result, iterator.Value(iter))
}
return result
}

View file

@ -1,4 +0,0 @@
name: Test deploy
permissions:
- hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
methods: ["update"]

View file

@ -1,14 +0,0 @@
package sub
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
var Key = "sub"
func _deploy(data any, isUpdate bool) {
ctx := storage.GetContext()
value := "sub create"
if isUpdate {
value = "sub update"
}
storage.Put(ctx, Key, value)
}

View file

@ -1,6 +0,0 @@
package deploy
// NewMethod in updated contract.
func NewMethod() int {
return 42
}

View file

@ -1,52 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package gastoken contains RPC wrappers for GasToken contract.
package gastoken
import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Hash contains contract hash.
var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep17.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep17.Actor
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep17.TokenReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep17.TokenWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep17t = nep17.New(actor, hash)
return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash}
}

View file

@ -1 +0,0 @@
{"name":"GasToken","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"symbol","parameters":[],"returntype":"String","offset":14,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":21,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":28,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}

View file

@ -1,21 +0,0 @@
// invalid is an example of a contract which doesn't pass event check.
package invalid1
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
// Notify1 emits a correctly typed event.
func Notify1() bool {
runtime.Notify("Event", interop.Hash160{1, 2, 3})
return true
}
// Notify2 emits an invalid event (ByteString instead of Hash160).
func Notify2() bool {
runtime.Notify("Event", []byte{1, 2, 3})
return true
}

View file

@ -1,7 +0,0 @@
name: "Invalid example"
supportedstandards: []
events:
- name: Event
parameters:
- name: address
type: Hash160

View file

@ -1,21 +0,0 @@
// invalid is an example of a contract which doesn't pass event check.
package invalid2
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
// Notify1 emits a correctly typed event.
func Notify1() bool {
runtime.Notify("Event", interop.Hash160{1, 2, 3})
return true
}
// Notify2 emits an invalid event (extra parameter).
func Notify2() bool {
runtime.Notify("Event", interop.Hash160{1, 2, 3}, "extra parameter")
return true
}

View file

@ -1,7 +0,0 @@
name: "Invalid example"
supportedstandards: []
events:
- name: Event
parameters:
- name: address
type: Hash160

View file

@ -1,21 +0,0 @@
// invalid is an example of a contract which doesn't pass event check.
package invalid3
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
// Notify1 emits a correctly typed event.
func Notify1() bool {
runtime.Notify("Event", interop.Hash160{1, 2, 3})
return true
}
// Notify2 emits an invalid event (missing from manifest).
func Notify2() bool {
runtime.Notify("AnotherEvent", interop.Hash160{1, 2, 3})
return true
}

View file

@ -1,7 +0,0 @@
name: "Invalid example"
supportedstandards: []
events:
- name: Event
parameters:
- name: address
type: Hash160

View file

@ -1,5 +0,0 @@
package invalid4
func Verify() bool {
return true
}

View file

@ -1,2 +0,0 @@
name: Test bad source url
sourceurl: http://example.com/with/some/huge/path/that/cant/be/ever/normally/reached/but/we/need/to/test/for/it/anyway/because/there/is/some/path/in/the/code/that/does/this/check/and/it/should/be/triggered/to/ensure/proper/coverage/in/this/particular/component/of/our/perfect/compiler

View file

@ -1,511 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nameservice contains RPC wrappers for NameService contract.
package nameservice
import (
"errors"
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
"unicode/utf8"
)
// Hash contains contract hash.
var Hash = util.Uint160{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x47, 0x94, 0xc5, 0xcf, 0xc2, 0xc, 0x69, 0x37, 0x1c, 0xac, 0x50}
// SetAdminEvent represents "SetAdmin" event emitted by the contract.
type SetAdminEvent struct {
Name string
OldAdmin util.Uint160
NewAdmin util.Uint160
}
// RenewEvent represents "Renew" event emitted by the contract.
type RenewEvent struct {
Name string
OldExpiration *big.Int
NewExpiration *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.NonDivisibleReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.BaseWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep11ndt = nep11.NewNonDivisible(actor, hash)
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash}
}
// Roots invokes `roots` method of contract.
func (c *ContractReader) Roots() (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "roots"))
}
// RootsExpanded is similar to Roots (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) RootsExpanded(_numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "roots", _numOfIteratorItems))
}
// GetPrice invokes `getPrice` method of contract.
func (c *ContractReader) GetPrice(length *big.Int) (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice", length))
}
// IsAvailable invokes `isAvailable` method of contract.
func (c *ContractReader) IsAvailable(name string) (bool, error) {
return unwrap.Bool(c.invoker.Call(c.hash, "isAvailable", name))
}
// GetRecord invokes `getRecord` method of contract.
func (c *ContractReader) GetRecord(name string, typev *big.Int) (string, error) {
return unwrap.UTF8String(c.invoker.Call(c.hash, "getRecord", name, typev))
}
// GetAllRecords invokes `getAllRecords` method of contract.
func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name))
}
// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name))
}
// Resolve invokes `resolve` method of contract.
func (c *ContractReader) Resolve(name string, typev *big.Int) (string, error) {
return unwrap.UTF8String(c.invoker.Call(c.hash, "resolve", name, typev))
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
// AddRoot creates a transaction invoking `addRoot` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) AddRoot(root string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "addRoot", root)
}
// AddRootTransaction creates a transaction invoking `addRoot` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) AddRootTransaction(root string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "addRoot", root)
}
// AddRootUnsigned creates a transaction invoking `addRoot` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) AddRootUnsigned(root string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "addRoot", nil, root)
}
// SetPrice creates a transaction invoking `setPrice` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetPrice(priceList []any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setPrice", priceList)
}
// SetPriceTransaction creates a transaction invoking `setPrice` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetPriceTransaction(priceList []any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setPrice", priceList)
}
// SetPriceUnsigned creates a transaction invoking `setPrice` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetPriceUnsigned(priceList []any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setPrice", nil, priceList)
}
func (c *Contract) scriptForRegister(name string, owner util.Uint160) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner)
}
// Register creates a transaction invoking `register` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Register(name string, owner util.Uint160) (util.Uint256, uint32, error) {
script, err := c.scriptForRegister(name, owner)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// RegisterTransaction creates a transaction invoking `register` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RegisterTransaction(name string, owner util.Uint160) (*transaction.Transaction, error) {
script, err := c.scriptForRegister(name, owner)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// RegisterUnsigned creates a transaction invoking `register` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RegisterUnsigned(name string, owner util.Uint160) (*transaction.Transaction, error) {
script, err := c.scriptForRegister(name, owner)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// Renew creates a transaction invoking `renew` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Renew(name string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "renew", name)
}
// RenewTransaction creates a transaction invoking `renew` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RenewTransaction(name string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "renew", name)
}
// RenewUnsigned creates a transaction invoking `renew` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RenewUnsigned(name string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name)
}
// Renew2 creates a transaction invoking `renew` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Renew2(name string, years *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "renew", name, years)
}
// Renew2Transaction creates a transaction invoking `renew` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) Renew2Transaction(name string, years *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "renew", name, years)
}
// Renew2Unsigned creates a transaction invoking `renew` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) Renew2Unsigned(name string, years *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name, years)
}
// SetAdmin creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetAdmin(name string, admin util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setAdmin", name, admin)
}
// SetAdminTransaction creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetAdminTransaction(name string, admin util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setAdmin", name, admin)
}
// SetAdminUnsigned creates a transaction invoking `setAdmin` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetAdminUnsigned(name string, admin util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, name, admin)
}
// SetRecord creates a transaction invoking `setRecord` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetRecord(name string, typev *big.Int, data string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setRecord", name, typev, data)
}
// SetRecordTransaction creates a transaction invoking `setRecord` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetRecordTransaction(name string, typev *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setRecord", name, typev, data)
}
// SetRecordUnsigned creates a transaction invoking `setRecord` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetRecordUnsigned(name string, typev *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setRecord", nil, name, typev, data)
}
// DeleteRecord creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) DeleteRecord(name string, typev *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "deleteRecord", name, typev)
}
// DeleteRecordTransaction creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DeleteRecordTransaction(name string, typev *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "deleteRecord", name, typev)
}
// DeleteRecordUnsigned creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "deleteRecord", nil, name, typev)
}
// SetAdminEventsFromApplicationLog retrieves a set of all emitted events
// with "SetAdmin" name from the provided [result.ApplicationLog].
func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SetAdminEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SetAdmin" {
continue
}
event := new(SetAdminEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SetAdminEvent or
// returns an error if it's not possible to do to so.
func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 3 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.OldAdmin, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field OldAdmin: %w", err)
}
index++
e.NewAdmin, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field NewAdmin: %w", err)
}
return nil
}
// RenewEventsFromApplicationLog retrieves a set of all emitted events
// with "Renew" name from the provided [result.ApplicationLog].
func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*RenewEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Renew" {
continue
}
event := new(RenewEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to RenewEvent or
// returns an error if it's not possible to do to so.
func (e *RenewEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 3 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.OldExpiration, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field OldExpiration: %w", err)
}
index++
e.NewExpiration, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field NewExpiration: %w", err)
}
return nil
}

View file

@ -1,441 +0,0 @@
{
"abi" : {
"events" : [
{
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"name" : "amount",
"type" : "Integer"
},
{
"type" : "ByteArray",
"name" : "tokenId"
}
],
"name" : "Transfer"
},
{
"parameters" : [
{
"type" : "String",
"name" : "name"
},
{
"type" : "Hash160",
"name" : "oldAdmin"
},
{
"type" : "Hash160",
"name" : "newAdmin"
}
],
"name" : "SetAdmin"
},
{
"name" : "Renew",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "oldExpiration"
},
{
"name" : "newExpiration",
"type" : "Integer"
}
]
}
],
"methods" : [
{
"safe" : true,
"parameters" : [],
"name" : "symbol",
"returntype" : "String",
"offset" : 0
},
{
"parameters" : [],
"name" : "decimals",
"returntype" : "Integer",
"safe" : true,
"offset" : 6
},
{
"offset" : 8,
"returntype" : "Integer",
"name" : "totalSupply",
"parameters" : [],
"safe" : true
},
{
"offset" : 53,
"safe" : true,
"parameters" : [
{
"type" : "ByteArray",
"name" : "tokenId"
}
],
"name" : "ownerOf",
"returntype" : "Hash160"
},
{
"safe" : true,
"name" : "properties",
"returntype" : "Map",
"parameters" : [
{
"type" : "ByteArray",
"name" : "tokenId"
}
],
"offset" : 184
},
{
"safe" : true,
"returntype" : "Integer",
"name" : "balanceOf",
"parameters" : [
{
"name" : "owner",
"type" : "Hash160"
}
],
"offset" : 341
},
{
"safe" : true,
"returntype" : "InteropInterface",
"name" : "tokens",
"parameters" : [],
"offset" : 453
},
{
"safe" : true,
"name" : "tokensOf",
"returntype" : "InteropInterface",
"parameters" : [
{
"name" : "owner",
"type" : "Hash160"
}
],
"offset" : 494
},
{
"offset" : 600,
"safe" : false,
"parameters" : [
{
"type" : "Hash160",
"name" : "to"
},
{
"name" : "tokenId",
"type" : "ByteArray"
},
{
"name" : "data",
"type" : "Any"
}
],
"name" : "transfer",
"returntype" : "Boolean"
},
{
"name" : "update",
"returntype" : "Void",
"parameters" : [
{
"type" : "ByteArray",
"name" : "nef"
},
{
"name" : "manifest",
"type" : "String"
}
],
"safe" : false,
"offset" : 1121
},
{
"offset" : 1291,
"returntype" : "Void",
"name" : "addRoot",
"parameters" : [
{
"name" : "root",
"type" : "String"
}
],
"safe" : false
},
{
"offset" : 1725,
"safe" : true,
"parameters" : [],
"returntype" : "InteropInterface",
"name" : "roots"
},
{
"offset" : 1757,
"parameters" : [
{
"type" : "Array",
"name" : "priceList"
}
],
"name" : "setPrice",
"returntype" : "Void",
"safe" : false
},
{
"offset" : 1952,
"safe" : true,
"parameters" : [
{
"name" : "length",
"type" : "Integer"
}
],
"name" : "getPrice",
"returntype" : "Integer"
},
{
"offset" : 2017,
"safe" : true,
"parameters" : [
{
"type" : "String",
"name" : "name"
}
],
"name" : "isAvailable",
"returntype" : "Boolean"
},
{
"offset" : 2405,
"parameters" : [
{
"type" : "String",
"name" : "name"
},
{
"type" : "Hash160",
"name" : "owner"
}
],
"name" : "register",
"returntype" : "Boolean",
"safe" : false
},
{
"name" : "renew",
"returntype" : "Integer",
"parameters" : [
{
"type" : "String",
"name" : "name"
}
],
"safe" : false,
"offset" : 3113
},
{
"offset" : 3123,
"safe" : false,
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"name" : "years",
"type" : "Integer"
}
],
"name" : "renew",
"returntype" : "Integer"
},
{
"offset" : 3697,
"parameters" : [
{
"type" : "String",
"name" : "name"
},
{
"name" : "admin",
"type" : "Hash160"
}
],
"name" : "setAdmin",
"returntype" : "Void",
"safe" : false
},
{
"safe" : false,
"returntype" : "Void",
"name" : "setRecord",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
},
{
"name" : "data",
"type" : "String"
}
],
"offset" : 3921
},
{
"name" : "getRecord",
"returntype" : "String",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
}
],
"safe" : true,
"offset" : 5877
},
{
"returntype" : "InteropInterface",
"name" : "getAllRecords",
"parameters" : [
{
"name" : "name",
"type" : "String"
}
],
"safe" : true,
"offset" : 6201
},
{
"safe" : false,
"returntype" : "Void",
"name" : "deleteRecord",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
}
],
"offset" : 6281
},
{
"offset" : 6565,
"name" : "resolve",
"returntype" : "String",
"parameters" : [
{
"name" : "name",
"type" : "String"
},
{
"type" : "Integer",
"name" : "type"
}
],
"safe" : true
},
{
"safe" : false,
"parameters" : [
{
"name" : "data",
"type" : "Any"
},
{
"name" : "update",
"type" : "Boolean"
}
],
"name" : "_deploy",
"returntype" : "Void",
"offset" : 7152
},
{
"offset" : 7514,
"parameters" : [],
"returntype" : "Void",
"name" : "_initialize",
"safe" : false
}
]
},
"supportedstandards" : [
"NEP-11"
],
"permissions" : [
{
"contract" : "0x726cb6e0cd8628a1350a611384688911ab75f51b",
"methods" : [
"ripemd160"
]
},
{
"contract" : "0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0",
"methods" : [
"atoi",
"deserialize",
"serialize",
"stringSplit"
]
},
{
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"methods" : [
"getCommittee"
]
},
{
"contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd",
"methods" : [
"getContract",
"update"
]
},
{
"methods" : [
"onNEP11Payment"
],
"contract" : "*"
}
],
"features" : {},
"name" : "NameService",
"trusts" : [],
"extra" : {
"Author" : "The Neo Project",
"Description" : "Neo Name Service",
"Email" : "dev@neo.org"
},
"groups" : []
}

View file

@ -1,339 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nextoken contains RPC wrappers for NEX Token contract.
package nextoken
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xce, 0xd5, 0xbf, 0xc6, 0x22, 0xcf, 0xe8, 0x9, 0x7f, 0xa6, 0xa2}
// OnMintEvent represents "OnMint" event emitted by the contract.
type OnMintEvent struct {
From util.Uint160
To util.Uint160
Amount *big.Int
SwapId *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep17.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep17.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep17.TokenReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep17.TokenWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep17t = nep17.New(actor, hash)
return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash}
}
// Cap invokes `cap` method of contract.
func (c *ContractReader) Cap() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "cap"))
}
// GetMinter invokes `getMinter` method of contract.
func (c *ContractReader) GetMinter() (*keys.PublicKey, error) {
return unwrap.PublicKey(c.invoker.Call(c.hash, "getMinter"))
}
// GetOwner invokes `getOwner` method of contract.
func (c *ContractReader) GetOwner() (util.Uint160, error) {
return unwrap.Uint160(c.invoker.Call(c.hash, "getOwner"))
}
// TotalMinted invokes `totalMinted` method of contract.
func (c *ContractReader) TotalMinted() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "totalMinted"))
}
// ChangeMinter creates a transaction invoking `changeMinter` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) ChangeMinter(newMinter *keys.PublicKey) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "changeMinter", newMinter)
}
// ChangeMinterTransaction creates a transaction invoking `changeMinter` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ChangeMinterTransaction(newMinter *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "changeMinter", newMinter)
}
// ChangeMinterUnsigned creates a transaction invoking `changeMinter` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ChangeMinterUnsigned(newMinter *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "changeMinter", nil, newMinter)
}
// ChangeOwner creates a transaction invoking `changeOwner` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) ChangeOwner(newOwner util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "changeOwner", newOwner)
}
// ChangeOwnerTransaction creates a transaction invoking `changeOwner` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ChangeOwnerTransaction(newOwner util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "changeOwner", newOwner)
}
// ChangeOwnerUnsigned creates a transaction invoking `changeOwner` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ChangeOwnerUnsigned(newOwner util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "changeOwner", nil, newOwner)
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
// MaxSupply creates a transaction invoking `maxSupply` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) MaxSupply() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "maxSupply")
}
// MaxSupplyTransaction creates a transaction invoking `maxSupply` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MaxSupplyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "maxSupply")
}
// MaxSupplyUnsigned creates a transaction invoking `maxSupply` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MaxSupplyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "maxSupply", nil)
}
// Mint creates a transaction invoking `mint` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Mint(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "mint", from, to, amount, swapId, signature, data)
}
// MintTransaction creates a transaction invoking `mint` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MintTransaction(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "mint", from, to, amount, swapId, signature, data)
}
// MintUnsigned creates a transaction invoking `mint` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MintUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, swapId *big.Int, signature []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "mint", nil, from, to, amount, swapId, signature, data)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
// UpdateCap creates a transaction invoking `updateCap` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UpdateCap(newCap *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "updateCap", newCap)
}
// UpdateCapTransaction creates a transaction invoking `updateCap` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateCapTransaction(newCap *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "updateCap", newCap)
}
// UpdateCapUnsigned creates a transaction invoking `updateCap` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "updateCap", nil, newCap)
}
// OnMintEventsFromApplicationLog retrieves a set of all emitted events
// with "OnMint" name from the provided [result.ApplicationLog].
func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*OnMintEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "OnMint" {
continue
}
event := new(OnMintEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize OnMintEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to OnMintEvent or
// returns an error if it's not possible to do to so.
func (e *OnMintEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 4 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.From, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field From: %w", err)
}
index++
e.To, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field To: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
index++
e.SwapId, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field SwapId: %w", err)
}
return nil
}

View file

@ -1,275 +0,0 @@
{
"name" : "NEX Token",
"abi" : {
"events" : [
{
"parameters" : [
{
"type" : "Hash160",
"name" : "from"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"name" : "amount",
"type" : "Integer"
}
],
"name" : "Transfer"
},
{
"name" : "OnMint",
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"type" : "Hash160",
"name" : "to"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"name" : "swapId",
"type" : "Integer"
}
]
}
],
"methods" : [
{
"parameters" : [],
"offset" : 0,
"name" : "_initialize",
"safe" : false,
"returntype" : "Void"
},
{
"parameters" : [
{
"type" : "Any",
"name" : "data"
},
{
"name" : "isUpdate",
"type" : "Boolean"
}
],
"offset" : 3,
"name" : "_deploy",
"safe" : false,
"returntype" : "Void"
},
{
"parameters" : [
{
"type" : "Hash160",
"name" : "holder"
}
],
"offset" : 484,
"returntype" : "Integer",
"safe" : true,
"name" : "balanceOf"
},
{
"safe" : true,
"returntype" : "Integer",
"name" : "cap",
"offset" : 1881,
"parameters" : []
},
{
"name" : "changeMinter",
"safe" : false,
"returntype" : "Void",
"parameters" : [
{
"name" : "newMinter",
"type" : "PublicKey"
}
],
"offset" : 1678
},
{
"parameters" : [
{
"type" : "Hash160",
"name" : "newOwner"
}
],
"offset" : 1659,
"name" : "changeOwner",
"safe" : false,
"returntype" : "Void"
},
{
"offset" : 466,
"parameters" : [],
"safe" : true,
"name" : "decimals",
"returntype" : "Integer"
},
{
"returntype" : "Void",
"safe" : false,
"name" : "destroy",
"parameters" : [],
"offset" : 1194
},
{
"safe" : true,
"returntype" : "PublicKey",
"name" : "getMinter",
"offset" : 1671,
"parameters" : []
},
{
"parameters" : [],
"offset" : 1652,
"name" : "getOwner",
"safe" : true,
"returntype" : "Hash160"
},
{
"name" : "maxSupply",
"safe" : false,
"returntype" : "Integer",
"parameters" : [],
"offset" : 468
},
{
"offset" : 1222,
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"name" : "swapId",
"type" : "Integer"
},
{
"name" : "signature",
"type" : "Signature"
},
{
"name" : "data",
"type" : "Any"
}
],
"safe" : false,
"name" : "mint",
"returntype" : "Void"
},
{
"safe" : true,
"name" : "symbol",
"returntype" : "String",
"parameters" : [],
"offset" : 460
},
{
"offset" : 1854,
"parameters" : [],
"name" : "totalMinted",
"safe" : true,
"returntype" : "Integer"
},
{
"offset" : 478,
"parameters" : [],
"name" : "totalSupply",
"safe" : true,
"returntype" : "Integer"
},
{
"offset" : 543,
"parameters" : [
{
"name" : "from",
"type" : "Hash160"
},
{
"name" : "to",
"type" : "Hash160"
},
{
"type" : "Integer",
"name" : "amount"
},
{
"name" : "data",
"type" : "Any"
}
],
"name" : "transfer",
"safe" : false,
"returntype" : "Boolean"
},
{
"offset" : 1205,
"parameters" : [
{
"type" : "ByteArray",
"name" : "nef"
},
{
"name" : "manifest",
"type" : "ByteArray"
}
],
"safe" : false,
"returntype" : "Void",
"name" : "update"
},
{
"offset" : 1717,
"parameters" : [
{
"type" : "Integer",
"name" : "newCap"
}
],
"returntype" : "Void",
"safe" : false,
"name" : "updateCap"
}
]
},
"supportedstandards" : [
"NEP-17"
],
"extra" : null,
"trusts" : [],
"features" : {},
"groups" : [],
"permissions" : [
{
"methods" : [
"onNEP17Payment"
],
"contract" : "*"
},
{
"methods" : [
"update",
"destroy"
],
"contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd"
}
]
}

View file

@ -1,63 +0,0 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nonnepxxcontractwithiterators contains RPC wrappers for Non-NEPXX contract with iterators contract.
package nonnepxxcontractwithiterators
import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error)
TerminateSession(sessionID uuid.UUID) error
TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{invoker, hash}
}
// Tokens invokes `tokens` method of contract.
func (c *ContractReader) Tokens() (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "tokens"))
}
// TokensExpanded is similar to Tokens (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) TokensExpanded(_numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "tokens", _numOfIteratorItems))
}
// GetAllRecords invokes `getAllRecords` method of contract.
func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name))
}
// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name))
}

View file

@ -1,33 +0,0 @@
{
"groups" : [],
"abi" : {
"events" : [],
"methods" : [
{
"parameters" : [],
"safe" : true,
"name" : "tokens",
"offset" : 0,
"returntype" : "InteropInterface"
},
{
"offset" : 1,
"returntype" : "InteropInterface",
"safe" : true,
"parameters" : [
{
"type" : "String",
"name" : "name"
}
],
"name" : "getAllRecords"
}
]
},
"supportedstandards" : [],
"trusts" : [],
"extra" : {},
"permissions" : [],
"name" : "Non-NEPXX contract with iterators",
"features" : {}
}

View file

@ -1,7 +0,0 @@
package invalid1
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() {
runtime.Notify("Non declared event")
}

View file

@ -1 +0,0 @@
name: Test undeclared event

View file

@ -1,7 +0,0 @@
package invalid2
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() {
runtime.Notify("SomeEvent", "p1", "p2")
}

View file

@ -1,6 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: String

View file

@ -1,7 +0,0 @@
package invalid3
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() {
runtime.Notify("SomeEvent", "p1", 5)
}

View file

@ -1,8 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: String
- name: p2
type: String

View file

@ -1,17 +0,0 @@
package invalid4
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
type SomeStruct1 struct {
Field1 int
}
type SomeStruct2 struct {
Field2 string
}
func Main() {
// Inconsistent event params usages (different named types throughout the usages).
runtime.Notify("SomeEvent", SomeStruct1{Field1: 123})
runtime.Notify("SomeEvent", SomeStruct2{Field2: "str"})
}

View file

@ -1,6 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: Array

View file

@ -1,12 +0,0 @@
package invalid5
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
type NamedStruct struct {
SomeInt int
}
func Main() NamedStruct {
runtime.Notify("SomeEvent", []interface{}{123})
return NamedStruct{SomeInt: 123}
}

View file

@ -1,16 +0,0 @@
name: Test undeclared event
events:
- name: SomeEvent
parameters:
- name: p1
type: Array
extendedtype:
base: Array
name: invalid5.NamedStruct
namedtypes:
invalid5.NamedStruct:
base: Array
name: invalid5.NamedStruct
fields:
- field: SomeInt
base: Integer

View file

@ -1,14 +0,0 @@
package invalid6
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
type SomeStruct struct {
Field int
// RPC binding generator will convert this field into exported, which matches
// exactly the existing Field.
field int
}
func Main() {
runtime.Notify("SomeEvent", SomeStruct{Field: 123, field: 123})
}

View file

@ -1,18 +0,0 @@
name: Test duplicating event fields
events:
- name: SomeEvent
parameters:
- name: p1
type: Struct
extendedtype:
base: Struct
name: SomeStruct
namedtypes:
SomeStruct:
base: Struct
name: SomeStruct
fields:
- field: Field
base: Integer
- field: field
base: Integer

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