Compare commits
161 commits
subs-notar
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
b66cea5ccc | ||
|
e5eb20bbae | ||
|
cf4d4a2611 | ||
|
7a7a5d0322 | ||
|
4d6333866d | ||
|
4a5e8f8592 | ||
|
b4fdf8c3c9 | ||
|
3c471f0b7e | ||
|
41109f442a | ||
|
5c408f7fe4 | ||
|
cd525a1df5 | ||
|
836183ecb6 | ||
|
3c1c650ddf | ||
|
45b8af359d | ||
|
4945145b09 | ||
|
f48e992a78 | ||
|
0b136c1c9c | ||
|
f4aeaa6387 | ||
|
0ae5e7ea83 | ||
|
228052360e | ||
|
5cbfe215a4 | ||
|
72161fe902 | ||
|
aceaabe9d6 | ||
|
9ba532ce9c | ||
|
2aa3c93acd | ||
|
aa5a0cb49b | ||
|
7868578571 | ||
|
6a5833759c | ||
|
6be757af3e | ||
|
67b6d5da31 | ||
|
b5bb605651 | ||
|
d91a0945df | ||
|
c90f6785b9 | ||
|
c30e7ec8d7 | ||
|
ce442a1942 | ||
|
e92606a7ae | ||
|
df1ed68d98 | ||
|
82a7c1bd9c | ||
|
c43cfae24c | ||
|
2d4993a837 | ||
|
647b8c7fb1 | ||
|
0b69901d1e | ||
|
502f024e7c | ||
|
b1bb12df48 | ||
|
31a99d44eb | ||
|
dc8b2f639a | ||
|
2ad4c86712 | ||
|
9a1075d332 | ||
|
58a086ea91 | ||
|
59c98c4d09 | ||
|
f4731eab91 | ||
|
13020ccd02 | ||
|
bce94dfa66 | ||
|
244ca6f438 | ||
|
a32217fac8 | ||
|
0435551eee | ||
|
0a3bf01a8f | ||
|
0aecddea10 | ||
|
02ecbeb519 | ||
|
e275495850 | ||
|
6e0926e59f | ||
|
988440949b | ||
|
701ea8d5f3 | ||
|
3d1e33a502 | ||
|
5053a073f9 | ||
|
71aa32406d | ||
|
3acb132e9a | ||
|
bd2f9c75e1 | ||
|
34eef47a18 | ||
|
7995229f6b | ||
|
b21db99747 | ||
|
bc158eed51 | ||
|
ac70f069ff | ||
|
b54fcbcdd9 | ||
|
8da7c0a671 | ||
|
956fd08adb | ||
|
1292a00ef9 | ||
|
7c8d2c3ec5 | ||
|
df2a56908b | ||
|
198af7f3ae | ||
|
874ed1ac2e | ||
|
4a8a43b983 | ||
|
6cf785ce54 | ||
|
9b8d579bce | ||
|
36e128516b | ||
|
4ca2686583 | ||
|
235f4398c6 | ||
|
ec6fc54bc6 | ||
|
2d3d52e3d0 | ||
|
82993dab2b | ||
|
5c75ee13d0 | ||
|
3a2e301267 | ||
|
d74dc368e0 | ||
|
73c742a466 | ||
|
d62fad1268 | ||
|
a13dc59bd9 | ||
|
678c1982f9 | ||
|
c84adf856a | ||
|
7345dbcab4 | ||
|
fbe514ca91 | ||
|
83fdcc8568 | ||
|
4b9024fa45 | ||
|
9b688a9ee9 | ||
|
1786136a23 | ||
|
d3993ad54c | ||
|
db37101e34 | ||
|
567f9f9c6e | ||
|
fb4a4f074f | ||
|
97a19df6f0 | ||
|
c5dbecb754 | ||
|
fc79d38ad2 | ||
|
ae3515e819 | ||
|
8913542f93 | ||
|
69bcd4b7d9 | ||
|
51537dfd29 | ||
|
ee1ee56f97 | ||
|
8c1d061446 | ||
|
a28e5b24b2 | ||
|
7aa5983543 | ||
|
fcbfc3b807 | ||
|
cf49e8c4c8 | ||
|
78cefca5c9 | ||
|
f2bb4d455b | ||
|
f553f77d20 | ||
|
700a550973 | ||
|
f84f072a29 | ||
|
927dbb6dc4 | ||
|
e5c919f701 | ||
|
22af33a14e | ||
|
ef3ec190d0 | ||
|
b1a986fba8 | ||
|
f6901806a8 | ||
|
686c77aeea | ||
|
89ef26164c | ||
|
4c288720cd | ||
|
f8c2d269fe | ||
|
698bdc7eea | ||
|
0280ceefe6 | ||
|
b6a9c64c55 | ||
|
be1b97d04e | ||
|
29deba45ca | ||
|
c81ed22698 | ||
|
c5ba53986f | ||
|
fff25bd85c | ||
|
182762af5a | ||
|
e7a0ded65e | ||
|
7202518fcf | ||
|
e0e7fdf810 | ||
|
f95004ad83 | ||
|
309016561d | ||
|
65dbd537ec | ||
|
b947d09552 | ||
|
5c995e71b5 | ||
|
5b30d15f8e | ||
|
57d82c1281 | ||
|
e341e64684 | ||
|
cd215fe096 | ||
|
d7ab7634ef | ||
|
00ac38b545 | ||
|
56e6119f78 | ||
|
d3c8916827 |
171 changed files with 4308 additions and 752 deletions
47
.github/workflows/tests.yml
vendored
47
.github/workflows/tests.yml
vendored
|
@ -8,7 +8,7 @@ on:
|
||||||
- master
|
- master
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'scripts/**'
|
- 'scripts/*.sh'
|
||||||
- '**/*.md'
|
- '**/*.md'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ jobs:
|
||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v4
|
||||||
with:
|
with:
|
||||||
version: latest
|
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:
|
gomodcheck:
|
||||||
name: Check internal dependencies
|
name: Check internal dependencies
|
||||||
|
@ -38,6 +39,40 @@ jobs:
|
||||||
- name: Check dependencies
|
- name: Check dependencies
|
||||||
run: |
|
run: |
|
||||||
./scripts/check_deps.sh
|
./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:
|
codeql:
|
||||||
name: CodeQL
|
name: CodeQL
|
||||||
|
@ -57,7 +92,7 @@ jobs:
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
@ -68,7 +103,7 @@ jobs:
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
|
@ -82,7 +117,7 @@ jobs:
|
||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|
||||||
test_cover:
|
test_cover:
|
||||||
name: Coverage
|
name: Coverage
|
||||||
|
@ -107,10 +142,12 @@ jobs:
|
||||||
run: go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
|
run: go test -timeout 15m -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/...
|
||||||
|
|
||||||
- name: Upload coverage results to Codecov
|
- name: Upload coverage results to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: true # if something is wrong on uploading codecov results, then this job will fail
|
fail_ci_if_error: true # if something is wrong on uploading codecov results, then this job will fail
|
||||||
files: ./coverage.txt
|
files: ./coverage.txt
|
||||||
|
slug: nspcc-dev/neo-go
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
verbose: true
|
verbose: true
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
|
|
115
CHANGELOG.md
115
CHANGELOG.md
|
@ -2,6 +2,119 @@
|
||||||
|
|
||||||
This document outlines major changes between releases.
|
This document outlines major changes between releases.
|
||||||
|
|
||||||
|
## 0.106.1 "Implication" (3 Jun 2024)
|
||||||
|
|
||||||
|
An urgent release that fixes mainnet state difference at block 5462944 which halts
|
||||||
|
blocks processing starting from the height 5468658. This release requires full node
|
||||||
|
DB resynchronization for mainnet nodes. T5 testnet DB state is not affected by this
|
||||||
|
bug (at least up to the current 4087361 height). Thus, DB resynchronisation may be
|
||||||
|
skipped for testnet nodes. No configuration changes implied.
|
||||||
|
|
||||||
|
Bugs fixed:
|
||||||
|
* mainnet state difference at block 5462944 caused by runtime notification
|
||||||
|
permissions check against the updated contract state instead of executing state
|
||||||
|
(#3472)
|
||||||
|
* unused neofs-contract dependency in the node modules (#3458)
|
||||||
|
|
||||||
|
## 0.106.0 "Zephyranthes" (21 May 2024)
|
||||||
|
|
||||||
|
We're rolling out a large set of updates including all of Neo 3.7.4 protocol changes:
|
||||||
|
native contracts update functionality and extended native contract APIs united under
|
||||||
|
the upcoming Cockatrice hardfork. For smart contract developers an ability to
|
||||||
|
generate smart contract bindings with dynamic hash is supported as well as a number
|
||||||
|
of useful extensions for `unwrap` package, compatible NNS smart contract RPC binding
|
||||||
|
and a lot of other handy enhancements. This release also includes a couple of severe
|
||||||
|
regression bug fixes affecting the node state. As a bonus of fixing a wide range of
|
||||||
|
failing tests, we've refactored the node services start and shutdown procedures to
|
||||||
|
make it more stable. This version drops support for Go 1.19 and requires 1.20+ to
|
||||||
|
build (with Go 1.22 supported).
|
||||||
|
|
||||||
|
Notice that this release requires full node resynchronization for both mainnet
|
||||||
|
and testnet nodes. Please pay a special attention to the Cockatrice hardfork schedule
|
||||||
|
and check your configurations. It should be configured for 3967000 of T5 testnet and
|
||||||
|
5450000 of mainnet. To ensure compatibility your node must be configured
|
||||||
|
appropriately.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
* native contracts update functionality bound to hardforks (#3402, #3444)
|
||||||
|
* Cockatrice hardfork planned for 5450000 block of mainnet and 3967000 block of T5
|
||||||
|
testnet (#3402, #3448, #3402, #3446)
|
||||||
|
* `CommitteeChanged` events are emitted by NeoToken native contract starting from
|
||||||
|
Cockatrice hardfork (#3351, #3448)
|
||||||
|
* `getCommitteeAddress` method of NeoToken native contract is available starting
|
||||||
|
from Cockatrice hardfork (#3362, #3402)
|
||||||
|
* `keccak256` method of CryptoLib native contract is available starting from
|
||||||
|
Cockatrice hardfork (#3301, #3402)
|
||||||
|
* dynamic contract hash support for smart contract bindings (#3405)
|
||||||
|
* support `StringCompressed` API for `crypto.PublicKey` (#3408)
|
||||||
|
* support `Copy` API for `transaction.Transaction` and `payload.P2PNotaryRequest`
|
||||||
|
(#3407)
|
||||||
|
* extend `verifyWithECDsa` method of native CryptoLib contract with Keccak256 hasher
|
||||||
|
starting from Cockatrice and add an example of custom Koblitz-based and
|
||||||
|
Keccak256-based transaction witness verification (#3425)
|
||||||
|
* autogenerated `nativehashes` package (#3402, #3431)
|
||||||
|
* introduce `unwrap.Exception` type for better RPC invocation result exceptions
|
||||||
|
detection (#3438)
|
||||||
|
|
||||||
|
Behavior changes:
|
||||||
|
* Neo Name Service smart contract RPC binding follows the official N3 implementation
|
||||||
|
(#3291)
|
||||||
|
* clear LastGasPerVote NeoToken account info on unvoting (#3349)
|
||||||
|
* return fault exception (if any) in `unwrap` RPC client helpers (#3356)
|
||||||
|
* move P2PNotary designation role out of P2PSigExtensions (#3452)
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
* support `smartcontract.Convertible` interface as RPC Actor and Invoker call
|
||||||
|
parameters (#3297)
|
||||||
|
* allow to import multisignature account into wallet without WIF and password
|
||||||
|
specified (#3293)
|
||||||
|
* upgrade go-ordered-json dependency to avoid possible node state incompatibility
|
||||||
|
issues caused by smart contract manifest marshalling (#3331, #3333)
|
||||||
|
* Go 1.22 support, bump minimum required Go version up to Go 1.20 (#3336)
|
||||||
|
* a number of dependent libraries are updated to the fresh versions (#3338, #3418)
|
||||||
|
* improve error message of `System.Crypto.CheckMultisig` interop API handler (#3374)
|
||||||
|
* upgrade dBFT library to v0.2.0 (#3371, #3413)
|
||||||
|
* increase `ValidUntilBlock` value for transactions generated by CLI commands
|
||||||
|
(#3376)
|
||||||
|
* documentation updates (#3375, #3382)
|
||||||
|
* let default Notary Actor options be customizable (#3394)
|
||||||
|
* extend `getversion` RPC response with RPC server settings (#3386)
|
||||||
|
* make errors related to wallet opening more detailed (#3389)
|
||||||
|
* adjust error messages of `setExecFeeFactor` and `setStoragePrice` methods of
|
||||||
|
native PolicyContract (#3406)
|
||||||
|
* make neotest chain constructor options customizable (#3404)
|
||||||
|
* format improvements for CLI commands description (#3410)
|
||||||
|
* make state dumps comparator script more verbose (#3411)
|
||||||
|
* refactor storage `Get` implementation for BoltDB (#3441)
|
||||||
|
* add `updatecounter` field to the resulting items of `getnativecontracts` RPC API
|
||||||
|
call (#3439)
|
||||||
|
|
||||||
|
Bugs fixed:
|
||||||
|
* node panic on committee missing from node configuration (#3294)
|
||||||
|
* autogenerated RPC bindings do not follow Go naming convention (#3299)
|
||||||
|
* a batch of failing tests is fixed (#3306, #3313, #3321, #3332, #3337, #3335,
|
||||||
|
#3344, #3340, #3350, #3355, #3364, #3368, #3377, #3393, #3397, #3392, #3398,
|
||||||
|
#3400)
|
||||||
|
* outdated minimum required Go version of boilerplate contract generated by
|
||||||
|
`neo-go contract init` (#3318)
|
||||||
|
* outdated address used as an example in RPC client documentation (#3327)
|
||||||
|
* ungraceful node services shutdown procedure (#3307)
|
||||||
|
* logging data race on node services shutdown (#3307)
|
||||||
|
* Map parameter support is missing from RPC server handlers (#3329)
|
||||||
|
* smart contract storage iterator prefix wasn't copied while iterating over values
|
||||||
|
(#3336)
|
||||||
|
* RPC error with -511 code (insufficient funds) returned on `sendrawtransaction` RPC
|
||||||
|
request should cover multiple cases of sender's insufficient funds (#3360, #3361)
|
||||||
|
* reentrancy possibility for Notary deposit withdrawal (#3357)
|
||||||
|
* null `findstorage` RPC response in case of missing storage items (#3385)
|
||||||
|
* uninitialized named return variables in compiler (#3401)
|
||||||
|
* wrong debug sequence points after `JUMP*` instructions shortening by compiler
|
||||||
|
(#3412)
|
||||||
|
* corrupted genesis block record and application log caused by improper Conflicts
|
||||||
|
attribute processing (#3437)
|
||||||
|
* false positive DB-based blocked accounts detection in native PolicyContract leaded
|
||||||
|
to invalid committee computations and reward distribution made by NeoToken (#3443)
|
||||||
|
|
||||||
## 0.105.1 "Enumeration" (12 Jan 2024)
|
## 0.105.1 "Enumeration" (12 Jan 2024)
|
||||||
|
|
||||||
This is another v3.6.2-compatible release that fixes mainnet state difference at
|
This is another v3.6.2-compatible release that fixes mainnet state difference at
|
||||||
|
@ -2496,7 +2609,7 @@ Behavior changes:
|
||||||
* contracts no longer have single entry point, rather they export a set of
|
* contracts no longer have single entry point, rather they export a set of
|
||||||
methods with specific offsets. Go smart contract compiler has been changed
|
methods with specific offsets. Go smart contract compiler has been changed
|
||||||
accordingly to add all exported (as in Go) methods to the manifest
|
accordingly to add all exported (as in Go) methods to the manifest
|
||||||
(but with the first letter being lowercased to match NEP-5 expections,
|
(but with the first letter being lowercased to match NEP-5 expectations,
|
||||||
#1228). Please also refer to examples changes to better see how it affects
|
#1228). Please also refer to examples changes to better see how it affects
|
||||||
contracts, manifests and configuration files (#1296)
|
contracts, manifests and configuration files (#1296)
|
||||||
* native contracts are now called via Neo.Native.Call syscall (#1191)
|
* native contracts are now called via Neo.Native.Call syscall (#1191)
|
||||||
|
|
20
ROADMAP.md
20
ROADMAP.md
|
@ -7,10 +7,11 @@ functionality.
|
||||||
## Versions 0.7X.Y (as needed)
|
## Versions 0.7X.Y (as needed)
|
||||||
* Neo 2.0 support (bug fixes, minor functionality additions)
|
* Neo 2.0 support (bug fixes, minor functionality additions)
|
||||||
|
|
||||||
## Version 0.106.0 (~February 2023)
|
## Version 0.107.0 (~Jun-Jul 2024)
|
||||||
* extended data types for iterators to be used by RPC wrapper generator
|
|
||||||
* protocol updates
|
* protocol updates
|
||||||
* test fixes
|
* bug fixes
|
||||||
|
* node resynchronisation from local DB
|
||||||
|
* CLI library upgrade
|
||||||
|
|
||||||
## Version 1.0 (2024, TBD)
|
## Version 1.0 (2024, TBD)
|
||||||
* stable version
|
* stable version
|
||||||
|
@ -30,15 +31,15 @@ GetPeers RPC command returns a list of Peers where the port type has changed fro
|
||||||
string to uint16 to match C#. The RPC client currently supports unmarshalling both
|
string to uint16 to match C#. The RPC client currently supports unmarshalling both
|
||||||
formats.
|
formats.
|
||||||
|
|
||||||
Removal of Peer unmarshalling with string based ports is scheduled for ~September 2023
|
Removal of Peer unmarshalling with string based ports is scheduled for Jun-Jul 2024
|
||||||
(~0.105.0 release).
|
(~0.107.0 release).
|
||||||
|
|
||||||
## `NEOBalance` from stack item
|
## `NEOBalance` from stack item
|
||||||
|
|
||||||
We check struct items count before convert LastGasPerVote to let RPC client be compatible with
|
We check struct items count before convert LastGasPerVote to let RPC client be compatible with
|
||||||
old versions.
|
old versions.
|
||||||
|
|
||||||
Removal of this compatiblility code is scheduled for Sep-Oct 2023.
|
Removal of this compatiblility code is scheduled for Jun-Jul 2024.
|
||||||
|
|
||||||
## `serv_node_version` Prometheus gauge metric
|
## `serv_node_version` Prometheus gauge metric
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ metrics with proper version formatting. `neogo_version` contains NeoGo version
|
||||||
hidden under `version` label and `server_id` contains network server ID hidden
|
hidden under `version` label and `server_id` contains network server ID hidden
|
||||||
under `server_id` label.
|
under `server_id` label.
|
||||||
|
|
||||||
Removal of `serv_node_version` is scheduled for Sep-Oct 2023 (~0.105.0 release).
|
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
|
## RPC error codes returned by old versions and C#-nodes
|
||||||
|
|
||||||
|
@ -55,7 +56,8 @@ NeoGo retains certain deprecated error codes: `neorpc.ErrCompatGeneric`,
|
||||||
`neorpc.ErrCompatNoOpenedWallet`. They returned by nodes not compliant with the
|
`neorpc.ErrCompatNoOpenedWallet`. They returned by nodes not compliant with the
|
||||||
neo-project/proposals#156 (NeoGo pre-0.102.0 and all known C# versions).
|
neo-project/proposals#156 (NeoGo pre-0.102.0 and all known C# versions).
|
||||||
|
|
||||||
Removal of the deprecated RPC error codes is planned once all nodes adopt the new error standard.
|
Removal of the deprecated RPC error codes is planned for Jun-Jul 2024 (~0.107.0
|
||||||
|
release).
|
||||||
|
|
||||||
## Block based web-socket waiter transaction awaiting
|
## Block based web-socket waiter transaction awaiting
|
||||||
|
|
||||||
|
@ -66,4 +68,4 @@ event-based waiter fallbacks to the old way of block monitoring with
|
||||||
`block_added` notifications subscription.
|
`block_added` notifications subscription.
|
||||||
|
|
||||||
Removal of stale RPC server compatibility code from `waiter.EventWaiter` is
|
Removal of stale RPC server compatibility code from `waiter.EventWaiter` is
|
||||||
scheduled for May-June 2024 (~0.107.0 release).
|
scheduled for Jun-Jul 2024 (~0.107.0 release).
|
|
@ -1,7 +1,6 @@
|
||||||
package cmdargs
|
package cmdargs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@ func TestParseCosigner(t *testing.T) {
|
||||||
Scopes: transaction.CalledByEntry | transaction.CustomContracts,
|
Scopes: transaction.CalledByEntry | transaction.CustomContracts,
|
||||||
AllowedContracts: []util.Uint160{c1, c2},
|
AllowedContracts: []util.Uint160{c1, c2},
|
||||||
},
|
},
|
||||||
acc.StringLE() + ":CustomGroups:" + hex.EncodeToString(priv.PublicKey().Bytes()): {
|
acc.StringLE() + ":CustomGroups:" + priv.PublicKey().StringCompressed(): {
|
||||||
Account: acc,
|
Account: acc,
|
||||||
Scopes: transaction.CustomGroups,
|
Scopes: transaction.CustomGroups,
|
||||||
AllowedGroups: keys.PublicKeys{priv.PublicKey()},
|
AllowedGroups: keys.PublicKeys{priv.PublicKey()},
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
|
"golang.org/x/term"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -219,6 +220,7 @@ var (
|
||||||
// If logPath is configured -- function creates a dir and a file for logging.
|
// If logPath is configured -- function creates a dir and a file for logging.
|
||||||
// If logPath is configured on Windows -- function returns closer to be
|
// If logPath is configured on Windows -- function returns closer to be
|
||||||
// able to close sink for the opened log output file.
|
// 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) {
|
func HandleLoggingParams(debug bool, cfg config.ApplicationConfiguration) (*zap.Logger, *zap.AtomicLevel, func() error, error) {
|
||||||
var (
|
var (
|
||||||
level = zapcore.InfoLevel
|
level = zapcore.InfoLevel
|
||||||
|
@ -239,7 +241,11 @@ func HandleLoggingParams(debug bool, cfg config.ApplicationConfiguration) (*zap.
|
||||||
cc.DisableStacktrace = true
|
cc.DisableStacktrace = true
|
||||||
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
|
cc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
|
||||||
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
cc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||||
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
if term.IsTerminal(int(os.Stdout.Fd())) {
|
||||||
|
cc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||||
|
} else {
|
||||||
|
cc.EncoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {}
|
||||||
|
}
|
||||||
cc.Encoding = "console"
|
cc.Encoding = "console"
|
||||||
cc.Level = zap.NewAtomicLevelAt(level)
|
cc.Level = zap.NewAtomicLevelAt(level)
|
||||||
cc.Sampling = nil
|
cc.Sampling = nil
|
||||||
|
|
|
@ -3,7 +3,6 @@ package query
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -204,7 +203,7 @@ func queryCandidates(ctx *cli.Context) error {
|
||||||
var res []byte
|
var res []byte
|
||||||
res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n")
|
res = fmt.Appendf(res, "Key\tVotes\tCommittee\tConsensus\n")
|
||||||
for _, val := range vals {
|
for _, val := range vals {
|
||||||
res = fmt.Appendf(res, "%s\t%d\t%t\t%t\n", hex.EncodeToString(val.PublicKey.Bytes()), val.Votes, comm.Contains(&val.PublicKey), val.Active)
|
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)
|
tw := tabwriter.NewWriter(ctx.App.Writer, 0, 2, 2, ' ', 0)
|
||||||
_, err = tw.Write(res)
|
_, err = tw.Write(res)
|
||||||
|
@ -235,7 +234,7 @@ func queryCommittee(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, k := range comm {
|
for _, k := range comm {
|
||||||
fmt.Fprintln(ctx.App.Writer, hex.EncodeToString(k.Bytes()))
|
fmt.Fprintln(ctx.App.Writer, k.StringCompressed())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -306,7 +305,7 @@ func queryVoter(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
voted := "null"
|
voted := "null"
|
||||||
if st.VoteTo != nil {
|
if st.VoteTo != nil {
|
||||||
voted = fmt.Sprintf("%s (%s)", hex.EncodeToString(st.VoteTo.Bytes()), address.Uint160ToString(st.VoteTo.GetScriptHash()))
|
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, "\tVoted: %s\n", voted)
|
||||||
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec)))
|
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", fixedn.ToString(&st.Balance, int(dec)))
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
|
@ -145,18 +146,7 @@ func Blocks() []*alias.Block {
|
||||||
cmd = append(cmd, "--in", ctrPath, "--bindings", bindingsPath)
|
cmd = append(cmd, "--in", ctrPath, "--bindings", bindingsPath)
|
||||||
|
|
||||||
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
|
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
|
||||||
goMod := filepath.Join(ctrPath, "go.mod")
|
require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
|
||||||
data, err := os.ReadFile(goMod)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
i := bytes.IndexByte(data, '\n')
|
|
||||||
data = append([]byte("module myimport.com/testcontract"), data[i:]...)
|
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
require.NoError(t, err)
|
|
||||||
data = append(data, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "...)
|
|
||||||
data = append(data, filepath.Join(wd, "../../pkg/interop")...)
|
|
||||||
require.NoError(t, os.WriteFile(goMod, data, os.ModePerm))
|
|
||||||
|
|
||||||
cmd = append(cmd, "--config", cfgPath,
|
cmd = append(cmd, "--config", cfgPath,
|
||||||
"--out", filepath.Join(tmpDir, "out.nef"),
|
"--out", filepath.Join(tmpDir, "out.nef"),
|
||||||
|
@ -176,7 +166,7 @@ func Blocks() []*alias.Block {
|
||||||
|
|
||||||
bs, err := os.ReadFile(outPath)
|
bs, err := os.ReadFile(outPath)
|
||||||
require.NoError(t, err)
|
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.
|
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 testcontract contains wrappers for testcontract contract.
|
// Package testcontract contains wrappers for testcontract contract.
|
||||||
package testcontract
|
package testcontract
|
||||||
|
@ -213,6 +203,84 @@ func ToMap(a []testcontract.MyPair) map[int]string {
|
||||||
`, string(bs))
|
`, string(bs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateGoMod updates the go.mod file located in the specified directory.
|
||||||
|
// It sets the module name and replaces the neo-go interop package path with
|
||||||
|
// the provided one to avoid getting an actual module version.
|
||||||
|
func updateGoMod(dir, moduleName, neoGoPath string) error {
|
||||||
|
goModPath := filepath.Join(dir, "go.mod")
|
||||||
|
data, err := os.ReadFile(goModPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read go.mod: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := bytes.IndexByte(data, '\n')
|
||||||
|
if i == -1 {
|
||||||
|
return fmt.Errorf("unexpected go.mod format")
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedData := append([]byte("module "+moduleName), data[i:]...)
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get working directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
replacementPath := filepath.Join(wd, neoGoPath)
|
||||||
|
updatedData = append(updatedData, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "+replacementPath+" \n"...)
|
||||||
|
|
||||||
|
if err := os.WriteFile(goModPath, updatedData, os.ModePerm); err != nil {
|
||||||
|
return fmt.Errorf("failed to write updated go.mod: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDynamicWrapper(t *testing.T) {
|
||||||
|
// For proper contract init. The actual version as it will be replaced.
|
||||||
|
smartcontract.ModVersion = "v0.0.0"
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
e := testcli.NewExecutor(t, true)
|
||||||
|
|
||||||
|
ctrPath := "../smartcontract/testdata"
|
||||||
|
|
||||||
|
verifyHash := testcli.DeployContract(t, e, filepath.Join(ctrPath, "verify.go"), filepath.Join(ctrPath, "verify.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
|
||||||
|
|
||||||
|
helperContract := `package testcontract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
verify "myimport.com/testcontract/bindings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CallVerifyContract(h interop.Hash160) bool{
|
||||||
|
contractInstance := verify.NewContract(h)
|
||||||
|
return contractInstance.Verify()
|
||||||
|
}`
|
||||||
|
|
||||||
|
helperDir := filepath.Join(tmpDir, "helper")
|
||||||
|
e.Run(t, "neo-go", "contract", "init", "--name", helperDir)
|
||||||
|
|
||||||
|
require.NoError(t, updateGoMod(helperDir, "myimport.com/testcontract", "../../pkg/interop"))
|
||||||
|
require.NoError(t, os.WriteFile(filepath.Join(helperDir, "main.go"), []byte(helperContract), os.ModePerm))
|
||||||
|
require.NoError(t, os.Mkdir(filepath.Join(helperDir, "bindings"), os.ModePerm))
|
||||||
|
|
||||||
|
e.Run(t, "neo-go", "contract", "generate-wrapper",
|
||||||
|
"--config", filepath.Join(ctrPath, "verify.bindings.yml"), "--manifest", filepath.Join(ctrPath, "verify.manifest.json"),
|
||||||
|
"--out", filepath.Join(helperDir, "bindings", "testdata.go"))
|
||||||
|
e.Run(t, "neo-go", "contract", "compile", "--in", filepath.Join(helperDir, "main.go"), "--config", filepath.Join(helperDir, "neo-go.yml"))
|
||||||
|
helperHash := testcli.DeployContract(t, e, filepath.Join(helperDir, "main.go"), filepath.Join(helperDir, "neo-go.yml"), testcli.ValidatorWallet, testcli.ValidatorAddr, testcli.ValidatorPass)
|
||||||
|
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.Run(t, "neo-go", "contract", "invokefunction",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addresses()[0],
|
||||||
|
"--wallet", testcli.ValidatorWallet, "--address", testcli.ValidatorAddr, "--force", "--await", helperHash.StringLE(), "callVerifyContract", verifyHash.StringLE())
|
||||||
|
|
||||||
|
tx, _ := e.CheckTxPersisted(t, "Sent invocation transaction ")
|
||||||
|
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, aer[0].Stack[0].Value().(bool), true)
|
||||||
|
}
|
||||||
|
|
||||||
func TestContractInitAndCompile(t *testing.T) {
|
func TestContractInitAndCompile(t *testing.T) {
|
||||||
// For proper contract init. The actual version as it will be replaced.
|
// For proper contract init. The actual version as it will be replaced.
|
||||||
smartcontract.ModVersion = "v0.0.0"
|
smartcontract.ModVersion = "v0.0.0"
|
||||||
|
@ -265,15 +333,7 @@ func TestContractInitAndCompile(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
|
// Replace `pkg/interop` in go.mod to avoid getting an actual module version.
|
||||||
goMod := filepath.Join(ctrPath, "go.mod")
|
require.NoError(t, updateGoMod(ctrPath, "myimport.com/testcontract", "../../pkg/interop"))
|
||||||
data, err := os.ReadFile(goMod)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
require.NoError(t, err)
|
|
||||||
data = append(data, "\nreplace github.com/nspcc-dev/neo-go/pkg/interop => "...)
|
|
||||||
data = append(data, filepath.Join(wd, "../../pkg/interop")...)
|
|
||||||
require.NoError(t, os.WriteFile(goMod, data, os.ModePerm))
|
|
||||||
|
|
||||||
cmd = append(cmd, "--config", cfgPath)
|
cmd = append(cmd, "--config", cfgPath)
|
||||||
|
|
||||||
|
|
|
@ -30,17 +30,22 @@ var generatorFlags = []cli.Flag{
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "hash",
|
Name: "hash",
|
||||||
Usage: "Smart-contract hash",
|
Usage: "Smart-contract hash. If not passed, the wrapper will be designed for dynamic hash usage",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var generateWrapperCmd = cli.Command{
|
var generateWrapperCmd = cli.Command{
|
||||||
Name: "generate-wrapper",
|
Name: "generate-wrapper",
|
||||||
Usage: "generate wrapper to use in other contracts",
|
Usage: "generate wrapper to use in other contracts",
|
||||||
UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> --hash <hash> [--config <config>]",
|
UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]",
|
||||||
Description: ``,
|
Description: `Generates a Go wrapper to use it in other smart contracts. If the
|
||||||
Action: contractGenerateWrapper,
|
--hash flag is provided, CALLT instruction is used for the target contract
|
||||||
Flags: generatorFlags,
|
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{
|
var generateRPCWrapperCmd = cli.Command{
|
||||||
|
@ -52,15 +57,15 @@ var generateRPCWrapperCmd = cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func contractGenerateWrapper(ctx *cli.Context) error {
|
func contractGenerateWrapper(ctx *cli.Context) error {
|
||||||
return contractGenerateSomething(ctx, binding.Generate, false)
|
return contractGenerateSomething(ctx, binding.Generate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func contractGenerateRPCWrapper(ctx *cli.Context) error {
|
func contractGenerateRPCWrapper(ctx *cli.Context) error {
|
||||||
return contractGenerateSomething(ctx, rpcbinding.Generate, true)
|
return contractGenerateSomething(ctx, rpcbinding.Generate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractGenerateSomething reads generator parameters and calls the given callback.
|
// contractGenerateSomething reads generator parameters and calls the given callback.
|
||||||
func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error, allowEmptyHash bool) error {
|
func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error) error {
|
||||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -73,8 +78,6 @@ func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("invalid contract hash: %w", err), 1)
|
||||||
}
|
}
|
||||||
} else if !allowEmptyHash {
|
|
||||||
return cli.NewExitError("contract hash must be provided via --hash flag", 1)
|
|
||||||
}
|
}
|
||||||
m, _, err := readManifest(ctx.String("manifest"), h)
|
m, _, err := readManifest(ctx.String("manifest"), h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -151,7 +151,7 @@ callflags:
|
||||||
"--hash", h.StringLE(),
|
"--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.
|
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 contains wrappers for MyContract contract.
|
||||||
package wrapper
|
package wrapper
|
||||||
|
@ -233,6 +233,99 @@ func MyFunc(in map[int]mycontract.Input) []mycontract.Output {
|
||||||
data, err := os.ReadFile(outFile)
|
data, err := os.ReadFile(outFile)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expected, string(data))
|
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) {
|
func TestGenerateValidPackageName(t *testing.T) {
|
||||||
|
@ -267,7 +360,7 @@ func TestGenerateValidPackageName(t *testing.T) {
|
||||||
|
|
||||||
data, err := os.ReadFile(outFile)
|
data, err := os.ReadFile(outFile)
|
||||||
require.NoError(t, err)
|
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.
|
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 contains wrappers for My space contract contract.
|
||||||
package myspacecontract
|
package myspacecontract
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package smartcontract
|
package smartcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
@ -28,10 +27,9 @@ func (p permission) MarshalYAML() (any, error) {
|
||||||
&yaml.Node{Kind: yaml.ScalarNode, Value: permHashKey},
|
&yaml.Node{Kind: yaml.ScalarNode, Value: permHashKey},
|
||||||
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(util.Uint160).StringLE()})
|
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(util.Uint160).StringLE()})
|
||||||
case manifest.PermissionGroup:
|
case manifest.PermissionGroup:
|
||||||
bs := p.Contract.Value.(*keys.PublicKey).Bytes()
|
|
||||||
m.Content = append(m.Content,
|
m.Content = append(m.Content,
|
||||||
&yaml.Node{Kind: yaml.ScalarNode, Value: permGroupKey},
|
&yaml.Node{Kind: yaml.ScalarNode, Value: permGroupKey},
|
||||||
&yaml.Node{Kind: yaml.ScalarNode, Value: hex.EncodeToString(bs)})
|
&yaml.Node{Kind: yaml.ScalarNode, Value: p.Contract.Value.(*keys.PublicKey).StringCompressed()})
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type)
|
return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type)
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,8 @@ func NewCommands() []cli.Command {
|
||||||
then the output filenames for these flags will be guessed using the contract
|
then the output filenames for these flags will be guessed using the contract
|
||||||
name or path provided via --in option by trimming/adding corresponding suffixes
|
name or path provided via --in option by trimming/adding corresponding suffixes
|
||||||
to the common part of the path. In the latter case the configuration filepath
|
to the common part of the path. In the latter case the configuration filepath
|
||||||
will be guessed from the --in option using the same rule."`,
|
will be guessed from the --in option using the same rule.
|
||||||
|
`,
|
||||||
Action: contractCompile,
|
Action: contractCompile,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package smartcontract
|
package smartcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -109,7 +108,7 @@ func TestPermissionMarshal(t *testing.T) {
|
||||||
p.Methods.Add("abc")
|
p.Methods.Add("abc")
|
||||||
p.Methods.Add("lamao")
|
p.Methods.Add("lamao")
|
||||||
testPermissionMarshal(t, p,
|
testPermissionMarshal(t, p,
|
||||||
"group: "+hex.EncodeToString(priv.PublicKey().Bytes())+"\n"+
|
"group: "+priv.PublicKey().StringCompressed()+"\n"+
|
||||||
"methods:\n - abc\n - lamao\n")
|
"methods:\n - abc\n - lamao\n")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -118,7 +117,7 @@ func TestPermissionUnmarshalInvalid(t *testing.T) {
|
||||||
priv, err := keys.NewPrivateKey()
|
priv, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pub := hex.EncodeToString(priv.PublicKey().Bytes())
|
pub := priv.PublicKey().StringCompressed()
|
||||||
u160 := random.Uint160().StringLE()
|
u160 := random.Uint160().StringLE()
|
||||||
testCases := []string{
|
testCases := []string{
|
||||||
"hash: []\nmethods: '*'\n", // invalid hash type
|
"hash: []\nmethods: '*'\n", // invalid hash type
|
||||||
|
|
297
cli/smartcontract/testdata/verify.bindings.yml
vendored
Executable file
297
cli/smartcontract/testdata/verify.bindings.yml
vendored
Executable file
|
@ -0,0 +1,297 @@
|
||||||
|
package: testdata
|
||||||
|
hash: "0x0000000000000000000000000000000000000000"
|
||||||
|
overrides:
|
||||||
|
burnGas.gas: int
|
||||||
|
call: any
|
||||||
|
call.args: '[]any'
|
||||||
|
call.f: any
|
||||||
|
call.method: string
|
||||||
|
call.scriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
|
||||||
|
callWithToken: any
|
||||||
|
callWithToken.args: '[]any'
|
||||||
|
callWithToken.flags: int
|
||||||
|
callWithToken.method: string
|
||||||
|
callWithToken.scriptHash: string
|
||||||
|
callWithTokenNoRet.args: '[]any'
|
||||||
|
callWithTokenNoRet.flags: int
|
||||||
|
callWithTokenNoRet.method: string
|
||||||
|
callWithTokenNoRet.scriptHash: string
|
||||||
|
checkWitness: bool
|
||||||
|
checkWitness.hashOrKey: '[]byte'
|
||||||
|
createMultisigAccount: '[]byte'
|
||||||
|
createMultisigAccount.m: int
|
||||||
|
createMultisigAccount.pubs: '[]github.com/nspcc-dev/neo-go/pkg/interop.PublicKey'
|
||||||
|
createStandardAccount: '[]byte'
|
||||||
|
createStandardAccount.pub: github.com/nspcc-dev/neo-go/pkg/interop.PublicKey
|
||||||
|
currentHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
|
||||||
|
currentIndex: int
|
||||||
|
currentSigners: '[]github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.TransactionSigner'
|
||||||
|
equals: bool
|
||||||
|
equals.b: any
|
||||||
|
gasLeft: int
|
||||||
|
getAddressVersion: int
|
||||||
|
getBlock: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Block'
|
||||||
|
getBlock.indexOrHash: any
|
||||||
|
getCallFlags: any
|
||||||
|
getCallingScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
|
||||||
|
getEntryScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
|
||||||
|
getExecutingScriptHash: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
|
||||||
|
getInvocationCounter: int
|
||||||
|
getNetwork: int
|
||||||
|
getNotifications: '[][]any'
|
||||||
|
getNotifications.h: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
|
||||||
|
getRandom: int
|
||||||
|
getScriptContainer: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction'
|
||||||
|
getTime: int
|
||||||
|
getTransaction: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction'
|
||||||
|
getTransaction.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
|
||||||
|
getTransactionFromBlock: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.Transaction'
|
||||||
|
getTransactionFromBlock.indexOrHash: any
|
||||||
|
getTransactionFromBlock.txIndex: int
|
||||||
|
getTransactionHeight: int
|
||||||
|
getTransactionHeight.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
|
||||||
|
getTransactionSigners: '[]github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.TransactionSigner'
|
||||||
|
getTransactionSigners.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
|
||||||
|
getTransactionVMState: int
|
||||||
|
getTransactionVMState.hash: github.com/nspcc-dev/neo-go/pkg/interop.Hash256
|
||||||
|
getTrigger: int
|
||||||
|
loadScript: any
|
||||||
|
loadScript.args: '[]any'
|
||||||
|
loadScript.f: any
|
||||||
|
loadScript.script: '[]byte'
|
||||||
|
log.message: string
|
||||||
|
notify.args: '[]any'
|
||||||
|
notify.name: string
|
||||||
|
onNEP11Payment.amount: int
|
||||||
|
onNEP11Payment.data: any
|
||||||
|
onNEP11Payment.from: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
|
||||||
|
onNEP11Payment.token: '[]byte'
|
||||||
|
onNEP17Payment.amount: int
|
||||||
|
onNEP17Payment.data: any
|
||||||
|
onNEP17Payment.from: github.com/nspcc-dev/neo-go/pkg/interop.Hash160
|
||||||
|
opcode0NoReturn.op: string
|
||||||
|
opcode1: any
|
||||||
|
opcode1NoReturn.arg: any
|
||||||
|
opcode1NoReturn.op: string
|
||||||
|
opcode1.arg: any
|
||||||
|
opcode1.op: string
|
||||||
|
opcode2: any
|
||||||
|
opcode2NoReturn.arg1: any
|
||||||
|
opcode2NoReturn.arg2: any
|
||||||
|
opcode2NoReturn.op: string
|
||||||
|
opcode2.arg1: any
|
||||||
|
opcode2.arg2: any
|
||||||
|
opcode2.op: string
|
||||||
|
opcode3: any
|
||||||
|
opcode3.arg1: any
|
||||||
|
opcode3.arg2: any
|
||||||
|
opcode3.arg3: any
|
||||||
|
opcode3.op: string
|
||||||
|
platform: '[]byte'
|
||||||
|
syscall0: any
|
||||||
|
syscall0NoReturn.name: string
|
||||||
|
syscall0.name: string
|
||||||
|
syscall1: any
|
||||||
|
syscall1NoReturn.arg: any
|
||||||
|
syscall1NoReturn.name: string
|
||||||
|
syscall1.arg: any
|
||||||
|
syscall1.name: string
|
||||||
|
syscall2: any
|
||||||
|
syscall2NoReturn.arg1: any
|
||||||
|
syscall2NoReturn.arg2: any
|
||||||
|
syscall2NoReturn.name: string
|
||||||
|
syscall2.arg1: any
|
||||||
|
syscall2.arg2: any
|
||||||
|
syscall2.name: string
|
||||||
|
syscall3: any
|
||||||
|
syscall3NoReturn.arg1: any
|
||||||
|
syscall3NoReturn.arg2: any
|
||||||
|
syscall3NoReturn.arg3: any
|
||||||
|
syscall3NoReturn.name: string
|
||||||
|
syscall3.arg1: any
|
||||||
|
syscall3.arg2: any
|
||||||
|
syscall3.arg3: any
|
||||||
|
syscall3.name: string
|
||||||
|
syscall4: any
|
||||||
|
syscall4NoReturn.arg1: any
|
||||||
|
syscall4NoReturn.arg2: any
|
||||||
|
syscall4NoReturn.arg3: any
|
||||||
|
syscall4NoReturn.arg4: any
|
||||||
|
syscall4NoReturn.name: string
|
||||||
|
syscall4.arg1: any
|
||||||
|
syscall4.arg2: any
|
||||||
|
syscall4.arg3: any
|
||||||
|
syscall4.arg4: any
|
||||||
|
syscall4.name: string
|
||||||
|
toBlockSR: '*github.com/nspcc-dev/neo-go/pkg/interop/native/ledger.BlockSR'
|
||||||
|
verify: bool
|
||||||
|
namedtypes:
|
||||||
|
ledger.Block:
|
||||||
|
base: Array
|
||||||
|
name: ledger.Block
|
||||||
|
fields:
|
||||||
|
- field: Hash
|
||||||
|
base: Hash256
|
||||||
|
- field: Version
|
||||||
|
base: Integer
|
||||||
|
- field: PrevHash
|
||||||
|
base: Hash256
|
||||||
|
- field: MerkleRoot
|
||||||
|
base: Hash256
|
||||||
|
- field: Timestamp
|
||||||
|
base: Integer
|
||||||
|
- field: Nonce
|
||||||
|
base: Integer
|
||||||
|
- field: Index
|
||||||
|
base: Integer
|
||||||
|
- field: NextConsensus
|
||||||
|
base: Hash160
|
||||||
|
- field: TransactionsLength
|
||||||
|
base: Integer
|
||||||
|
ledger.BlockSR:
|
||||||
|
base: Array
|
||||||
|
name: ledger.BlockSR
|
||||||
|
fields:
|
||||||
|
- field: Hash
|
||||||
|
base: Hash256
|
||||||
|
- field: Version
|
||||||
|
base: Integer
|
||||||
|
- field: PrevHash
|
||||||
|
base: Hash256
|
||||||
|
- field: MerkleRoot
|
||||||
|
base: Hash256
|
||||||
|
- field: Timestamp
|
||||||
|
base: Integer
|
||||||
|
- field: Nonce
|
||||||
|
base: Integer
|
||||||
|
- field: Index
|
||||||
|
base: Integer
|
||||||
|
- field: NextConsensus
|
||||||
|
base: Hash160
|
||||||
|
- field: TransactionsLength
|
||||||
|
base: Integer
|
||||||
|
- field: PrevStateRoot
|
||||||
|
base: Hash256
|
||||||
|
ledger.Transaction:
|
||||||
|
base: Array
|
||||||
|
name: ledger.Transaction
|
||||||
|
fields:
|
||||||
|
- field: Hash
|
||||||
|
base: Hash256
|
||||||
|
- field: Version
|
||||||
|
base: Integer
|
||||||
|
- field: Nonce
|
||||||
|
base: Integer
|
||||||
|
- field: Sender
|
||||||
|
base: Hash160
|
||||||
|
- field: SysFee
|
||||||
|
base: Integer
|
||||||
|
- field: NetFee
|
||||||
|
base: Integer
|
||||||
|
- field: ValidUntilBlock
|
||||||
|
base: Integer
|
||||||
|
- field: Script
|
||||||
|
base: ByteArray
|
||||||
|
ledger.TransactionSigner:
|
||||||
|
base: Array
|
||||||
|
name: ledger.TransactionSigner
|
||||||
|
fields:
|
||||||
|
- field: Account
|
||||||
|
base: Hash160
|
||||||
|
- field: Scopes
|
||||||
|
base: Integer
|
||||||
|
- field: AllowedContracts
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Hash160
|
||||||
|
- field: AllowedGroups
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: PublicKey
|
||||||
|
- field: Rules
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Array
|
||||||
|
name: ledger.WitnessRule
|
||||||
|
ledger.WitnessCondition:
|
||||||
|
base: Array
|
||||||
|
name: ledger.WitnessCondition
|
||||||
|
fields:
|
||||||
|
- field: Type
|
||||||
|
base: Integer
|
||||||
|
- field: Value
|
||||||
|
base: Any
|
||||||
|
ledger.WitnessRule:
|
||||||
|
base: Array
|
||||||
|
name: ledger.WitnessRule
|
||||||
|
fields:
|
||||||
|
- field: Action
|
||||||
|
base: Integer
|
||||||
|
- field: Condition
|
||||||
|
base: Array
|
||||||
|
name: ledger.WitnessCondition
|
||||||
|
types:
|
||||||
|
call.args:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Any
|
||||||
|
call.f:
|
||||||
|
base: InteropInterface
|
||||||
|
interface: iterator
|
||||||
|
callWithToken.args:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Any
|
||||||
|
callWithTokenNoRet.args:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Any
|
||||||
|
createMultisigAccount.pubs:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: PublicKey
|
||||||
|
currentSigners:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Array
|
||||||
|
name: ledger.TransactionSigner
|
||||||
|
getBlock:
|
||||||
|
base: Array
|
||||||
|
name: ledger.Block
|
||||||
|
getCallFlags:
|
||||||
|
base: InteropInterface
|
||||||
|
interface: iterator
|
||||||
|
getNotifications:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Any
|
||||||
|
getScriptContainer:
|
||||||
|
base: Array
|
||||||
|
name: ledger.Transaction
|
||||||
|
getTransaction:
|
||||||
|
base: Array
|
||||||
|
name: ledger.Transaction
|
||||||
|
getTransactionFromBlock:
|
||||||
|
base: Array
|
||||||
|
name: ledger.Transaction
|
||||||
|
getTransactionSigners:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Array
|
||||||
|
name: ledger.TransactionSigner
|
||||||
|
loadScript.args:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Any
|
||||||
|
loadScript.f:
|
||||||
|
base: InteropInterface
|
||||||
|
interface: iterator
|
||||||
|
notify.args:
|
||||||
|
base: Array
|
||||||
|
value:
|
||||||
|
base: Any
|
||||||
|
toBlockSR:
|
||||||
|
base: Array
|
||||||
|
name: ledger.BlockSR
|
1
cli/testdata/wallet1_solo.json
vendored
1
cli/testdata/wallet1_solo.json
vendored
|
@ -65,7 +65,6 @@
|
||||||
"key": "6PYSATFztBa3CHjSR6sLAKungUEAbQUCVE16KzmaQQ38gLeYGZ9Knd5mGv",
|
"key": "6PYSATFztBa3CHjSR6sLAKungUEAbQUCVE16KzmaQQ38gLeYGZ9Knd5mGv",
|
||||||
"label": "verify",
|
"label": "verify",
|
||||||
"contract": {
|
"contract": {
|
||||||
"script": "VwEAEUBXAANA",
|
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"deployed": true
|
"deployed": true
|
||||||
},
|
},
|
||||||
|
|
|
@ -52,7 +52,7 @@ func cancelTx(ctx *cli.Context) error {
|
||||||
|
|
||||||
mainTx, _ := c.GetRawTransactionVerbose(txHash)
|
mainTx, _ := c.GetRawTransactionVerbose(txHash)
|
||||||
if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) {
|
if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) {
|
||||||
return cli.NewExitError(fmt.Errorf("transaction %s is already accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1)
|
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) {
|
if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) {
|
||||||
|
@ -76,10 +76,7 @@ func cancelTx(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to send conflicting transaction: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to send conflicting transaction: %w", err), 1)
|
||||||
}
|
}
|
||||||
var (
|
var res *state.AppExecResult
|
||||||
acceptedH = resHash
|
|
||||||
res *state.AppExecResult
|
|
||||||
)
|
|
||||||
if ctx.Bool("await") {
|
if ctx.Bool("await") {
|
||||||
res, err = a.WaitAny(gctx, resVub, txHash, resHash)
|
res, err = a.WaitAny(gctx, resVub, txHash, resHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,12 +90,14 @@ func cancelTx(ctx *cli.Context) error {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1)
|
||||||
}
|
}
|
||||||
if txHash.Equals(res.Container) {
|
if txHash.Equals(res.Container) {
|
||||||
fmt.Fprintln(ctx.App.Writer, "Target transaction accepted")
|
tx, err := c.GetRawTransactionVerbose(txHash)
|
||||||
acceptedH = txHash
|
if err != nil {
|
||||||
} else {
|
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted", txHash), 1)
|
||||||
fmt.Fprintln(ctx.App.Writer, "Conflicting transaction accepted")
|
}
|
||||||
|
return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, tx.Blockhash.StringLE()), 1)
|
||||||
}
|
}
|
||||||
|
fmt.Fprintln(ctx.App.Writer, "Conflicting transaction accepted")
|
||||||
}
|
}
|
||||||
txctx.DumpTransactionInfo(ctx.App.Writer, acceptedH, res)
|
txctx.DumpTransactionInfo(ctx.App.Writer, resHash, res)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,8 @@ func NewCommands() []cli.Command {
|
||||||
NetworkFee calculations are performed based on the calculatenetworkfee RPC request. If the --gas
|
NetworkFee calculations are performed based on the calculatenetworkfee RPC request. If the --gas
|
||||||
flag is included, the specified value is added to the resulting conflicting transaction network fee
|
flag is included, the specified value is added to the resulting conflicting transaction network fee
|
||||||
in both scenarios. When the --await flag is included, the command waits for one of the conflicting
|
in both scenarios. When the --await flag is included, the command waits for one of the conflicting
|
||||||
or target transactions to be included in a block.`,
|
or target transactions to be included in a block.
|
||||||
|
`,
|
||||||
Action: cancelTx,
|
Action: cancelTx,
|
||||||
Flags: txCancelFlags,
|
Flags: txCancelFlags,
|
||||||
},
|
},
|
||||||
|
@ -83,7 +84,8 @@ func NewCommands() []cli.Command {
|
||||||
the output. This command expects a ContractParametersContext JSON file for input, it can't handle
|
the output. This command expects a ContractParametersContext JSON file for input, it can't handle
|
||||||
binary (or hex- or base64-encoded) transactions. If --rpc-endpoint flag is specified the result
|
binary (or hex- or base64-encoded) transactions. If --rpc-endpoint flag is specified the result
|
||||||
of the given script after running it true the VM will be printed. Otherwise only transaction will
|
of the given script after running it true the VM will be printed. Otherwise only transaction will
|
||||||
be printed.`,
|
be printed.
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "ops",
|
Name: "ops",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package util_test
|
package util_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -10,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/internal/testcli"
|
"github.com/nspcc-dev/neo-go/internal/testcli"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -168,24 +170,23 @@ func TestAwaitUtilCancelTx(t *testing.T) {
|
||||||
_, ok := e.Chain.GetMemPool().TryGetValue(txHash)
|
_, ok := e.Chain.GetMemPool().TryGetValue(txHash)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
|
// Allow both cases: either target or conflicting tx acceptance.
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
e.Run(t, append(args, txHash.StringLE())...)
|
err = e.RunUnchecked(t, append(args, txHash.StringLE())...)
|
||||||
|
switch {
|
||||||
response := e.GetNextLine(t)
|
case err == nil:
|
||||||
if strings.Contains(response, "Conflicting transaction accepted") {
|
response := e.GetNextLine(t)
|
||||||
|
require.Equal(t, "Conflicting transaction accepted", response)
|
||||||
resHash, _ := e.CheckAwaitableTxPersisted(t)
|
resHash, _ := e.CheckAwaitableTxPersisted(t)
|
||||||
require.Eventually(t, func() bool {
|
require.NotEqual(t, resHash, txHash)
|
||||||
_, aerErr := e.Chain.GetAppExecResults(resHash.Hash(), trigger.Application)
|
case strings.Contains(err.Error(), fmt.Sprintf("target transaction %s is accepted", txHash)) ||
|
||||||
return aerErr == nil
|
strings.Contains(err.Error(), fmt.Sprintf("failed to send conflicting transaction: Invalid transaction attribute (-507) - invalid attribute: conflicting transaction %s is already on chain", txHash)):
|
||||||
}, time.Second*2, time.Millisecond*50)
|
tx, _ := e.GetTransaction(t, txHash)
|
||||||
} else if strings.Contains(response, "Target transaction accepted") {
|
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
require.Eventually(t, func() bool {
|
require.NoError(t, err)
|
||||||
_, _, err := e.Chain.GetTransaction(txHash)
|
require.Equal(t, 1, len(aer))
|
||||||
require.NoError(t, err, "original transaction should be on chain")
|
require.Equal(t, vmstate.Halt, aer[0].VMState)
|
||||||
_, aerErr := e.Chain.GetAppExecResults(txHash, trigger.Application)
|
default:
|
||||||
return aerErr == nil
|
t.Fatal(fmt.Errorf("unexpected error: %w", err))
|
||||||
}, time.Second*2, time.Millisecond*50)
|
|
||||||
} else {
|
|
||||||
t.Fatalf("unexpected response: %s", response)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1105,7 +1105,7 @@ func handleRun(c *cli.Context) error {
|
||||||
breaks := v.Context().BreakPoints() // We ensure that there's a context loaded.
|
breaks := v.Context().BreakPoints() // We ensure that there's a context loaded.
|
||||||
ic.ReuseVM(v)
|
ic.ReuseVM(v)
|
||||||
v.GasLimit = gasLimit
|
v.GasLimit = gasLimit
|
||||||
v.LoadNEFMethod(&cs.NEF, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
|
v.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, cs.Hash, callflag.All, hasRet, offset, initOff, nil)
|
||||||
for _, bp := range breaks {
|
for _, bp := range breaks {
|
||||||
v.AddBreakPoint(bp)
|
v.AddBreakPoint(bp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,10 @@ func newTestVMClIWithState(t *testing.T) *executor {
|
||||||
}
|
}
|
||||||
bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, store)
|
bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, store)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Save config for future usage.
|
||||||
|
protoCfg := bc.GetConfig()
|
||||||
|
|
||||||
go bc.Run()
|
go bc.Run()
|
||||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
basicchain.InitSimple(t, "../../", e)
|
basicchain.InitSimple(t, "../../", e)
|
||||||
|
@ -145,7 +149,9 @@ func newTestVMClIWithState(t *testing.T) *executor {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
|
cfg.ApplicationConfiguration.DBConfiguration.Type = dbconfig.LevelDB
|
||||||
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions = opts
|
cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions = opts
|
||||||
cfg.ProtocolConfiguration.StateRootInHeader = true
|
cfg.ProtocolConfiguration.StateRootInHeader = protoCfg.StateRootInHeader
|
||||||
|
cfg.ProtocolConfiguration.P2PStateExchangeExtensions = protoCfg.P2PStateExchangeExtensions
|
||||||
|
cfg.ProtocolConfiguration.Hardforks = protoCfg.Hardforks
|
||||||
return newTestVMCLIWithLogoAndCustomConfig(t, false, &cfg)
|
return newTestVMCLIWithLogoAndCustomConfig(t, false, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package wallet_test
|
package wallet_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -18,7 +17,7 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
|
|
||||||
validatorAddress := testcli.ValidatorPriv.Address()
|
validatorAddress := testcli.ValidatorPriv.Address()
|
||||||
validatorPublic := testcli.ValidatorPriv.PublicKey()
|
validatorPublic := testcli.ValidatorPriv.PublicKey()
|
||||||
validatorHex := hex.EncodeToString(validatorPublic.Bytes())
|
validatorHex := validatorPublic.StringCompressed()
|
||||||
|
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package wallet_test
|
package wallet_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
@ -45,9 +44,9 @@ func TestSignMultisigTx(t *testing.T) {
|
||||||
"--wallet", w,
|
"--wallet", w,
|
||||||
"--wif", wif,
|
"--wif", wif,
|
||||||
"--min", "2",
|
"--min", "2",
|
||||||
hex.EncodeToString(pubs[0].Bytes()),
|
pubs[0].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[1].Bytes()),
|
pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[2].Bytes()))
|
pubs[2].StringCompressed())
|
||||||
}
|
}
|
||||||
addAccount(wallet1Path, privs[0].WIF())
|
addAccount(wallet1Path, privs[0].WIF())
|
||||||
addAccount(wallet2Path, privs[1].WIF())
|
addAccount(wallet2Path, privs[1].WIF())
|
||||||
|
|
|
@ -651,7 +651,8 @@ func importDeployed(ctx *cli.Context) error {
|
||||||
return cli.NewExitError("contract has no `verify` method with boolean return", 1)
|
return cli.NewExitError("contract has no `verify` method with boolean return", 1)
|
||||||
}
|
}
|
||||||
acc.Address = address.Uint160ToString(cs.Hash)
|
acc.Address = address.Uint160ToString(cs.Hash)
|
||||||
acc.Contract.Script = cs.NEF.Script
|
// Explicitly overwrite single signature script of the provided WIF since the contract is known to be deployed.
|
||||||
|
acc.Contract.Script = nil
|
||||||
acc.Contract.Parameters = acc.Contract.Parameters[:0]
|
acc.Contract.Parameters = acc.Contract.Parameters[:0]
|
||||||
for _, p := range md.Parameters {
|
for _, p := range md.Parameters {
|
||||||
acc.Contract.Parameters = append(acc.Contract.Parameters, wallet.ContractParam{
|
acc.Contract.Parameters = append(acc.Contract.Parameters, wallet.ContractParam{
|
||||||
|
@ -948,14 +949,14 @@ func createAccount(wall *wallet.Wallet, pass *string) error {
|
||||||
func openWallet(ctx *cli.Context, canUseWalletConfig bool) (*wallet.Wallet, *string, error) {
|
func openWallet(ctx *cli.Context, canUseWalletConfig bool) (*wallet.Wallet, *string, error) {
|
||||||
path, pass, err := getWalletPathAndPass(ctx, canUseWalletConfig)
|
path, pass, err := getWalletPathAndPass(ctx, canUseWalletConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, cli.NewExitError(fmt.Errorf("failed to get wallet path or password: %w", err), 1)
|
||||||
}
|
}
|
||||||
if path == "-" {
|
if path == "-" {
|
||||||
return nil, nil, errNoStdin
|
return nil, nil, errNoStdin
|
||||||
}
|
}
|
||||||
w, err := wallet.NewWalletFromFile(path)
|
w, err := wallet.NewWalletFromFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, cli.NewExitError(fmt.Errorf("failed to read wallet: %w", err), 1)
|
||||||
}
|
}
|
||||||
return w, pass, nil
|
return w, pass, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package wallet_test
|
package wallet_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
@ -437,16 +436,16 @@ func TestWalletInit(t *testing.T) {
|
||||||
"--wallet", walletPath,
|
"--wallet", walletPath,
|
||||||
"--min", "2"}
|
"--min", "2"}
|
||||||
t.Run("invalid pub encoding", func(t *testing.T) {
|
t.Run("invalid pub encoding", func(t *testing.T) {
|
||||||
e.RunWithError(t, append(cmd, hex.EncodeToString(pubs[1].Bytes()),
|
e.RunWithError(t, append(cmd, pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[1].Bytes()),
|
pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[2].Bytes()),
|
pubs[2].StringCompressed(),
|
||||||
"not-a-pub")...)
|
"not-a-pub")...)
|
||||||
})
|
})
|
||||||
t.Run("missing WIF", func(t *testing.T) {
|
t.Run("missing WIF", func(t *testing.T) {
|
||||||
e.RunWithError(t, append(cmd, hex.EncodeToString(pubs[0].Bytes()),
|
e.RunWithError(t, append(cmd, pubs[0].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[1].Bytes()),
|
pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[2].Bytes()),
|
pubs[2].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[3].Bytes()))...)
|
pubs[3].StringCompressed())...)
|
||||||
})
|
})
|
||||||
cmd = append(cmd, "--wif", privs[0].WIF())
|
cmd = append(cmd, "--wif", privs[0].WIF())
|
||||||
t.Run("InvalidPublicKeys", func(t *testing.T) {
|
t.Run("InvalidPublicKeys", func(t *testing.T) {
|
||||||
|
@ -455,18 +454,18 @@ func TestWalletInit(t *testing.T) {
|
||||||
e.In.WriteString("multipass\r")
|
e.In.WriteString("multipass\r")
|
||||||
defer e.In.Reset()
|
defer e.In.Reset()
|
||||||
|
|
||||||
e.RunWithError(t, append(cmd, hex.EncodeToString(pubs[1].Bytes()),
|
e.RunWithError(t, append(cmd, pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[1].Bytes()),
|
pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[2].Bytes()),
|
pubs[2].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[3].Bytes()))...)
|
pubs[3].StringCompressed())...)
|
||||||
})
|
})
|
||||||
e.In.WriteString("multiacc\r")
|
e.In.WriteString("multiacc\r")
|
||||||
e.In.WriteString("multipass\r")
|
e.In.WriteString("multipass\r")
|
||||||
e.In.WriteString("multipass\r")
|
e.In.WriteString("multipass\r")
|
||||||
e.Run(t, append(cmd, hex.EncodeToString(pubs[0].Bytes()),
|
e.Run(t, append(cmd, pubs[0].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[1].Bytes()),
|
pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[2].Bytes()),
|
pubs[2].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[3].Bytes()))...)
|
pubs[3].StringCompressed())...)
|
||||||
|
|
||||||
script, err := smartcontract.CreateMultiSigRedeemScript(2, pubs)
|
script, err := smartcontract.CreateMultiSigRedeemScript(2, pubs)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -482,10 +481,10 @@ func TestWalletInit(t *testing.T) {
|
||||||
e.In.WriteString("multiacc\r")
|
e.In.WriteString("multiacc\r")
|
||||||
e.In.WriteString("multipass\r")
|
e.In.WriteString("multipass\r")
|
||||||
e.In.WriteString("multipass\r")
|
e.In.WriteString("multipass\r")
|
||||||
e.RunWithError(t, append(cmd, hex.EncodeToString(pubs[0].Bytes()),
|
e.RunWithError(t, append(cmd, pubs[0].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[1].Bytes()),
|
pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[2].Bytes()),
|
pubs[2].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[3].Bytes()))...)
|
pubs[3].StringCompressed())...)
|
||||||
})
|
})
|
||||||
|
|
||||||
privs, pubs = testcli.GenerateKeys(t, 3)
|
privs, pubs = testcli.GenerateKeys(t, 3)
|
||||||
|
@ -508,9 +507,9 @@ func TestWalletInit(t *testing.T) {
|
||||||
e.Run(t, "neo-go", "wallet", "import-multisig",
|
e.Run(t, "neo-go", "wallet", "import-multisig",
|
||||||
"--wallet", walletPath,
|
"--wallet", walletPath,
|
||||||
"--min", "2",
|
"--min", "2",
|
||||||
hex.EncodeToString(pubs[0].Bytes()), // Public key of the already imported account
|
pubs[0].StringCompressed(), // Public key of the already imported account
|
||||||
hex.EncodeToString(pubs[1].Bytes()),
|
pubs[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubs[2].Bytes()))
|
pubs[2].StringCompressed())
|
||||||
|
|
||||||
w, err := wallet.NewWalletFromFile(walletPath)
|
w, err := wallet.NewWalletFromFile(walletPath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -530,9 +529,9 @@ func TestWalletInit(t *testing.T) {
|
||||||
e.RunWithError(t, "neo-go", "wallet", "import-multisig",
|
e.RunWithError(t, "neo-go", "wallet", "import-multisig",
|
||||||
"--wallet", walletPath,
|
"--wallet", walletPath,
|
||||||
"--min", "2",
|
"--min", "2",
|
||||||
hex.EncodeToString(pubsNew[0].Bytes()),
|
pubsNew[0].StringCompressed(),
|
||||||
hex.EncodeToString(pubsNew[1].Bytes()),
|
pubsNew[1].StringCompressed(),
|
||||||
hex.EncodeToString(pubsNew[2].Bytes()))
|
pubsNew[2].StringCompressed())
|
||||||
|
|
||||||
w, err := wallet.NewWalletFromFile(walletPath)
|
w, err := wallet.NewWalletFromFile(walletPath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -26,6 +26,7 @@ ProtocolConfiguration:
|
||||||
Hardforks:
|
Hardforks:
|
||||||
Aspidochelone: 3000000
|
Aspidochelone: 3000000
|
||||||
Basilisk: 4500000
|
Basilisk: 4500000
|
||||||
|
Cockatrice: 5800000
|
||||||
|
|
||||||
ApplicationConfiguration:
|
ApplicationConfiguration:
|
||||||
SkipBlockVerification: false
|
SkipBlockVerification: false
|
||||||
|
|
|
@ -38,6 +38,7 @@ ProtocolConfiguration:
|
||||||
Hardforks:
|
Hardforks:
|
||||||
Aspidochelone: 1730000
|
Aspidochelone: 1730000
|
||||||
Basilisk: 4120000
|
Basilisk: 4120000
|
||||||
|
Cockatrice: 5450000
|
||||||
|
|
||||||
ApplicationConfiguration:
|
ApplicationConfiguration:
|
||||||
SkipBlockVerification: false
|
SkipBlockVerification: false
|
||||||
|
|
|
@ -41,6 +41,7 @@ ProtocolConfiguration:
|
||||||
Hardforks:
|
Hardforks:
|
||||||
Aspidochelone: 210000
|
Aspidochelone: 210000
|
||||||
Basilisk: 2680000
|
Basilisk: 2680000
|
||||||
|
Cockatrice: 3967000
|
||||||
|
|
||||||
ApplicationConfiguration:
|
ApplicationConfiguration:
|
||||||
SkipBlockVerification: false
|
SkipBlockVerification: false
|
||||||
|
|
|
@ -332,7 +332,7 @@ protocol-related settings described in the table below.
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
|
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
|
||||||
| Genesis | [Genesis](#Genesis-Configuration) | none | The set of genesis block settings including NeoGo-specific protocol extensions that should be enabled at the genesis block or during native contracts initialisation. |
|
| Genesis | [Genesis](#Genesis-Configuration) | none | The set of genesis block settings including NeoGo-specific protocol extensions that should be enabled at the genesis block or during native contracts initialisation. |
|
||||||
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. It also includes [#3085](https://github.com/nspcc-dev/neo-go/pull/3085) (ported from the [reference](https://github.com/neo-project/neo/pull/2810)) that enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. |
|
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. It also includes [#3085](https://github.com/nspcc-dev/neo-go/pull/3085) (ported from the [reference](https://github.com/neo-project/neo/pull/2810)) that enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. <br>• `Cockatrice` represents hard-fork introduced in [#3402](https://github.com/nspcc-dev/neo-go/pull/3402) (ported from the [reference](https://github.com/neo-project/neo/pull/2942)). Initially it is introduced along with the ability to update native contracts. This hard-fork also includes a couple of new native smart contract APIs: `keccak256` of native CryptoLib contract introduced in [#3301](https://github.com/nspcc-dev/neo-go/pull/3301) (ported from the [reference](https://github.com/neo-project/neo/pull/2925)) and `getCommitteeAddress` of native NeoToken contract inctroduced in [#3362](https://github.com/nspcc-dev/neo-go/pull/3362) (ported from the [reference](https://github.com/neo-project/neo/pull/3154)). |
|
||||||
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
|
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
|
||||||
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |
|
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |
|
||||||
| MaxBlockSystemFee | `int64` | `900000000000` | Maximum overall transactions system fee per block. |
|
| MaxBlockSystemFee | `int64` | `900000000000` | Maximum overall transactions system fee per block. |
|
||||||
|
|
|
@ -388,7 +388,7 @@ subpackage with an example written in Go doc.
|
||||||
configuration (just a simple `RET` script by default), pack both transactions
|
configuration (just a simple `RET` script by default), pack both transactions
|
||||||
into a P2PNotaryRequest and submit it to the RPC node. It returns hashes of
|
into a P2PNotaryRequest and submit it to the RPC node. It returns hashes of
|
||||||
the main and fallback transactions as well as their `ValidUntilBlock` value.
|
the main and fallback transactions as well as their `ValidUntilBlock` value.
|
||||||
If you need more control over fallback transction use `Actor` options or
|
If you need more control over fallback transaction use `Actor` options or
|
||||||
[func (*Actor) SendRequest](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/rpcclient/notary#Actor.SendRequest)
|
[func (*Actor) SendRequest](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/rpcclient/notary#Actor.SendRequest)
|
||||||
API.
|
API.
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ These should run successfully:
|
||||||
* unit-tests
|
* unit-tests
|
||||||
* lint
|
* lint
|
||||||
* privnet with consensus nodes
|
* privnet with consensus nodes
|
||||||
* mainnet synchronization
|
* testnet and mainnet synchronization
|
||||||
|
* NeoFS testnet and mainnet synchronisation and containers compatibility tests
|
||||||
|
|
||||||
## Update CHANGELOG and ROADMAP
|
## Update CHANGELOG and ROADMAP
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/engine
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/events
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/iterator
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -4,7 +4,7 @@ go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689
|
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -200,8 +200,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h
|
||||||
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
||||||
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689 h1:WnEdGAQwaW0C8wnNnQZ+rM/JfFKZDSTOqwm8cS0TOdk=
|
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689 h1:WnEdGAQwaW0C8wnNnQZ+rM/JfFKZDSTOqwm8cS0TOdk=
|
||||||
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689/go.mod h1:x+wmcYqpZYJwLp1l/pHZrqNp3RSWlkMymWGDij3/OPo=
|
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689/go.mod h1:x+wmcYqpZYJwLp1l/pHZrqNp3RSWlkMymWGDij3/OPo=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/oracle
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/runtime
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/storage
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/timer
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/token
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -33,7 +33,7 @@ require (
|
||||||
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c // indirect
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c // indirect
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 // indirect
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d // indirect
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||||
|
|
|
@ -216,8 +216,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c/go.mod h
|
||||||
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
||||||
github.com/nspcc-dev/neo-go v0.103.1 h1:BfRBceHUu8jSc1KQy7CzmQ/pa+xzAmgcyteGf0/IGgM=
|
github.com/nspcc-dev/neo-go v0.103.1 h1:BfRBceHUu8jSc1KQy7CzmQ/pa+xzAmgcyteGf0/IGgM=
|
||||||
github.com/nspcc-dev/neo-go v0.103.1/go.mod h1:MD7MPiyshUwrE5n1/LzxeandbItaa/iLW/bJb6gNs/U=
|
github.com/nspcc-dev/neo-go v0.103.1/go.mod h1:MD7MPiyshUwrE5n1/LzxeandbItaa/iLW/bJb6gNs/U=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/zkp/xor
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
24
go.mod
24
go.mod
|
@ -14,10 +14,10 @@ require (
|
||||||
github.com/holiman/uint256 v1.2.4
|
github.com/holiman/uint256 v1.2.4
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/mr-tron/base58 v1.2.0
|
github.com/mr-tron/base58 v1.2.0
|
||||||
github.com/nspcc-dev/dbft v0.1.1-0.20240321205542-332ff86ba4c6
|
github.com/nspcc-dev/dbft v0.2.0
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.1
|
github.com/nspcc-dev/rfc6979 v0.2.1
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible
|
github.com/pierrec/lz4 v2.6.1+incompatible
|
||||||
github.com/pmezard/go-difflib v1.0.0
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
|
@ -51,24 +51,22 @@ require (
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
||||||
github.com/nspcc-dev/hrw v1.0.9 // indirect
|
github.com/nspcc-dev/hrw/v2 v2.0.1 // indirect
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 // indirect
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 // indirect
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0 // indirect
|
github.com/nspcc-dev/tzhash v1.7.2 // indirect
|
||||||
github.com/nspcc-dev/tzhash v1.7.0 // indirect
|
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.5.0 // indirect
|
||||||
github.com/prometheus/common v0.48.0 // indirect
|
github.com/prometheus/common v0.48.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/rs/zerolog v1.30.0 // indirect
|
github.com/rs/zerolog v1.30.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||||
golang.org/x/mod v0.16.0 // indirect
|
golang.org/x/mod v0.16.0 // indirect
|
||||||
golang.org/x/net v0.22.0 // indirect
|
golang.org/x/net v0.23.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||||
google.golang.org/grpc v1.57.1 // indirect
|
google.golang.org/grpc v1.62.0 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
rsc.io/tmplfunc v0.0.3 // indirect
|
rsc.io/tmplfunc v0.0.3 // indirect
|
||||||
)
|
)
|
||||||
|
|
55
go.sum
55
go.sum
|
@ -26,7 +26,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||||
|
@ -88,25 +87,22 @@ github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqky
|
||||||
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
|
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
|
||||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
github.com/nspcc-dev/dbft v0.1.1-0.20240321205542-332ff86ba4c6 h1:6rpKY0AvXQpL2ZpLTxGDJAM54ak6ziXghi4uLGUrSdg=
|
github.com/nspcc-dev/dbft v0.2.0 h1:sDwsQES600OSIMncV176t2SX5OvB14lzeOAyKFOkbMI=
|
||||||
github.com/nspcc-dev/dbft v0.1.1-0.20240321205542-332ff86ba4c6/go.mod h1:oFE6paSC/yfFh9mcNU6MheMGOYXK9+sPiRk3YMoz49o=
|
github.com/nspcc-dev/dbft v0.2.0/go.mod h1:oFE6paSC/yfFh9mcNU6MheMGOYXK9+sPiRk3YMoz49o=
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk=
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk=
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
|
||||||
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUOU=
|
||||||
github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU=
|
github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0/go.mod h1:DRIr0Ic1s+6QgdqmNFNLIqMqd7lNMJfYwkczlm1hDtM=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 h1:mdxtlSU2I4oVZ/7AXTLKyz8uUPbDWikZw4DM8gvrddA=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12/go.mod h1:JdsEM1qgNukrWqgOBDChcYp8oY4XUzidcKaxY4hNJvQ=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11/go.mod h1:W+ImTNRnSNMH8w43H1knCcIqwu7dLHePXtlJNZ7EFIs=
|
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM=
|
github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM=
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc=
|
github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc=
|
||||||
github.com/nspcc-dev/tzhash v1.7.0 h1:/+aL33NC7y5OIGnY2kYgjZt8mg7LVGFMdj/KAJLndnk=
|
github.com/nspcc-dev/tzhash v1.7.2 h1:iRXoa9TJqH/DQO7FFcqpq9BdruF9E7/xnFGlIghl5J4=
|
||||||
github.com/nspcc-dev/tzhash v1.7.0/go.mod h1:Dnx9LUlOLr5paL2Rtc96x0PPs8D9eIkUtowt1n+KQus=
|
github.com/nspcc-dev/tzhash v1.7.2/go.mod h1:oHiH0qwmTsZkeVs7pvCS5cVXUaLhXxSFvnmnZ++ijm4=
|
||||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
@ -139,11 +135,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
|
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
|
||||||
|
@ -156,26 +147,25 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
||||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
||||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -207,10 +197,10 @@ golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 h1:eSaPbMR4T7WfH9FvABk36NBMacoTUKdWCvV0dx+KfOg=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||||
google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg=
|
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
|
||||||
google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
@ -230,7 +220,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
||||||
|
|
|
@ -196,7 +196,7 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) {
|
||||||
require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt))
|
require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt))
|
||||||
designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
|
||||||
int64(noderoles.P2PNotary), []any{ntr.Accounts[0].PublicKey().Bytes()})
|
int64(noderoles.P2PNotary), []any{ntr.Accounts[0].PublicKey().Bytes()})
|
||||||
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PublicKey().Bytes()))
|
t.Logf("Designated Notary node: %s", ntr.Accounts[0].PublicKey().StringCompressed())
|
||||||
|
|
||||||
// Block #10: push verification contract with arguments into the chain.
|
// Block #10: push verification contract with arguments into the chain.
|
||||||
verifyPath = filepath.Join(testDataPrefix, "verify_args", "verification_with_args_contract.go")
|
verifyPath = filepath.Join(testDataPrefix, "verify_args", "verification_with_args_contract.go")
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/internal/examples/oracle
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d h1:Vcb7YkZuUSSIC+WF/xV3UDfHbAxZgyT2zGleJP3Ig5k=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240521091047-78685785716d/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (*FakeChain) IsExtensibleAllowed(uint160 util.Uint160) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNatives implements the blockchainer.Blockchainer interface.
|
// GetNatives implements the blockchainer.Blockchainer interface.
|
||||||
func (*FakeChain) GetNatives() []state.NativeContract {
|
func (*FakeChain) GetNatives() []state.Contract {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -286,6 +286,20 @@ func (e *Executor) Run(t *testing.T, args ...string) {
|
||||||
require.NoError(t, e.run(args...))
|
require.NoError(t, e.run(args...))
|
||||||
checkExit(t, ch, 0)
|
checkExit(t, ch, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunUnchecked runs command and ensures that proper exit code is set (0 if no error is returned, 1 is an error is returned).
|
||||||
|
// The resulting error is returned (if so).
|
||||||
|
func (e *Executor) RunUnchecked(t *testing.T, args ...string) error {
|
||||||
|
ch := setExitFunc()
|
||||||
|
err := e.run(args...)
|
||||||
|
if err != nil {
|
||||||
|
checkExit(t, ch, 1)
|
||||||
|
} else {
|
||||||
|
checkExit(t, ch, 0)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) run(args ...string) error {
|
func (e *Executor) run(args ...string) error {
|
||||||
e.Out.Reset()
|
e.Out.Reset()
|
||||||
e.Err.Reset()
|
e.Err.Reset()
|
||||||
|
|
|
@ -522,6 +522,19 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit defaults for named returns.
|
||||||
|
if decl.Type.Results.NumFields() != 0 {
|
||||||
|
for _, arg := range decl.Type.Results.List {
|
||||||
|
for _, id := range arg.Names {
|
||||||
|
if id.Name != "_" {
|
||||||
|
i := c.scope.newLocal(id.Name)
|
||||||
|
c.emitDefault(c.typeOf(arg.Type))
|
||||||
|
c.emitStoreByIndex(varLocal, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ast.Walk(c, decl.Body)
|
ast.Walk(c, decl.Body)
|
||||||
|
|
||||||
// If we have reached the end of the function without encountering `return` statement,
|
// If we have reached the end of the function without encountering `return` statement,
|
||||||
|
@ -763,7 +776,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
for i := len(results.List) - 1; i >= 0; i-- {
|
for i := len(results.List) - 1; i >= 0; i-- {
|
||||||
names := results.List[i].Names
|
names := results.List[i].Names
|
||||||
for j := len(names) - 1; j >= 0; j-- {
|
for j := len(names) - 1; j >= 0; j-- {
|
||||||
c.emitLoadVar("", names[j].Name)
|
if names[j].Name == "_" {
|
||||||
|
c.emitDefault(c.typeOf(results.List[i].Type))
|
||||||
|
} else {
|
||||||
|
c.emitLoadVar("", names[j].Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2396,7 +2413,7 @@ func (c *codegen) writeJumps(b []byte) ([]byte, error) {
|
||||||
for _, f := range c.funcs {
|
for _, f := range c.funcs {
|
||||||
f.rng.Start, f.rng.End = correctRange(f.rng.Start, f.rng.End, nopOffsets)
|
f.rng.Start, f.rng.End = correctRange(f.rng.Start, f.rng.End, nopOffsets)
|
||||||
}
|
}
|
||||||
return removeNOPs(b, nopOffsets), nil
|
return removeNOPs(b, nopOffsets, c.sequencePoints), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func correctRange(start, end uint16, offsets []int) (uint16, uint16) {
|
func correctRange(start, end uint16, offsets []int) (uint16, uint16) {
|
||||||
|
@ -2438,9 +2455,10 @@ func (c *codegen) replaceLabelWithOffset(ip int, arg []byte) (int, error) {
|
||||||
// This is done in 2 passes:
|
// This is done in 2 passes:
|
||||||
// 1. Alter jump offsets taking into account parts to be removed.
|
// 1. Alter jump offsets taking into account parts to be removed.
|
||||||
// 2. Perform actual removal of jump targets.
|
// 2. Perform actual removal of jump targets.
|
||||||
|
// 3. Reevaluate debug sequence points offsets.
|
||||||
// Note: after jump offsets altering, there can appear new candidates for conversion.
|
// Note: after jump offsets altering, there can appear new candidates for conversion.
|
||||||
// These are ignored for now.
|
// These are ignored for now.
|
||||||
func removeNOPs(b []byte, nopOffsets []int) []byte {
|
func removeNOPs(b []byte, nopOffsets []int, sequencePoints map[string][]DebugSeqPoint) []byte {
|
||||||
if len(nopOffsets) == 0 {
|
if len(nopOffsets) == 0 {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
@ -2502,6 +2520,20 @@ func removeNOPs(b []byte, nopOffsets []int) []byte {
|
||||||
copy(b[start-copyOffset:], b[start+1:end])
|
copy(b[start-copyOffset:], b[start+1:end])
|
||||||
copyOffset++
|
copyOffset++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. Reevaluate debug sequence points.
|
||||||
|
for _, seqPoints := range sequencePoints {
|
||||||
|
for i := range seqPoints {
|
||||||
|
diff := 0
|
||||||
|
for _, offset := range nopOffsets {
|
||||||
|
if offset < seqPoints[i].Opcode {
|
||||||
|
diff++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seqPoints[i].Opcode -= diff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return b[:len(b)-copyOffset]
|
return b[:len(b)-copyOffset]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"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/core/transaction"
|
||||||
|
@ -610,7 +610,7 @@ func TestCallWithVersion(t *testing.T) {
|
||||||
e.DeployContract(t, ctr, nil)
|
e.DeployContract(t, ctr, nil)
|
||||||
c := e.CommitteeInvoker(ctr.Hash)
|
c := e.CommitteeInvoker(ctr.Hash)
|
||||||
|
|
||||||
policyH := state.CreateNativeContractHash(nativenames.Policy)
|
policyH := nativehashes.PolicyContract
|
||||||
t.Run("good", func(t *testing.T) {
|
t.Run("good", func(t *testing.T) {
|
||||||
c.Invoke(t, e.Chain.GetBaseExecFee(), "callWithVersion", policyH.BytesBE(), 0, "getExecFeeFactor")
|
c.Invoke(t, e.Chain.GetBaseExecFee(), "callWithVersion", policyH.BytesBE(), 0, "getExecFeeFactor")
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,16 +7,17 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testShortenJumps(t *testing.T, before, after []opcode.Opcode, indices []int) {
|
func testShortenJumps(t *testing.T, before, after []opcode.Opcode, indices []int, spBefore, spAfter map[string][]DebugSeqPoint) {
|
||||||
prog := make([]byte, len(before))
|
prog := make([]byte, len(before))
|
||||||
for i := range before {
|
for i := range before {
|
||||||
prog[i] = byte(before[i])
|
prog[i] = byte(before[i])
|
||||||
}
|
}
|
||||||
raw := removeNOPs(prog, indices)
|
raw := removeNOPs(prog, indices, spBefore)
|
||||||
actual := make([]opcode.Opcode, len(raw))
|
actual := make([]opcode.Opcode, len(raw))
|
||||||
for i := range raw {
|
for i := range raw {
|
||||||
actual[i] = opcode.Opcode(raw[i])
|
actual[i] = opcode.Opcode(raw[i])
|
||||||
}
|
}
|
||||||
|
require.Equal(t, spAfter, spBefore)
|
||||||
require.Equal(t, after, actual)
|
require.Equal(t, after, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,18 +46,32 @@ func TestShortenJumps(t *testing.T) {
|
||||||
op, 3, 12, 0, 0, opcode.PUSH1, opcode.NOP,
|
op, 3, 12, 0, 0, opcode.PUSH1, opcode.NOP,
|
||||||
sop, 249, sop, 0xFF - 2,
|
sop, 249, sop, 0xFF - 2,
|
||||||
}
|
}
|
||||||
testShortenJumps(t, before, after, []int{2, 3, 4, 16, 17, 18, 21, 22, 23})
|
spBefore := map[string][]DebugSeqPoint{
|
||||||
|
"test": {
|
||||||
|
DebugSeqPoint{Opcode: 0}, DebugSeqPoint{Opcode: 5},
|
||||||
|
DebugSeqPoint{Opcode: 7}, DebugSeqPoint{Opcode: 12},
|
||||||
|
DebugSeqPoint{Opcode: 14}, DebugSeqPoint{Opcode: 19},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spAfter := map[string][]DebugSeqPoint{
|
||||||
|
"test": {
|
||||||
|
DebugSeqPoint{Opcode: 0}, DebugSeqPoint{Opcode: 2},
|
||||||
|
DebugSeqPoint{Opcode: 4}, DebugSeqPoint{Opcode: 9},
|
||||||
|
DebugSeqPoint{Opcode: 11}, DebugSeqPoint{Opcode: 13},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testShortenJumps(t, before, after, []int{2, 3, 4, 16, 17, 18, 21, 22, 23}, spBefore, spAfter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
t.Run("NoReplace", func(t *testing.T) {
|
t.Run("NoReplace", func(t *testing.T) {
|
||||||
b := []byte{0, 1, 2, 3, 4, 5}
|
b := []byte{0, 1, 2, 3, 4, 5}
|
||||||
expected := []byte{0, 1, 2, 3, 4, 5}
|
expected := []byte{0, 1, 2, 3, 4, 5}
|
||||||
require.Equal(t, expected, removeNOPs(b, nil))
|
require.Equal(t, expected, removeNOPs(b, nil, map[string][]DebugSeqPoint{}))
|
||||||
})
|
})
|
||||||
t.Run("InvalidIndex", func(t *testing.T) {
|
t.Run("InvalidIndex", func(t *testing.T) {
|
||||||
before := []byte{byte(opcode.PUSH1), 0, 0, 0, 0}
|
before := []byte{byte(opcode.PUSH1), 0, 0, 0, 0}
|
||||||
require.Panics(t, func() {
|
require.Panics(t, func() {
|
||||||
removeNOPs(before, []int{0})
|
removeNOPs(before, []int{0}, map[string][]DebugSeqPoint{})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("SideConditions", func(t *testing.T) {
|
t.Run("SideConditions", func(t *testing.T) {
|
||||||
|
@ -69,7 +84,19 @@ func TestShortenJumps(t *testing.T) {
|
||||||
opcode.JMP, 2,
|
opcode.JMP, 2,
|
||||||
opcode.JMP, 2,
|
opcode.JMP, 2,
|
||||||
}
|
}
|
||||||
testShortenJumps(t, before, after, []int{2, 3, 4, 7, 8, 9})
|
spBefore := map[string][]DebugSeqPoint{
|
||||||
|
"test": {
|
||||||
|
DebugSeqPoint{Opcode: 0},
|
||||||
|
DebugSeqPoint{Opcode: 5},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spAfter := map[string][]DebugSeqPoint{
|
||||||
|
"test": {
|
||||||
|
DebugSeqPoint{Opcode: 0},
|
||||||
|
DebugSeqPoint{Opcode: 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testShortenJumps(t, before, after, []int{2, 3, 4, 7, 8, 9}, spBefore, spAfter)
|
||||||
})
|
})
|
||||||
t.Run("Backwards", func(t *testing.T) {
|
t.Run("Backwards", func(t *testing.T) {
|
||||||
before := []opcode.Opcode{
|
before := []opcode.Opcode{
|
||||||
|
@ -82,7 +109,21 @@ func TestShortenJumps(t *testing.T) {
|
||||||
opcode.JMP, 0xFF - 1,
|
opcode.JMP, 0xFF - 1,
|
||||||
opcode.JMP, 0xFF - 1,
|
opcode.JMP, 0xFF - 1,
|
||||||
}
|
}
|
||||||
testShortenJumps(t, before, after, []int{2, 3, 4, 7, 8, 9, 12, 13, 14})
|
spBefore := map[string][]DebugSeqPoint{
|
||||||
|
"test": {
|
||||||
|
DebugSeqPoint{Opcode: 0},
|
||||||
|
DebugSeqPoint{Opcode: 5},
|
||||||
|
DebugSeqPoint{Opcode: 10},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spAfter := map[string][]DebugSeqPoint{
|
||||||
|
"test": {
|
||||||
|
DebugSeqPoint{Opcode: 0},
|
||||||
|
DebugSeqPoint{Opcode: 2},
|
||||||
|
DebugSeqPoint{Opcode: 4},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testShortenJumps(t, before, after, []int{2, 3, 4, 7, 8, 9, 12, 13, 14}, spBefore, spAfter)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -100,6 +141,17 @@ func TestWriteJumps(t *testing.T) {
|
||||||
"main": {rng: DebugRange{Start: 4, End: 9}},
|
"main": {rng: DebugRange{Start: 4, End: 9}},
|
||||||
"method": {rng: DebugRange{Start: 10, End: 11}},
|
"method": {rng: DebugRange{Start: 10, End: 11}},
|
||||||
}
|
}
|
||||||
|
c.sequencePoints = map[string][]DebugSeqPoint{
|
||||||
|
"init": {
|
||||||
|
DebugSeqPoint{Opcode: 1}, DebugSeqPoint{Opcode: 3},
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
DebugSeqPoint{Opcode: 4}, DebugSeqPoint{Opcode: 9},
|
||||||
|
},
|
||||||
|
"method": {
|
||||||
|
DebugSeqPoint{Opcode: 10}, DebugSeqPoint{Opcode: 11},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expProg := []byte{
|
expProg := []byte{
|
||||||
byte(opcode.NOP), byte(opcode.JMP), 2, byte(opcode.RET),
|
byte(opcode.NOP), byte(opcode.JMP), 2, byte(opcode.RET),
|
||||||
|
@ -111,11 +163,23 @@ func TestWriteJumps(t *testing.T) {
|
||||||
"main": {rng: DebugRange{Start: 4, End: 6}},
|
"main": {rng: DebugRange{Start: 4, End: 6}},
|
||||||
"method": {rng: DebugRange{Start: 7, End: 8}},
|
"method": {rng: DebugRange{Start: 7, End: 8}},
|
||||||
}
|
}
|
||||||
|
expSeqPoints := map[string][]DebugSeqPoint{
|
||||||
|
"init": {
|
||||||
|
DebugSeqPoint{Opcode: 1}, DebugSeqPoint{Opcode: 3},
|
||||||
|
},
|
||||||
|
"main": {
|
||||||
|
DebugSeqPoint{Opcode: 4}, DebugSeqPoint{Opcode: 6},
|
||||||
|
},
|
||||||
|
"method": {
|
||||||
|
DebugSeqPoint{Opcode: 7}, DebugSeqPoint{Opcode: 8},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
buf, err := c.writeJumps(before)
|
buf, err := c.writeJumps(before)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expProg, buf)
|
require.Equal(t, expProg, buf)
|
||||||
require.Equal(t, expFuncs, c.funcs)
|
require.Equal(t, expFuncs, c.funcs)
|
||||||
|
require.Equal(t, expSeqPoints, c.sequencePoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteJumpsLastJump(t *testing.T) {
|
func TestWriteJumpsLastJump(t *testing.T) {
|
||||||
|
|
|
@ -74,8 +74,10 @@ func TestRoleManagementRole(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCryptoLibNamedCurve(t *testing.T) {
|
func TestCryptoLibNamedCurve(t *testing.T) {
|
||||||
require.EqualValues(t, native.Secp256k1, crypto.Secp256k1)
|
require.EqualValues(t, native.Secp256k1Sha256, crypto.Secp256k1Sha256)
|
||||||
require.EqualValues(t, native.Secp256r1, crypto.Secp256r1)
|
require.EqualValues(t, native.Secp256r1Sha256, crypto.Secp256r1Sha256)
|
||||||
|
require.EqualValues(t, native.Secp256k1Keccak256, crypto.Secp256k1Keccak256)
|
||||||
|
require.EqualValues(t, native.Secp256r1Keccak256, crypto.Secp256r1Keccak256)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOracleContractValues(t *testing.T) {
|
func TestOracleContractValues(t *testing.T) {
|
||||||
|
@ -233,7 +235,7 @@ func TestNativeHelpersCompile(t *testing.T) {
|
||||||
{"sha256", []string{"[]byte{1, 2, 3}"}},
|
{"sha256", []string{"[]byte{1, 2, 3}"}},
|
||||||
{"ripemd160", []string{"[]byte{1, 2, 3}"}},
|
{"ripemd160", []string{"[]byte{1, 2, 3}"}},
|
||||||
{"murmur32", []string{"[]byte{1, 2, 3}", "123"}},
|
{"murmur32", []string{"[]byte{1, 2, 3}", "123"}},
|
||||||
{"verifyWithECDsa", []string{"[]byte{1, 2, 3}", pub, sig, "crypto.Secp256k1"}},
|
{"verifyWithECDsa", []string{"[]byte{1, 2, 3}", pub, sig, "crypto.Secp256k1Sha256"}},
|
||||||
{"bls12381Serialize", []string{"crypto.Bls12381Point{}"}},
|
{"bls12381Serialize", []string{"crypto.Bls12381Point{}"}},
|
||||||
{"bls12381Deserialize", []string{"[]byte{1, 2, 3}"}},
|
{"bls12381Deserialize", []string{"[]byte{1, 2, 3}"}},
|
||||||
{"bls12381Equal", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}},
|
{"bls12381Equal", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}},
|
||||||
|
@ -288,7 +290,7 @@ func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, nativ
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []string) interop.MethodAndPrice {
|
func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []string) interop.HFSpecificMethodAndPrice {
|
||||||
paramLen := len(params)
|
paramLen := len(params)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -308,8 +310,10 @@ func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []strin
|
||||||
name = strings.TrimSuffix(name, "WithData")
|
name = strings.TrimSuffix(name, "WithData")
|
||||||
}
|
}
|
||||||
|
|
||||||
md, ok := ctr.GetMethod(name, paramLen)
|
latestHF := config.LatestHardfork()
|
||||||
require.True(t, ok, ctr.Manifest.Name, name, paramLen)
|
cMD := ctr.HFSpecificContractMD(&latestHF)
|
||||||
|
md, ok := cMD.GetMethod(name, paramLen)
|
||||||
|
require.True(t, ok, cMD.Manifest.Name, name, paramLen)
|
||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,19 @@ func TestNamedReturn(t *testing.T) {
|
||||||
t.Run("AnotherVariable", runCase("b, c", big.NewInt(5)))
|
t.Run("AnotherVariable", runCase("b, c", big.NewInt(5)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNamedReturnDefault(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
func Main() int {
|
||||||
|
a, b, c := f()
|
||||||
|
return a + b + c
|
||||||
|
}
|
||||||
|
func f() (_ int, b int, c int) {
|
||||||
|
b += 1
|
||||||
|
return
|
||||||
|
}`
|
||||||
|
eval(t, src, big.NewInt(1))
|
||||||
|
}
|
||||||
|
|
||||||
func TestTypeAssertReturn(t *testing.T) {
|
func TestTypeAssertReturn(t *testing.T) {
|
||||||
src := `
|
src := `
|
||||||
package main
|
package main
|
||||||
|
|
|
@ -5,6 +5,13 @@ package config
|
||||||
// Hardfork represents the application hard-fork identifier.
|
// Hardfork represents the application hard-fork identifier.
|
||||||
type Hardfork byte
|
type Hardfork byte
|
||||||
|
|
||||||
|
// HFDefault is a default value of Hardfork enum. It's a special constant
|
||||||
|
// aimed to denote the node code enabled by default starting from the
|
||||||
|
// genesis block. HFDefault is not a hard-fork, but this constant can be used for
|
||||||
|
// convenient hard-forks comparison and to refer to the default hard-fork-less
|
||||||
|
// node behaviour.
|
||||||
|
const HFDefault Hardfork = 0 // Default
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// HFAspidochelone represents hard-fork introduced in #2469 (ported from
|
// HFAspidochelone represents hard-fork introduced in #2469 (ported from
|
||||||
// https://github.com/neo-project/neo/pull/2712) and #2519 (ported from
|
// https://github.com/neo-project/neo/pull/2712) and #2519 (ported from
|
||||||
|
@ -15,6 +22,11 @@ const (
|
||||||
// https://github.com/neo-project/neo/pull/2883) and #3085 (ported from
|
// https://github.com/neo-project/neo/pull/2883) and #3085 (ported from
|
||||||
// https://github.com/neo-project/neo/pull/2810).
|
// https://github.com/neo-project/neo/pull/2810).
|
||||||
HFBasilisk // Basilisk
|
HFBasilisk // Basilisk
|
||||||
|
// HFCockatrice represents hard-fork introduced in #3402 (ported from
|
||||||
|
// https://github.com/neo-project/neo/pull/2942), #3301 (ported from
|
||||||
|
// https://github.com/neo-project/neo/pull/2925) and #3362 (ported from
|
||||||
|
// https://github.com/neo-project/neo/pull/3154).
|
||||||
|
HFCockatrice // Cockatrice
|
||||||
// hfLast denotes the end of hardforks enum. Consider adding new hardforks
|
// hfLast denotes the end of hardforks enum. Consider adding new hardforks
|
||||||
// before hfLast.
|
// before hfLast.
|
||||||
hfLast
|
hfLast
|
||||||
|
@ -36,9 +48,38 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cmp returns the result of hardforks comparison. It returns:
|
||||||
|
//
|
||||||
|
// -1 if hf < other
|
||||||
|
// 0 if hf == other
|
||||||
|
// +1 if hf > other
|
||||||
|
func (hf Hardfork) Cmp(other Hardfork) int {
|
||||||
|
switch {
|
||||||
|
case hf == other:
|
||||||
|
return 0
|
||||||
|
case hf < other:
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev returns the previous hardfork for the given one. Calling Prev for the default hardfork is a no-op.
|
||||||
|
func (hf Hardfork) Prev() Hardfork {
|
||||||
|
if hf == HFDefault {
|
||||||
|
panic("unexpected call to Prev for the default hardfork")
|
||||||
|
}
|
||||||
|
return hf >> 1
|
||||||
|
}
|
||||||
|
|
||||||
// IsHardforkValid denotes whether the provided string represents a valid
|
// IsHardforkValid denotes whether the provided string represents a valid
|
||||||
// Hardfork name.
|
// Hardfork name.
|
||||||
func IsHardforkValid(s string) bool {
|
func IsHardforkValid(s string) bool {
|
||||||
_, ok := hardforks[s]
|
_, ok := hardforks[s]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LatestHardfork returns latest known hardfork.
|
||||||
|
func LatestHardfork() Hardfork {
|
||||||
|
return hfLast >> 1
|
||||||
|
}
|
||||||
|
|
|
@ -8,27 +8,31 @@ func _() {
|
||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
// Re-run the stringer command to generate them again.
|
// Re-run the stringer command to generate them again.
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
|
_ = x[HFDefault-0]
|
||||||
_ = x[HFAspidochelone-1]
|
_ = x[HFAspidochelone-1]
|
||||||
_ = x[HFBasilisk-2]
|
_ = x[HFBasilisk-2]
|
||||||
_ = x[hfLast-4]
|
_ = x[HFCockatrice-4]
|
||||||
|
_ = x[hfLast-8]
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_Hardfork_name_0 = "AspidocheloneBasilisk"
|
_Hardfork_name_0 = "DefaultAspidocheloneBasilisk"
|
||||||
_Hardfork_name_1 = "hfLast"
|
_Hardfork_name_1 = "Cockatrice"
|
||||||
|
_Hardfork_name_2 = "hfLast"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_Hardfork_index_0 = [...]uint8{0, 13, 21}
|
_Hardfork_index_0 = [...]uint8{0, 7, 20, 28}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i Hardfork) String() string {
|
func (i Hardfork) String() string {
|
||||||
switch {
|
switch {
|
||||||
case 1 <= i && i <= 2:
|
case i <= 2:
|
||||||
i -= 1
|
|
||||||
return _Hardfork_name_0[_Hardfork_index_0[i]:_Hardfork_index_0[i+1]]
|
return _Hardfork_name_0[_Hardfork_index_0[i]:_Hardfork_index_0[i+1]]
|
||||||
case i == 4:
|
case i == 4:
|
||||||
return _Hardfork_name_1
|
return _Hardfork_name_1
|
||||||
|
case i == 8:
|
||||||
|
return _Hardfork_name_2
|
||||||
default:
|
default:
|
||||||
return "Hardfork(" + strconv.FormatInt(int64(i), 10) + ")"
|
return "Hardfork(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -320,7 +319,7 @@ func TestGenesisExtensionsMarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
t.Run("unmarshal config", func(t *testing.T) {
|
t.Run("unmarshal config", func(t *testing.T) {
|
||||||
t.Run("good", func(t *testing.T) {
|
t.Run("good", func(t *testing.T) {
|
||||||
pubStr := hex.EncodeToString(pub.Bytes())
|
pubStr := pub.StringCompressed()
|
||||||
script := []byte{1, 2, 3, 4}
|
script := []byte{1, 2, 3, 4}
|
||||||
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
||||||
Genesis:
|
Genesis:
|
||||||
|
@ -354,7 +353,7 @@ func TestGenesisExtensionsMarshalYAML(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("unknown role", func(t *testing.T) {
|
t.Run("unknown role", func(t *testing.T) {
|
||||||
pubStr := hex.EncodeToString(pub.Bytes())
|
pubStr := pub.StringCompressed()
|
||||||
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
||||||
Genesis:
|
Genesis:
|
||||||
Roles:
|
Roles:
|
||||||
|
@ -367,7 +366,7 @@ func TestGenesisExtensionsMarshalYAML(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("last role", func(t *testing.T) {
|
t.Run("last role", func(t *testing.T) {
|
||||||
pubStr := hex.EncodeToString(pub.Bytes())
|
pubStr := pub.StringCompressed()
|
||||||
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
||||||
Genesis:
|
Genesis:
|
||||||
Roles:
|
Roles:
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
json "github.com/nspcc-dev/go-ordered-json"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
"github.com/nspcc-dev/neo-go/pkg/config/limits"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
@ -44,7 +45,7 @@ import (
|
||||||
|
|
||||||
// Tuning parameters.
|
// Tuning parameters.
|
||||||
const (
|
const (
|
||||||
version = "0.2.10"
|
version = "0.2.12"
|
||||||
|
|
||||||
// DefaultInitialGAS is the default amount of GAS emitted to the standby validators
|
// DefaultInitialGAS is the default amount of GAS emitted to the standby validators
|
||||||
// multisignature account during native GAS contract initialization.
|
// multisignature account during native GAS contract initialization.
|
||||||
|
@ -288,7 +289,7 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
|
||||||
cfg.Hardforks[hf.String()] = 0
|
cfg.Hardforks[hf.String()] = 0
|
||||||
}
|
}
|
||||||
log.Info("Hardforks are not set, using default value")
|
log.Info("Hardforks are not set, using default value")
|
||||||
} else {
|
} else if len(cfg.Hardforks) != 0 {
|
||||||
// Explicitly set the height of all old omitted hardforks to 0 for proper
|
// Explicitly set the height of all old omitted hardforks to 0 for proper
|
||||||
// IsHardforkEnabled behaviour.
|
// IsHardforkEnabled behaviour.
|
||||||
for _, hf := range config.Hardforks {
|
for _, hf := range config.Hardforks {
|
||||||
|
@ -341,16 +342,36 @@ func (bc *Blockchain) GetDesignatedByRole(r noderoles.Role) (keys.PublicKeys, ui
|
||||||
return res, h, err
|
return res, h, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getCurrentHF returns the latest currently enabled hardfork. In case if no hardforks are enabled, the
|
||||||
|
// default config.Hardfork(0) value is returned.
|
||||||
|
func (bc *Blockchain) getCurrentHF() config.Hardfork {
|
||||||
|
var (
|
||||||
|
height = bc.BlockHeight()
|
||||||
|
current config.Hardfork
|
||||||
|
)
|
||||||
|
// Rely on the fact that hardforks list is continuous.
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
enableHeight, ok := bc.config.Hardforks[hf.String()]
|
||||||
|
if !ok || height < enableHeight {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = hf
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
// SetOracle sets oracle module. It can safely be called on the running blockchain.
|
// SetOracle sets oracle module. It can safely be called on the running blockchain.
|
||||||
// To unregister Oracle service use SetOracle(nil).
|
// To unregister Oracle service use SetOracle(nil).
|
||||||
func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
func (bc *Blockchain) SetOracle(mod native.OracleService) {
|
||||||
orc := bc.contracts.Oracle
|
orc := bc.contracts.Oracle
|
||||||
|
currentHF := bc.getCurrentHF()
|
||||||
if mod != nil {
|
if mod != nil {
|
||||||
md, ok := orc.GetMethod(manifest.MethodVerify, -1)
|
orcMd := orc.HFSpecificContractMD(¤tHF)
|
||||||
|
md, ok := orcMd.GetMethod(manifest.MethodVerify, -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
|
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
|
||||||
}
|
}
|
||||||
mod.UpdateNativeContract(orc.NEF.Script, orc.GetOracleResponseScript(),
|
mod.UpdateNativeContract(orcMd.NEF.Script, orc.GetOracleResponseScript(),
|
||||||
orc.Hash, md.MD.Offset)
|
orc.Hash, md.MD.Offset)
|
||||||
keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle)
|
keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -487,6 +508,7 @@ func (bc *Blockchain) init() error {
|
||||||
// Check autogenerated native contracts' manifests and NEFs against the stored ones.
|
// Check autogenerated native contracts' manifests and NEFs against the stored ones.
|
||||||
// Need to be done after native Management cache initialization to be able to get
|
// Need to be done after native Management cache initialization to be able to get
|
||||||
// contract state from DAO via high-level bc API.
|
// contract state from DAO via high-level bc API.
|
||||||
|
var current = bc.getCurrentHF()
|
||||||
for _, c := range bc.contracts.Contracts {
|
for _, c := range bc.contracts.Contracts {
|
||||||
md := c.Metadata()
|
md := c.Metadata()
|
||||||
storedCS := bc.GetContractState(md.Hash)
|
storedCS := bc.GetContractState(md.Hash)
|
||||||
|
@ -504,8 +526,9 @@ func (bc *Blockchain) init() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
||||||
}
|
}
|
||||||
|
hfMD := md.HFSpecificContractMD(¤t)
|
||||||
autogenCS := &state.Contract{
|
autogenCS := &state.Contract{
|
||||||
ContractBase: md.ContractBase,
|
ContractBase: hfMD.ContractBase,
|
||||||
UpdateCounter: storedCS.UpdateCounter, // it can be restored only from the DB, so use the stored value.
|
UpdateCounter: storedCS.UpdateCounter, // it can be restored only from the DB, so use the stored value.
|
||||||
}
|
}
|
||||||
autogenCSBytes, err := stackitem.SerializeConvertible(autogenCS)
|
autogenCSBytes, err := stackitem.SerializeConvertible(autogenCS)
|
||||||
|
@ -513,8 +536,10 @@ func (bc *Blockchain) init() error {
|
||||||
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(storedCSBytes, autogenCSBytes) {
|
if !bytes.Equal(storedCSBytes, autogenCSBytes) {
|
||||||
return fmt.Errorf("native %s: version mismatch (stored contract state differs from autogenerated one), "+
|
storedJ, _ := json.Marshal(storedCS)
|
||||||
"try to resynchronize the node from the genesis", md.Name)
|
autogenJ, _ := json.Marshal(autogenCS)
|
||||||
|
return fmt.Errorf("native %s: version mismatch for the latest hardfork %s (stored contract state differs from autogenerated one), "+
|
||||||
|
"try to resynchronize the node from the genesis: %s vs %s", md.Name, current, string(storedJ), string(autogenJ))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1490,8 +1515,11 @@ func (bc *Blockchain) AddBlock(block *block.Block) error {
|
||||||
} else {
|
} else {
|
||||||
err = bc.verifyAndPoolTx(tx, mp, bc)
|
err = bc.verifyAndPoolTx(tx, mp, bc)
|
||||||
}
|
}
|
||||||
if err != nil && bc.config.VerifyTransactions {
|
if err != nil {
|
||||||
return fmt.Errorf("transaction %s failed to verify: %w", tx.Hash().StringLE(), err)
|
if bc.config.VerifyTransactions {
|
||||||
|
return fmt.Errorf("transaction %s failed to verify: %w", tx.Hash().StringLE(), err)
|
||||||
|
}
|
||||||
|
bc.log.Warn(fmt.Sprintf("transaction %s failed to verify: %s", tx.Hash().StringLE(), err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2267,10 +2295,19 @@ func (bc *Blockchain) GetNativeContractScriptHash(name string) (util.Uint160, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNatives returns list of native contracts.
|
// GetNatives returns list of native contracts.
|
||||||
func (bc *Blockchain) GetNatives() []state.NativeContract {
|
func (bc *Blockchain) GetNatives() []state.Contract {
|
||||||
res := make([]state.NativeContract, 0, len(bc.contracts.Contracts))
|
res := make([]state.Contract, 0, len(bc.contracts.Contracts))
|
||||||
|
current := bc.getCurrentHF()
|
||||||
for _, c := range bc.contracts.Contracts {
|
for _, c := range bc.contracts.Contracts {
|
||||||
res = append(res, c.Metadata().NativeContract)
|
activeIn := c.ActiveIn()
|
||||||
|
if !(activeIn == nil || activeIn.Cmp(current) <= 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
st := bc.GetContractState(c.Metadata().Hash)
|
||||||
|
if st != nil { // Should never happen, but better safe than sorry.
|
||||||
|
res = append(res, *st)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -2891,7 +2928,7 @@ func (bc *Blockchain) InitVerificationContext(ic *interop.Context, hash util.Uin
|
||||||
initOffset = md.Offset
|
initOffset = md.Offset
|
||||||
}
|
}
|
||||||
ic.Invocations[cs.Hash]++
|
ic.Invocations[cs.Hash]++
|
||||||
ic.VM.LoadNEFMethod(&cs.NEF, util.Uint160{}, hash, callflag.ReadOnly,
|
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, util.Uint160{}, hash, callflag.ReadOnly,
|
||||||
true, verifyOffset, initOffset, nil)
|
true, verifyOffset, initOffset, nil)
|
||||||
}
|
}
|
||||||
if len(witness.InvocationScript) != 0 {
|
if len(witness.InvocationScript) != 0 {
|
||||||
|
@ -2980,12 +3017,7 @@ func (bc *Blockchain) verifyTxWitnesses(t *transaction.Transaction, block *block
|
||||||
|
|
||||||
// verifyHeaderWitnesses is a block-specific implementation of VerifyWitnesses logic.
|
// verifyHeaderWitnesses is a block-specific implementation of VerifyWitnesses logic.
|
||||||
func (bc *Blockchain) verifyHeaderWitnesses(currHeader, prevHeader *block.Header) error {
|
func (bc *Blockchain) verifyHeaderWitnesses(currHeader, prevHeader *block.Header) error {
|
||||||
var hash util.Uint160
|
hash := prevHeader.NextConsensus
|
||||||
if prevHeader == nil && currHeader.PrevHash.Equals(util.Uint256{}) {
|
|
||||||
hash = currHeader.Script.ScriptHash()
|
|
||||||
} else {
|
|
||||||
hash = prevHeader.NextConsensus
|
|
||||||
}
|
|
||||||
_, err := bc.VerifyWitness(hash, currHeader, &currHeader.Script, HeaderVerificationGasLimit)
|
_, err := bc.VerifyWitness(hash, currHeader, &currHeader.Script, HeaderVerificationGasLimit)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,16 +361,24 @@ func TestBlockchain_IsRunning(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewBlockchain_InitHardforks(t *testing.T) {
|
func TestNewBlockchain_InitHardforks(t *testing.T) {
|
||||||
t.Run("empty set", func(t *testing.T) {
|
t.Run("nil set", func(t *testing.T) {
|
||||||
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
c.ProtocolConfiguration.Hardforks = map[string]uint32{}
|
c.ProtocolConfiguration.Hardforks = nil
|
||||||
require.NoError(t, c.ProtocolConfiguration.Validate())
|
require.NoError(t, c.ProtocolConfiguration.Validate())
|
||||||
})
|
})
|
||||||
require.Equal(t, map[string]uint32{
|
require.Equal(t, map[string]uint32{
|
||||||
config.HFAspidochelone.String(): 0,
|
config.HFAspidochelone.String(): 0,
|
||||||
config.HFBasilisk.String(): 0,
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): 0,
|
||||||
}, bc.GetConfig().Hardforks)
|
}, bc.GetConfig().Hardforks)
|
||||||
})
|
})
|
||||||
|
t.Run("empty set", func(t *testing.T) {
|
||||||
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
|
c.ProtocolConfiguration.Hardforks = map[string]uint32{}
|
||||||
|
require.NoError(t, c.ProtocolConfiguration.Validate())
|
||||||
|
})
|
||||||
|
require.Equal(t, map[string]uint32{}, bc.GetConfig().Hardforks)
|
||||||
|
})
|
||||||
t.Run("missing old", func(t *testing.T) {
|
t.Run("missing old", func(t *testing.T) {
|
||||||
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFBasilisk.String(): 5}
|
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFBasilisk.String(): 5}
|
||||||
|
@ -392,12 +400,13 @@ func TestNewBlockchain_InitHardforks(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("all present", func(t *testing.T) {
|
t.Run("all present", func(t *testing.T) {
|
||||||
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
bc := newTestChainWithCustomCfg(t, func(c *config.Config) {
|
||||||
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFAspidochelone.String(): 5, config.HFBasilisk.String(): 10}
|
c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFAspidochelone.String(): 5, config.HFBasilisk.String(): 10, config.HFCockatrice.String(): 15}
|
||||||
require.NoError(t, c.ProtocolConfiguration.Validate())
|
require.NoError(t, c.ProtocolConfiguration.Validate())
|
||||||
})
|
})
|
||||||
require.Equal(t, map[string]uint32{
|
require.Equal(t, map[string]uint32{
|
||||||
config.HFAspidochelone.String(): 5,
|
config.HFAspidochelone.String(): 5,
|
||||||
config.HFBasilisk.String(): 10,
|
config.HFBasilisk.String(): 10,
|
||||||
|
config.HFCockatrice.String(): 15,
|
||||||
}, bc.GetConfig().Hardforks)
|
}, bc.GetConfig().Hardforks)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -24,6 +25,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
|
@ -269,7 +271,7 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) {
|
||||||
|
|
||||||
_, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
|
_, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err)
|
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Cockatrice (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("good", func(t *testing.T) {
|
t.Run("good", func(t *testing.T) {
|
||||||
|
@ -315,6 +317,92 @@ func TestBlockchain_InitializeNeoCache_Bug3181(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBlockchain_InitializeNeoCache_Bug3424 ensures that Neo cache (new epoch
|
||||||
|
// committee and stand by validators) is properly initialized after node restart
|
||||||
|
// at the dBFT epoch boundary.
|
||||||
|
func TestBlockchain_InitializeNeoCache_Bug3424(t *testing.T) {
|
||||||
|
ps, path := newLevelDBForTestingWithPath(t, "")
|
||||||
|
bc, validators, committee, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps)
|
||||||
|
require.NoError(t, err)
|
||||||
|
go bc.Run()
|
||||||
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
|
cfg := e.Chain.GetConfig()
|
||||||
|
committeeSize := cfg.GetCommitteeSize(0)
|
||||||
|
validatorsCount := cfg.GetNumOfCNs(0)
|
||||||
|
|
||||||
|
// Stand by committee drives the chain.
|
||||||
|
standBySorted, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee)
|
||||||
|
require.NoError(t, err)
|
||||||
|
standBySorted = standBySorted[:validatorsCount]
|
||||||
|
sort.Sort(standBySorted)
|
||||||
|
pubs := e.Chain.ComputeNextBlockValidators()
|
||||||
|
require.Equal(t, standBySorted, keys.PublicKeys(pubs))
|
||||||
|
|
||||||
|
// Move from stand by committee to the elected nodes.
|
||||||
|
e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, 100_0000_0000, nil)
|
||||||
|
neoCommitteeInvoker := e.CommitteeInvoker(e.NativeHash(t, nativenames.Neo))
|
||||||
|
neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)
|
||||||
|
policyInvoker := neoCommitteeInvoker.CommitteeInvoker(neoCommitteeInvoker.NativeHash(t, nativenames.Policy))
|
||||||
|
|
||||||
|
advanceChain := func(t *testing.T) {
|
||||||
|
for int(e.Chain.BlockHeight())%committeeSize != 0 {
|
||||||
|
neoCommitteeInvoker.AddNewBlock(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
advanceChainToEpochBoundary := func(t *testing.T) {
|
||||||
|
for int(e.Chain.BlockHeight()+1)%committeeSize != 0 {
|
||||||
|
neoCommitteeInvoker.AddNewBlock(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// voters vote for candidates.
|
||||||
|
voters := make([]neotest.Signer, committeeSize+1)
|
||||||
|
candidates := make([]neotest.Signer, committeeSize+1)
|
||||||
|
for i := 0; i < committeeSize+1; i++ {
|
||||||
|
voters[i] = e.NewAccount(t, 10_0000_0000)
|
||||||
|
candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration
|
||||||
|
}
|
||||||
|
txes := make([]*transaction.Transaction, 0, committeeSize*3)
|
||||||
|
for i := 0; i < committeeSize+1; i++ {
|
||||||
|
transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil)
|
||||||
|
txes = append(txes, transferTx)
|
||||||
|
registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
|
||||||
|
txes = append(txes, registerTx)
|
||||||
|
voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
|
||||||
|
txes = append(txes, voteTx)
|
||||||
|
}
|
||||||
|
txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().ScriptHash()))
|
||||||
|
neoValidatorsInvoker.AddNewBlock(t, txes...)
|
||||||
|
for _, tx := range txes {
|
||||||
|
e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure validators are properly updated.
|
||||||
|
advanceChain(t)
|
||||||
|
pubs = e.Chain.ComputeNextBlockValidators()
|
||||||
|
sortedCandidates := make(keys.PublicKeys, validatorsCount)
|
||||||
|
for i := range candidates[:validatorsCount] {
|
||||||
|
sortedCandidates[i] = candidates[i].(neotest.SingleSigner).Account().PublicKey()
|
||||||
|
}
|
||||||
|
sort.Sort(sortedCandidates)
|
||||||
|
require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs))
|
||||||
|
|
||||||
|
// Move to the last block in the epoch and restart the node.
|
||||||
|
advanceChainToEpochBoundary(t)
|
||||||
|
bc.Close() // Ensure persist is done and persistent store is properly closed.
|
||||||
|
ps, _ = newLevelDBForTestingWithPath(t, path)
|
||||||
|
t.Cleanup(func() { require.NoError(t, ps.Close()) })
|
||||||
|
|
||||||
|
// Start chain from the existing database that should trigger an update of native
|
||||||
|
// Neo newEpoch* cached values during initializaition. This update requires candidates
|
||||||
|
// list recalculation and caldidates policies checks.
|
||||||
|
bc, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, nil, ps)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pubs = bc.ComputeNextBlockValidators()
|
||||||
|
require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs))
|
||||||
|
}
|
||||||
|
|
||||||
// This test enables Notary native contract at non-zero height and checks that no
|
// This test enables Notary native contract at non-zero height and checks that no
|
||||||
// Notary cache initialization is performed before that height on node restart.
|
// Notary cache initialization is performed before that height on node restart.
|
||||||
/*
|
/*
|
||||||
|
@ -2460,7 +2548,7 @@ func TestBlockchain_GenesisTransactionExtension(t *testing.T) {
|
||||||
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
||||||
emit.Bytes(script.BinWriter, to.BytesBE())
|
emit.Bytes(script.BinWriter, to.BytesBE())
|
||||||
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeCheckWitness)
|
||||||
emit.AppCall(script.BinWriter, state.CreateNativeContractHash(nativenames.Neo), "transfer", callflag.All, from, to, amount, nil)
|
emit.AppCall(script.BinWriter, nativehashes.NeoToken, "transfer", callflag.All, from, to, amount, nil)
|
||||||
emit.Opcodes(script.BinWriter, opcode.ASSERT)
|
emit.Opcodes(script.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
var sysFee int64 = 1_0000_0000
|
var sysFee int64 = 1_0000_0000
|
||||||
|
@ -2479,3 +2567,56 @@ func TestBlockchain_GenesisTransactionExtension(t *testing.T) {
|
||||||
require.Equal(t, int64(amount), actualNeo.Int64())
|
require.Equal(t, int64(amount), actualNeo.Int64())
|
||||||
require.Equal(t, 0, int(lub))
|
require.Equal(t, 0, int(lub))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestNativenames ensures that nativenames.All contains all expected native contract names
|
||||||
|
// in the right order.
|
||||||
|
func TestNativenames(t *testing.T) {
|
||||||
|
bc, _ := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
natives := bc.GetNatives()
|
||||||
|
require.Equal(t, len(natives), len(nativenames.All))
|
||||||
|
for i, cs := range natives {
|
||||||
|
require.Equal(t, cs.Manifest.Name, nativenames.All[i], i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockchain_StoreAsTransaction_ExecutableConflict ensures that transaction conflicting with
|
||||||
|
// some on-chain block can be properly stored and doesn't break the database.
|
||||||
|
func TestBlockchain_StoreAsTransaction_ExecutableConflict(t *testing.T) {
|
||||||
|
bc, acc := chain.NewSingleWithCustomConfig(t, nil)
|
||||||
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||||
|
genesisH := bc.GetHeaderHash(0)
|
||||||
|
currHeight := bc.BlockHeight()
|
||||||
|
|
||||||
|
// Ensure AER can be retrieved for genesis block.
|
||||||
|
aer, err := bc.GetAppExecResults(genesisH, trigger.All)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(aer))
|
||||||
|
|
||||||
|
tx := transaction.New([]byte{byte(opcode.PUSHT)}, 0)
|
||||||
|
tx.Nonce = 5
|
||||||
|
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
|
||||||
|
tx.Attributes = []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: genesisH}}}
|
||||||
|
e.SignTx(t, tx, -1, acc)
|
||||||
|
e.AddNewBlock(t, tx)
|
||||||
|
e.CheckHalt(t, tx.Hash(), stackitem.Make(true))
|
||||||
|
|
||||||
|
// Ensure original tx can be retrieved.
|
||||||
|
actual, actualHeight, err := bc.GetTransaction(tx.Hash())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, currHeight+1, actualHeight)
|
||||||
|
require.Equal(t, tx, actual, tx)
|
||||||
|
|
||||||
|
// Ensure conflict stub is not stored. This check doesn't give us 100% sure that
|
||||||
|
// there's no specific conflict record since GetTransaction doesn't return conflict records,
|
||||||
|
// but at least it allows to ensure that no transaction record is present.
|
||||||
|
_, _, err = bc.GetTransaction(genesisH)
|
||||||
|
require.ErrorIs(t, err, storage.ErrKeyNotFound)
|
||||||
|
|
||||||
|
// Ensure AER still can be retrieved for genesis block.
|
||||||
|
aer, err = bc.GetAppExecResults(genesisH, trigger.All)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(aer))
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ var (
|
||||||
ErrInternalDBInconsistency = errors.New("internal DB inconsistency")
|
ErrInternalDBInconsistency = errors.New("internal DB inconsistency")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// conflictRecordValueLen is the length of value of transaction conflict record.
|
||||||
|
// It consists of 1-byte [storage.ExecTransaction] prefix and 4-bytes block index
|
||||||
|
// in the LE form.
|
||||||
|
const conflictRecordValueLen = 1 + 4
|
||||||
|
|
||||||
// Simple is memCached wrapper around DB, simple DAO implementation.
|
// Simple is memCached wrapper around DB, simple DAO implementation.
|
||||||
type Simple struct {
|
type Simple struct {
|
||||||
Version Version
|
Version Version
|
||||||
|
@ -323,7 +328,7 @@ func (dao *Simple) GetTxExecResult(hash util.Uint256) (uint32, *transaction.Tran
|
||||||
// decodeTxAndExecResult decodes transaction, its height and execution result from
|
// decodeTxAndExecResult decodes transaction, its height and execution result from
|
||||||
// the given executable bytes. It performs no executable prefix check.
|
// the given executable bytes. It performs no executable prefix check.
|
||||||
func decodeTxAndExecResult(buf []byte) (uint32, *transaction.Transaction, *state.AppExecResult, error) {
|
func decodeTxAndExecResult(buf []byte) (uint32, *transaction.Transaction, *state.AppExecResult, error) {
|
||||||
if len(buf) >= 6 && buf[5] == transaction.DummyVersion {
|
if len(buf) == conflictRecordValueLen { // conflict record stub.
|
||||||
return 0, nil, nil, storage.ErrKeyNotFound
|
return 0, nil, nil, storage.ErrKeyNotFound
|
||||||
}
|
}
|
||||||
r := io.NewBinReaderFromBuf(buf)
|
r := io.NewBinReaderFromBuf(buf)
|
||||||
|
@ -605,7 +610,7 @@ func (dao *Simple) DeleteHeaderHashes(since uint32, batchSize int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransaction returns Transaction and its height by the given hash
|
// GetTransaction returns Transaction and its height by the given hash
|
||||||
// if it exists in the store. It does not return dummy transactions.
|
// if it exists in the store. It does not return conflict record stubs.
|
||||||
func (dao *Simple) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) {
|
func (dao *Simple) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) {
|
||||||
key := dao.makeExecutableKey(hash)
|
key := dao.makeExecutableKey(hash)
|
||||||
b, err := dao.Store.Get(key)
|
b, err := dao.Store.Get(key)
|
||||||
|
@ -619,7 +624,7 @@ func (dao *Simple) GetTransaction(hash util.Uint256) (*transaction.Transaction,
|
||||||
// It may be a block.
|
// It may be a block.
|
||||||
return nil, 0, storage.ErrKeyNotFound
|
return nil, 0, storage.ErrKeyNotFound
|
||||||
}
|
}
|
||||||
if len(b) == 1+4 { // storage.ExecTransaction + index
|
if len(b) == conflictRecordValueLen {
|
||||||
// It's a conflict record stub.
|
// It's a conflict record stub.
|
||||||
return nil, 0, storage.ErrKeyNotFound
|
return nil, 0, storage.ErrKeyNotFound
|
||||||
}
|
}
|
||||||
|
@ -699,10 +704,16 @@ func (dao *Simple) HasTransaction(hash util.Uint256, signers []transaction.Signe
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bytes) < 5 { // (storage.ExecTransaction + index) for conflict record
|
if len(bytes) < conflictRecordValueLen { // (storage.ExecTransaction + index) for conflict record
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(bytes) != 5 {
|
if bytes[0] != storage.ExecTransaction {
|
||||||
|
// It's a block, thus no conflict. This path is needed since there's a transaction accepted on mainnet
|
||||||
|
// that conflicts with block. This transaction was declined by Go nodes, but accepted by C# nodes, and hence
|
||||||
|
// we need to adjust Go behaviour post-factum. Ref. #3427 and 0x289c235dcdab8be7426d05f0fbb5e86c619f81481ea136493fa95deee5dbb7cc.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(bytes) != conflictRecordValueLen {
|
||||||
return ErrAlreadyExists // fully-qualified transaction
|
return ErrAlreadyExists // fully-qualified transaction
|
||||||
}
|
}
|
||||||
if len(signers) == 0 {
|
if len(signers) == 0 {
|
||||||
|
@ -778,6 +789,10 @@ func (dao *Simple) DeleteBlock(h util.Uint256) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve conflict record stub for %s (height %d, conflict %s): %w", tx.Hash().StringLE(), b.Index, hash.StringLE(), err)
|
return fmt.Errorf("failed to retrieve conflict record stub for %s (height %d, conflict %s): %w", tx.Hash().StringLE(), b.Index, hash.StringLE(), err)
|
||||||
}
|
}
|
||||||
|
// It might be a block since we allow transactions to have block hash in the Conflicts attribute.
|
||||||
|
if v[0] != storage.ExecTransaction {
|
||||||
|
continue
|
||||||
|
}
|
||||||
index := binary.LittleEndian.Uint32(v[1:])
|
index := binary.LittleEndian.Uint32(v[1:])
|
||||||
// We can check for `<=` here, but use equality comparison to be more precise
|
// We can check for `<=` here, but use equality comparison to be more precise
|
||||||
// and do not touch earlier conflict records (if any). Their removal must be triggered
|
// and do not touch earlier conflict records (if any). Their removal must be triggered
|
||||||
|
@ -838,8 +853,9 @@ func (dao *Simple) StoreAsCurrentBlock(block *block.Block) {
|
||||||
dao.Store.Put(dao.mkKeyPrefix(storage.SYSCurrentBlock), buf.Bytes())
|
dao.Store.Put(dao.mkKeyPrefix(storage.SYSCurrentBlock), buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreAsTransaction stores the given TX as DataTransaction. It also stores transactions
|
// StoreAsTransaction stores the given TX as DataTransaction. It also stores conflict records
|
||||||
// the given tx has conflicts with as DataTransaction with dummy version. It can reuse the given
|
// (hashes of transactions the given tx has conflicts with) as DataTransaction with value containing
|
||||||
|
// only five bytes: 1-byte [storage.ExecTransaction] executable prefix + 4-bytes-LE block index. It can reuse the given
|
||||||
// buffer for the purpose of value serialization.
|
// buffer for the purpose of value serialization.
|
||||||
func (dao *Simple) StoreAsTransaction(tx *transaction.Transaction, index uint32, aer *state.AppExecResult) error {
|
func (dao *Simple) StoreAsTransaction(tx *transaction.Transaction, index uint32, aer *state.AppExecResult) error {
|
||||||
key := dao.makeExecutableKey(tx.Hash())
|
key := dao.makeExecutableKey(tx.Hash())
|
||||||
|
@ -857,12 +873,23 @@ func (dao *Simple) StoreAsTransaction(tx *transaction.Transaction, index uint32,
|
||||||
val := buf.Bytes()
|
val := buf.Bytes()
|
||||||
dao.Store.Put(key, val)
|
dao.Store.Put(key, val)
|
||||||
|
|
||||||
val = val[:5] // storage.ExecTransaction (1 byte) + index (4 bytes)
|
val = val[:conflictRecordValueLen] // storage.ExecTransaction (1 byte) + index (4 bytes)
|
||||||
attrs := tx.GetAttributes(transaction.ConflictsT)
|
attrs := tx.GetAttributes(transaction.ConflictsT)
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
// Conflict record stub.
|
// Conflict record stub.
|
||||||
hash := attr.Value.(*transaction.Conflicts).Hash
|
hash := attr.Value.(*transaction.Conflicts).Hash
|
||||||
copy(key[1:], hash.BytesBE())
|
copy(key[1:], hash.BytesBE())
|
||||||
|
|
||||||
|
// A short path if there's a block with the matching hash. If it's there, then
|
||||||
|
// don't store the conflict record stub and conflict signers since it's a
|
||||||
|
// useless record, no transaction with the same hash is possible.
|
||||||
|
exec, err := dao.Store.Get(key)
|
||||||
|
if err == nil {
|
||||||
|
if len(exec) > 0 && exec[0] != storage.ExecTransaction {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dao.Store.Put(key, val)
|
dao.Store.Put(key, val)
|
||||||
|
|
||||||
// Conflicting signers.
|
// Conflicting signers.
|
||||||
|
|
|
@ -242,6 +242,53 @@ func TestStoreAsTransaction(t *testing.T) {
|
||||||
}
|
}
|
||||||
err = dao.StoreAsTransaction(tx2, blockIndex, aer2)
|
err = dao.StoreAsTransaction(tx2, blockIndex, aer2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// A special transaction that conflicts with genesis block.
|
||||||
|
genesis := &block.Block{
|
||||||
|
Header: block.Header{
|
||||||
|
Version: 0,
|
||||||
|
Timestamp: 123,
|
||||||
|
Nonce: 1,
|
||||||
|
Index: 0,
|
||||||
|
NextConsensus: util.Uint160{1, 2, 3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genesisAer1 := &state.AppExecResult{
|
||||||
|
Container: genesis.Hash(),
|
||||||
|
Execution: state.Execution{
|
||||||
|
Trigger: trigger.OnPersist,
|
||||||
|
Events: []state.NotificationEvent{},
|
||||||
|
Stack: []stackitem.Item{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genesisAer2 := &state.AppExecResult{
|
||||||
|
Container: genesis.Hash(),
|
||||||
|
Execution: state.Execution{
|
||||||
|
Trigger: trigger.PostPersist,
|
||||||
|
Events: []state.NotificationEvent{},
|
||||||
|
Stack: []stackitem.Item{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, dao.StoreAsBlock(genesis, genesisAer1, genesisAer2))
|
||||||
|
tx3 := transaction.New([]byte{byte(opcode.PUSH1)}, 1)
|
||||||
|
tx3.Signers = append(tx3.Signers, transaction.Signer{Account: signer1})
|
||||||
|
tx3.Scripts = append(tx3.Scripts, transaction.Witness{})
|
||||||
|
tx3.Attributes = []transaction.Attribute{
|
||||||
|
{
|
||||||
|
Type: transaction.ConflictsT,
|
||||||
|
Value: &transaction.Conflicts{Hash: genesis.Hash()},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hash3 := tx3.Hash()
|
||||||
|
aer3 := &state.AppExecResult{
|
||||||
|
Container: hash3,
|
||||||
|
Execution: state.Execution{
|
||||||
|
Trigger: trigger.Application,
|
||||||
|
Events: []state.NotificationEvent{},
|
||||||
|
Stack: []stackitem.Item{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
err = dao.HasTransaction(hash1, nil, 0, 0)
|
err = dao.HasTransaction(hash1, nil, 0, 0)
|
||||||
require.ErrorIs(t, err, ErrAlreadyExists)
|
require.ErrorIs(t, err, ErrAlreadyExists)
|
||||||
err = dao.HasTransaction(hash2, nil, 0, 0)
|
err = dao.HasTransaction(hash2, nil, 0, 0)
|
||||||
|
@ -280,6 +327,29 @@ func TestStoreAsTransaction(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(gotAppExecResult))
|
require.Equal(t, 1, len(gotAppExecResult))
|
||||||
require.Equal(t, *aer2, gotAppExecResult[0])
|
require.Equal(t, *aer2, gotAppExecResult[0])
|
||||||
|
|
||||||
|
// Ensure block is not treated as transaction.
|
||||||
|
err = dao.HasTransaction(genesis.Hash(), nil, 0, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Store tx3 and ensure genesis executable record is not corrupted.
|
||||||
|
require.NoError(t, dao.StoreAsTransaction(tx3, 0, aer3))
|
||||||
|
err = dao.HasTransaction(hash3, nil, 0, 0)
|
||||||
|
require.ErrorIs(t, err, ErrAlreadyExists)
|
||||||
|
actualAer, err := dao.GetAppExecResults(hash3, trigger.All)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(actualAer))
|
||||||
|
require.Equal(t, *aer3, actualAer[0])
|
||||||
|
actualGenesisAer, err := dao.GetAppExecResults(genesis.Hash(), trigger.All)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(actualGenesisAer))
|
||||||
|
require.Equal(t, *genesisAer1, actualGenesisAer[0])
|
||||||
|
require.Equal(t, *genesisAer2, actualGenesisAer[1])
|
||||||
|
|
||||||
|
// A special requirement for transactions that conflict with block: they should
|
||||||
|
// not produce conflict record stub, ref. #3427.
|
||||||
|
err = dao.HasTransaction(genesis.Hash(), nil, 0, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,8 +140,15 @@ type Function struct {
|
||||||
// Method is a signature for a native method.
|
// Method is a signature for a native method.
|
||||||
type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
|
type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
|
||||||
|
|
||||||
// MethodAndPrice is a native-contract method descriptor.
|
// MethodAndPrice is a generic hardfork-independent native contract method descriptor.
|
||||||
type MethodAndPrice struct {
|
type MethodAndPrice struct {
|
||||||
|
HFSpecificMethodAndPrice
|
||||||
|
ActiveFrom *config.Hardfork
|
||||||
|
ActiveTill *config.Hardfork
|
||||||
|
}
|
||||||
|
|
||||||
|
// HFSpecificMethodAndPrice is a hardfork-specific native contract method descriptor.
|
||||||
|
type HFSpecificMethodAndPrice struct {
|
||||||
Func Method
|
Func Method
|
||||||
MD *manifest.Method
|
MD *manifest.Method
|
||||||
CPUFee int64
|
CPUFee int64
|
||||||
|
@ -150,10 +157,24 @@ type MethodAndPrice struct {
|
||||||
RequiredFlags callflag.CallFlag
|
RequiredFlags callflag.CallFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event is a generic hardfork-independent native contract event descriptor.
|
||||||
|
type Event struct {
|
||||||
|
HFSpecificEvent
|
||||||
|
ActiveFrom *config.Hardfork
|
||||||
|
ActiveTill *config.Hardfork
|
||||||
|
}
|
||||||
|
|
||||||
|
// HFSpecificEvent is a hardfork-specific native contract event descriptor.
|
||||||
|
type HFSpecificEvent struct {
|
||||||
|
MD *manifest.Event
|
||||||
|
}
|
||||||
|
|
||||||
// Contract is an interface for all native contracts.
|
// Contract is an interface for all native contracts.
|
||||||
type Contract interface {
|
type Contract interface {
|
||||||
Initialize(*Context) error
|
// Initialize performs native contract initialization on contract deploy or update.
|
||||||
// ActiveIn returns the hardfork native contract is active from or nil in case
|
// Active hardfork is passed as the second argument.
|
||||||
|
Initialize(*Context, *config.Hardfork, *HFSpecificContractMD) error
|
||||||
|
// ActiveIn returns the hardfork native contract is active starting from or nil in case
|
||||||
// it's always active.
|
// it's always active.
|
||||||
ActiveIn() *config.Hardfork
|
ActiveIn() *config.Hardfork
|
||||||
// InitializeCache aimed to initialize contract's cache when the contract has
|
// InitializeCache aimed to initialize contract's cache when the contract has
|
||||||
|
@ -161,53 +182,176 @@ type Contract interface {
|
||||||
// It should be called each time after node restart iff the contract was
|
// It should be called each time after node restart iff the contract was
|
||||||
// deployed and no Initialize method was called.
|
// deployed and no Initialize method was called.
|
||||||
InitializeCache(blockHeight uint32, d *dao.Simple) error
|
InitializeCache(blockHeight uint32, d *dao.Simple) error
|
||||||
|
// Metadata returns generic native contract metadata.
|
||||||
Metadata() *ContractMD
|
Metadata() *ContractMD
|
||||||
OnPersist(*Context) error
|
OnPersist(*Context) error
|
||||||
PostPersist(*Context) error
|
PostPersist(*Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractMD represents a native contract instance.
|
// ContractMD represents a generic hardfork-independent native contract instance.
|
||||||
type ContractMD struct {
|
type ContractMD struct {
|
||||||
state.NativeContract
|
ID int32
|
||||||
Name string
|
Hash util.Uint160
|
||||||
Methods []MethodAndPrice
|
Name string
|
||||||
|
// methods is a generic set of contract methods with activation hardforks. Any HF-dependent part of included methods
|
||||||
|
// (offsets, in particular) must not be used, there's a mdCache field for that.
|
||||||
|
methods []MethodAndPrice
|
||||||
|
// events is a generic set of contract events with activation hardforks. Any HF-dependent part of events must not be
|
||||||
|
// used, there's a mdCache field for that.
|
||||||
|
events []Event
|
||||||
|
// ActiveHFs is a map of hardforks that contract should react to. Contract update should be called for active
|
||||||
|
// hardforks. Note, that unlike the C# implementation, this map doesn't include contract's activation hardfork.
|
||||||
|
// This map is being initialized on contract creation and used as a read-only, hence, not protected
|
||||||
|
// by mutex.
|
||||||
|
ActiveHFs map[config.Hardfork]struct{}
|
||||||
|
|
||||||
|
// mdCache contains hardfork-specific ready-to-use contract descriptors. This cache is initialized in the native
|
||||||
|
// contracts constructors, and acts as read-only during the whole node lifetime, thus not protected by mutex.
|
||||||
|
mdCache map[config.Hardfork]*HFSpecificContractMD
|
||||||
|
|
||||||
|
// onManifestConstruction is a callback for manifest finalization.
|
||||||
|
onManifestConstruction func(*manifest.Manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContractMD returns Contract with the specified list of methods.
|
// HFSpecificContractMD is a hardfork-specific native contract descriptor.
|
||||||
func NewContractMD(name string, id int32) *ContractMD {
|
type HFSpecificContractMD struct {
|
||||||
|
state.ContractBase
|
||||||
|
Methods []HFSpecificMethodAndPrice
|
||||||
|
Events []HFSpecificEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContractMD returns Contract with the specified fields set. onManifestConstruction callback every time
|
||||||
|
// after hardfork-specific manifest creation and aimed to finalize the manifest.
|
||||||
|
func NewContractMD(name string, id int32, onManifestConstruction ...func(*manifest.Manifest)) *ContractMD {
|
||||||
c := &ContractMD{Name: name}
|
c := &ContractMD{Name: name}
|
||||||
|
if len(onManifestConstruction) != 0 {
|
||||||
|
c.onManifestConstruction = onManifestConstruction[0]
|
||||||
|
}
|
||||||
|
|
||||||
c.ID = id
|
c.ID = id
|
||||||
|
|
||||||
// NEF is now stored in the contract state and affects state dump.
|
|
||||||
// Therefore, values are taken from C# node.
|
|
||||||
c.NEF.Header.Compiler = "neo-core-v3.0"
|
|
||||||
c.NEF.Header.Magic = nef.Magic
|
|
||||||
c.NEF.Tokens = []nef.MethodToken{} // avoid `nil` result during JSON marshalling
|
|
||||||
c.Hash = state.CreateNativeContractHash(c.Name)
|
c.Hash = state.CreateNativeContractHash(c.Name)
|
||||||
c.Manifest = *manifest.DefaultManifest(name)
|
c.ActiveHFs = make(map[config.Hardfork]struct{})
|
||||||
|
c.mdCache = make(map[config.Hardfork]*HFSpecificContractMD)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateHash creates a native contract script and updates hash.
|
// HFSpecificContractMD returns hardfork-specific native contract metadata, i.e. with methods, events and script
|
||||||
func (c *ContractMD) UpdateHash() {
|
// corresponding to the specified hardfork. If hardfork is not specified, then default metadata will be returned
|
||||||
|
// (methods, events and script that are always active). Calling this method for hardforks older than the contract
|
||||||
|
// activation hardfork is a no-op.
|
||||||
|
func (c *ContractMD) HFSpecificContractMD(hf *config.Hardfork) *HFSpecificContractMD {
|
||||||
|
var key config.Hardfork
|
||||||
|
if hf != nil {
|
||||||
|
key = *hf
|
||||||
|
}
|
||||||
|
md, ok := c.mdCache[key]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("native contract descriptor cache is not initialized: contract %s, hardfork %s", c.Hash.StringLE(), key))
|
||||||
|
}
|
||||||
|
if md == nil {
|
||||||
|
panic(fmt.Errorf("native contract descriptor cache is nil: contract %s, hardfork %s", c.Hash.StringLE(), key))
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildHFSpecificMD generates and caches contract's descriptor for every known hardfork.
|
||||||
|
func (c *ContractMD) BuildHFSpecificMD(activeIn *config.Hardfork) {
|
||||||
|
var start config.Hardfork
|
||||||
|
if activeIn != nil {
|
||||||
|
start = *activeIn
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hf := range append([]config.Hardfork{config.HFDefault}, config.Hardforks...) {
|
||||||
|
switch {
|
||||||
|
case hf.Cmp(start) < 0:
|
||||||
|
continue
|
||||||
|
case hf.Cmp(start) == 0:
|
||||||
|
c.buildHFSpecificMD(hf)
|
||||||
|
default:
|
||||||
|
if _, ok := c.ActiveHFs[hf]; !ok {
|
||||||
|
// Intentionally omit HFSpecificContractMD structure copying since mdCache is read-only.
|
||||||
|
c.mdCache[hf] = c.mdCache[hf.Prev()]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.buildHFSpecificMD(hf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildHFSpecificMD builds hardfork-specific contract descriptor that includes methods and events active starting from
|
||||||
|
// the specified hardfork or older. It also updates cache with the received value.
|
||||||
|
func (c *ContractMD) buildHFSpecificMD(hf config.Hardfork) {
|
||||||
|
var (
|
||||||
|
abiMethods = make([]manifest.Method, 0, len(c.methods))
|
||||||
|
methods = make([]HFSpecificMethodAndPrice, 0, len(c.methods))
|
||||||
|
abiEvents = make([]manifest.Event, 0, len(c.events))
|
||||||
|
events = make([]HFSpecificEvent, 0, len(c.events))
|
||||||
|
)
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
for i := range c.Methods {
|
for i := range c.methods {
|
||||||
offset := w.Len()
|
m := c.methods[i]
|
||||||
c.Methods[i].MD.Offset = offset
|
if !(m.ActiveFrom == nil || (hf != config.HFDefault && (*m.ActiveFrom).Cmp(hf) >= 0)) ||
|
||||||
c.Manifest.ABI.Methods[i].Offset = offset
|
(m.ActiveTill != nil && (*m.ActiveTill).Cmp(hf) <= 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform method descriptor copy to support independent HF-based offset update.
|
||||||
|
md := *m.MD
|
||||||
|
m.MD = &md
|
||||||
|
m.MD.Offset = w.Len()
|
||||||
|
|
||||||
emit.Int(w.BinWriter, 0)
|
emit.Int(w.BinWriter, 0)
|
||||||
c.Methods[i].SyscallOffset = w.Len()
|
m.SyscallOffset = w.Len()
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
|
||||||
|
abiMethods = append(abiMethods, *m.MD)
|
||||||
|
methods = append(methods, m.HFSpecificMethodAndPrice)
|
||||||
}
|
}
|
||||||
if w.Err != nil {
|
if w.Err != nil {
|
||||||
panic(fmt.Errorf("can't create native contract script: %w", w.Err))
|
panic(fmt.Errorf("can't create native contract script: %w", w.Err))
|
||||||
}
|
}
|
||||||
|
for i := range c.events {
|
||||||
|
e := c.events[i]
|
||||||
|
if !(e.ActiveFrom == nil || (hf != config.HFDefault && (*e.ActiveFrom).Cmp(hf) >= 0)) ||
|
||||||
|
(e.ActiveTill != nil && (*e.ActiveTill).Cmp(hf) <= 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
c.NEF.Script = w.Bytes()
|
abiEvents = append(abiEvents, *e.MD)
|
||||||
c.NEF.Checksum = c.NEF.CalculateChecksum()
|
events = append(events, e.HFSpecificEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEF is now stored in the contract state and affects state dump.
|
||||||
|
// Therefore, values are taken from C# node.
|
||||||
|
nf := nef.File{
|
||||||
|
Header: nef.Header{
|
||||||
|
Magic: nef.Magic,
|
||||||
|
Compiler: "neo-core-v3.0",
|
||||||
|
},
|
||||||
|
Tokens: []nef.MethodToken{}, // avoid `nil` result during JSON marshalling,
|
||||||
|
Script: w.Bytes(),
|
||||||
|
}
|
||||||
|
nf.Checksum = nf.CalculateChecksum()
|
||||||
|
m := manifest.DefaultManifest(c.Name)
|
||||||
|
m.ABI.Methods = abiMethods
|
||||||
|
m.ABI.Events = abiEvents
|
||||||
|
if c.onManifestConstruction != nil {
|
||||||
|
c.onManifestConstruction(m)
|
||||||
|
}
|
||||||
|
md := &HFSpecificContractMD{
|
||||||
|
ContractBase: state.ContractBase{
|
||||||
|
ID: c.ID,
|
||||||
|
Hash: c.Hash,
|
||||||
|
NEF: nf,
|
||||||
|
Manifest: *m,
|
||||||
|
},
|
||||||
|
Methods: methods,
|
||||||
|
Events: events,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mdCache[hf] = md
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddMethod adds a new method to a native contract.
|
// AddMethod adds a new method to a native contract.
|
||||||
|
@ -215,36 +359,38 @@ func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
|
||||||
md.MD = desc
|
md.MD = desc
|
||||||
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
|
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
|
||||||
|
|
||||||
index := sort.Search(len(c.Manifest.ABI.Methods), func(i int) bool {
|
index := sort.Search(len(c.methods), func(i int) bool {
|
||||||
md := c.Manifest.ABI.Methods[i]
|
md := c.methods[i].MD
|
||||||
if md.Name != desc.Name {
|
if md.Name != desc.Name {
|
||||||
return md.Name >= desc.Name
|
return md.Name >= desc.Name
|
||||||
}
|
}
|
||||||
return len(md.Parameters) > len(desc.Parameters)
|
return len(md.Parameters) > len(desc.Parameters)
|
||||||
})
|
})
|
||||||
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, manifest.Method{})
|
c.methods = append(c.methods, MethodAndPrice{})
|
||||||
copy(c.Manifest.ABI.Methods[index+1:], c.Manifest.ABI.Methods[index:])
|
copy(c.methods[index+1:], c.methods[index:])
|
||||||
c.Manifest.ABI.Methods[index] = *desc
|
c.methods[index] = *md
|
||||||
|
|
||||||
// Cache follows the same order.
|
if md.ActiveFrom != nil {
|
||||||
c.Methods = append(c.Methods, MethodAndPrice{})
|
c.ActiveHFs[*md.ActiveFrom] = struct{}{}
|
||||||
copy(c.Methods[index+1:], c.Methods[index:])
|
}
|
||||||
c.Methods[index] = *md
|
if md.ActiveTill != nil {
|
||||||
|
c.ActiveHFs[*md.ActiveTill] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMethodByOffset returns method with the provided offset.
|
// GetMethodByOffset returns method with the provided offset.
|
||||||
// Offset is offset of `System.Contract.CallNative` syscall.
|
// Offset is offset of `System.Contract.CallNative` syscall.
|
||||||
func (c *ContractMD) GetMethodByOffset(offset int) (MethodAndPrice, bool) {
|
func (c *HFSpecificContractMD) GetMethodByOffset(offset int) (HFSpecificMethodAndPrice, bool) {
|
||||||
for k := range c.Methods {
|
for k := range c.Methods {
|
||||||
if c.Methods[k].SyscallOffset == offset {
|
if c.Methods[k].SyscallOffset == offset {
|
||||||
return c.Methods[k], true
|
return c.Methods[k], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MethodAndPrice{}, false
|
return HFSpecificMethodAndPrice{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMethod returns method `name` with the specified number of parameters.
|
// GetMethod returns method `name` with the specified number of parameters.
|
||||||
func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) {
|
func (c *HFSpecificContractMD) GetMethod(name string, paramCount int) (HFSpecificMethodAndPrice, bool) {
|
||||||
index := sort.Search(len(c.Methods), func(i int) bool {
|
index := sort.Search(len(c.Methods), func(i int) bool {
|
||||||
md := c.Methods[i]
|
md := c.Methods[i]
|
||||||
res := strings.Compare(name, md.MD.Name)
|
res := strings.Compare(name, md.MD.Name)
|
||||||
|
@ -261,15 +407,19 @@ func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, boo
|
||||||
return md, true
|
return md, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MethodAndPrice{}, false
|
return HFSpecificMethodAndPrice{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddEvent adds a new event to the native contract.
|
// AddEvent adds a new event to the native contract.
|
||||||
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
func (c *ContractMD) AddEvent(md Event) {
|
||||||
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
c.events = append(c.events, md)
|
||||||
Name: name,
|
|
||||||
Parameters: ps,
|
if md.ActiveFrom != nil {
|
||||||
})
|
c.ActiveHFs[*md.ActiveFrom] = struct{}{}
|
||||||
|
}
|
||||||
|
if md.ActiveTill != nil {
|
||||||
|
c.ActiveHFs[*md.ActiveTill] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sorts interop functions by id.
|
// Sort sorts interop functions by id.
|
||||||
|
|
|
@ -144,7 +144,7 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, f,
|
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, caller, cs.Hash, f,
|
||||||
hasReturn, methodOff, initOff, onUnload)
|
hasReturn, methodOff, initOff, onUnload)
|
||||||
|
|
||||||
for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- {
|
for e, i := ic.VM.Estack(), len(args)-1; i >= 0; i-- {
|
||||||
|
|
|
@ -80,19 +80,19 @@ func Notify(ic *interop.Context) error {
|
||||||
if len(name) > MaxEventNameLen {
|
if len(name) > MaxEventNameLen {
|
||||||
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
|
return fmt.Errorf("event name must be less than %d", MaxEventNameLen)
|
||||||
}
|
}
|
||||||
curHash := ic.VM.GetCurrentScriptHash()
|
curr := ic.VM.Context().GetManifest()
|
||||||
ctr, err := ic.GetContract(curHash)
|
if curr == nil {
|
||||||
if err != nil {
|
|
||||||
return errors.New("notifications are not allowed in dynamic scripts")
|
return errors.New("notifications are not allowed in dynamic scripts")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
ev = ctr.Manifest.ABI.GetEvent(name)
|
ev = curr.ABI.GetEvent(name)
|
||||||
checkErr error
|
checkErr error
|
||||||
|
curHash = ic.VM.GetCurrentScriptHash()
|
||||||
)
|
)
|
||||||
if ev == nil {
|
if ev == nil {
|
||||||
checkErr = fmt.Errorf("notification %s does not exist", name)
|
checkErr = fmt.Errorf("notification %s does not exist", name)
|
||||||
} else {
|
} else {
|
||||||
err = ev.CheckCompliance(args)
|
err := ev.CheckCompliance(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
checkErr = fmt.Errorf("notification %s is invalid: %w", name, err)
|
checkErr = fmt.Errorf("notification %s is invalid: %w", name, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -637,7 +637,7 @@ func TestNotify(t *testing.T) {
|
||||||
_, _, bc, cs := getDeployedInternal(t)
|
_, _, bc, cs := getDeployedInternal(t)
|
||||||
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
|
ic, err := bc.GetTestVM(trigger.Application, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ic.VM.LoadNEFMethod(&cs.NEF, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
|
ic.VM.LoadNEFMethod(&cs.NEF, &cs.Manifest, caller, cs.Hash, callflag.NoneFlag, true, 0, -1, nil)
|
||||||
ic.VM.Estack().PushVal(args)
|
ic.VM.Estack().PushVal(args)
|
||||||
ic.VM.Estack().PushVal(name)
|
ic.VM.Estack().PushVal(name)
|
||||||
return ic
|
return ic
|
||||||
|
|
|
@ -12,12 +12,14 @@ import (
|
||||||
func TestNamesASCII(t *testing.T) {
|
func TestNamesASCII(t *testing.T) {
|
||||||
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
||||||
cs := NewContracts(cfg)
|
cs := NewContracts(cfg)
|
||||||
|
latestHF := config.LatestHardfork()
|
||||||
for _, c := range cs.Contracts {
|
for _, c := range cs.Contracts {
|
||||||
require.True(t, isASCII(c.Metadata().Name))
|
require.True(t, isASCII(c.Metadata().Name))
|
||||||
for _, m := range c.Metadata().Methods {
|
hfMD := c.Metadata().HFSpecificContractMD(&latestHF)
|
||||||
|
for _, m := range hfMD.Methods {
|
||||||
require.True(t, isASCII(m.MD.Name))
|
require.True(t, isASCII(m.MD.Name))
|
||||||
}
|
}
|
||||||
for _, e := range c.Metadata().Manifest.ABI.Events {
|
for _, e := range hfMD.Manifest.ABI.Events {
|
||||||
require.True(t, isASCII(e.Name))
|
require.True(t, isASCII(e.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts {
|
||||||
cs.Policy = policy
|
cs.Policy = policy
|
||||||
cs.Contracts = append(cs.Contracts, neo, gas, policy)
|
cs.Contracts = append(cs.Contracts, neo, gas, policy)
|
||||||
|
|
||||||
desig := newDesignate(cfg.P2PSigExtensions, cfg.Genesis.Roles)
|
desig := newDesignate(cfg.Genesis.Roles)
|
||||||
desig.NEO = neo
|
desig.NEO = neo
|
||||||
cs.Designate = desig
|
cs.Designate = desig
|
||||||
cs.Contracts = append(cs.Contracts, desig)
|
cs.Contracts = append(cs.Contracts, desig)
|
||||||
|
|
|
@ -12,10 +12,12 @@ import (
|
||||||
func TestNativeGetMethod(t *testing.T) {
|
func TestNativeGetMethod(t *testing.T) {
|
||||||
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
||||||
cs := NewContracts(cfg)
|
cs := NewContracts(cfg)
|
||||||
|
latestHF := config.LatestHardfork()
|
||||||
for _, c := range cs.Contracts {
|
for _, c := range cs.Contracts {
|
||||||
|
hfMD := c.Metadata().HFSpecificContractMD(&latestHF)
|
||||||
t.Run(c.Metadata().Name, func(t *testing.T) {
|
t.Run(c.Metadata().Name, func(t *testing.T) {
|
||||||
for _, m := range c.Metadata().Methods {
|
for _, m := range hfMD.Methods {
|
||||||
_, ok := c.Metadata().GetMethod(m.MD.Name, len(m.MD.Parameters))
|
_, ok := hfMD.GetMethod(m.MD.Name, len(m.MD.Parameters))
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/twmb/murmur3"
|
"github.com/twmb/murmur3"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
@ -28,20 +29,25 @@ type Crypto struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamedCurve identifies named elliptic curves.
|
// HashFunc is a delegate representing a hasher function with 256 bytes output length.
|
||||||
type NamedCurve byte
|
type HashFunc func([]byte) util.Uint256
|
||||||
|
|
||||||
// Various named elliptic curves.
|
// NamedCurveHash identifies a pair of named elliptic curve and hash function.
|
||||||
|
type NamedCurveHash byte
|
||||||
|
|
||||||
|
// Various pairs of named elliptic curves and hash functions.
|
||||||
const (
|
const (
|
||||||
Secp256k1 NamedCurve = 22
|
Secp256k1Sha256 NamedCurveHash = 22
|
||||||
Secp256r1 NamedCurve = 23
|
Secp256r1Sha256 NamedCurveHash = 23
|
||||||
|
Secp256k1Keccak256 NamedCurveHash = 122
|
||||||
|
Secp256r1Keccak256 NamedCurveHash = 123
|
||||||
)
|
)
|
||||||
|
|
||||||
const cryptoContractID = -3
|
const cryptoContractID = -3
|
||||||
|
|
||||||
func newCrypto() *Crypto {
|
func newCrypto() *Crypto {
|
||||||
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
|
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
|
||||||
defer c.UpdateHash()
|
defer c.BuildHFSpecificMD(c.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("sha256", smartcontract.ByteArrayType,
|
desc := newDescriptor("sha256", smartcontract.ByteArrayType,
|
||||||
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
|
@ -64,7 +70,15 @@ func newCrypto() *Crypto {
|
||||||
manifest.NewParameter("pubkey", smartcontract.ByteArrayType),
|
manifest.NewParameter("pubkey", smartcontract.ByteArrayType),
|
||||||
manifest.NewParameter("signature", smartcontract.ByteArrayType),
|
manifest.NewParameter("signature", smartcontract.ByteArrayType),
|
||||||
manifest.NewParameter("curve", smartcontract.IntegerType))
|
manifest.NewParameter("curve", smartcontract.IntegerType))
|
||||||
md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag)
|
md = newMethodAndPrice(c.verifyWithECDsaPreCockatrice, 1<<15, callflag.NoneFlag, config.HFDefault, config.HFCockatrice)
|
||||||
|
c.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("verifyWithECDsa", smartcontract.BoolType,
|
||||||
|
manifest.NewParameter("message", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("pubkey", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("signature", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("curveHash", smartcontract.IntegerType))
|
||||||
|
md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag, config.HFCockatrice)
|
||||||
c.AddMethod(md, desc)
|
c.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("bls12381Serialize", smartcontract.ByteArrayType,
|
desc = newDescriptor("bls12381Serialize", smartcontract.ByteArrayType,
|
||||||
|
@ -104,7 +118,7 @@ func newCrypto() *Crypto {
|
||||||
|
|
||||||
desc = newDescriptor("keccak256", smartcontract.ByteArrayType,
|
desc = newDescriptor("keccak256", smartcontract.ByteArrayType,
|
||||||
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||||
md = newMethodAndPrice(c.keccak256, 1<<15, callflag.NoneFlag)
|
md = newMethodAndPrice(c.keccak256, 1<<15, callflag.NoneFlag, config.HFCockatrice)
|
||||||
c.AddMethod(md, desc)
|
c.AddMethod(md, desc)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -137,12 +151,19 @@ func (c *Crypto) murmur32(_ *interop.Context, args []stackitem.Item) stackitem.I
|
||||||
return stackitem.NewByteArray(result)
|
return stackitem.NewByteArray(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Crypto) verifyWithECDsaPreCockatrice(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
return verifyWithECDsaGeneric(args, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
return verifyWithECDsaGeneric(args, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyWithECDsaGeneric(args []stackitem.Item, allowKeccak bool) stackitem.Item {
|
||||||
msg, err := args[0].TryBytes()
|
msg, err := args[0].TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid message stackitem: %w", err))
|
panic(fmt.Errorf("invalid message stackitem: %w", err))
|
||||||
}
|
}
|
||||||
hashToCheck := hash.Sha256(msg)
|
|
||||||
pubkey, err := args[1].TryBytes()
|
pubkey, err := args[1].TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid pubkey stackitem: %w", err))
|
panic(fmt.Errorf("invalid pubkey stackitem: %w", err))
|
||||||
|
@ -151,10 +172,11 @@ func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stac
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid signature stackitem: %w", err))
|
panic(fmt.Errorf("invalid signature stackitem: %w", err))
|
||||||
}
|
}
|
||||||
curve, err := curveFromStackitem(args[3])
|
curve, hasher, err := curveHasherFromStackitem(args[3], allowKeccak)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid curve stackitem: %w", err))
|
panic(fmt.Errorf("invalid curveHash stackitem: %w", err))
|
||||||
}
|
}
|
||||||
|
hashToCheck := hasher(msg)
|
||||||
pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve)
|
pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to decode pubkey: %w", err))
|
panic(fmt.Errorf("failed to decode pubkey: %w", err))
|
||||||
|
@ -163,22 +185,32 @@ func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stac
|
||||||
return stackitem.NewBool(res)
|
return stackitem.NewBool(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) {
|
func curveHasherFromStackitem(si stackitem.Item, allowKeccak bool) (elliptic.Curve, HashFunc, error) {
|
||||||
curve, err := si.TryInteger()
|
curve, err := si.TryInteger()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if !curve.IsInt64() {
|
if !curve.IsInt64() {
|
||||||
return nil, errors.New("not an int64")
|
return nil, nil, errors.New("not an int64")
|
||||||
}
|
}
|
||||||
c := curve.Int64()
|
c := curve.Int64()
|
||||||
switch c {
|
switch c {
|
||||||
case int64(Secp256k1):
|
case int64(Secp256k1Sha256):
|
||||||
return secp256k1.S256(), nil
|
return secp256k1.S256(), hash.Sha256, nil
|
||||||
case int64(Secp256r1):
|
case int64(Secp256r1Sha256):
|
||||||
return elliptic.P256(), nil
|
return elliptic.P256(), hash.Sha256, nil
|
||||||
|
case int64(Secp256k1Keccak256):
|
||||||
|
if !allowKeccak {
|
||||||
|
return nil, nil, errors.New("unsupported hash type")
|
||||||
|
}
|
||||||
|
return secp256k1.S256(), Keccak256, nil
|
||||||
|
case int64(Secp256r1Keccak256):
|
||||||
|
if !allowKeccak {
|
||||||
|
return nil, nil, errors.New("unsupported hash type")
|
||||||
|
}
|
||||||
|
return elliptic.P256(), Keccak256, nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unsupported curve type")
|
return nil, nil, errors.New("unsupported curve/hash type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,13 +327,7 @@ func (c *Crypto) keccak256(_ *interop.Context, args []stackitem.Item) stackitem.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
return stackitem.NewByteArray(Keccak256(bs).BytesBE())
|
||||||
digest := sha3.NewLegacyKeccak256()
|
|
||||||
_, err = digest.Write(bs)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return stackitem.NewByteArray(digest.Sum(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata implements the Contract interface.
|
// Metadata implements the Contract interface.
|
||||||
|
@ -310,7 +336,7 @@ func (c *Crypto) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (c *Crypto) Initialize(ic *interop.Context) error {
|
func (c *Crypto) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,3 +359,14 @@ func (c *Crypto) PostPersist(ic *interop.Context) error {
|
||||||
func (c *Crypto) ActiveIn() *config.Hardfork {
|
func (c *Crypto) ActiveIn() *config.Hardfork {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keccak256 hashes the incoming byte slice using the
|
||||||
|
// keccak256 algorithm.
|
||||||
|
func Keccak256(data []byte) util.Uint256 {
|
||||||
|
var hash util.Uint256
|
||||||
|
hasher := sha3.NewLegacyKeccak256()
|
||||||
|
_, _ = hasher.Write(data)
|
||||||
|
|
||||||
|
hasher.Sum(hash[:0])
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
@ -118,29 +119,44 @@ func TestMurmur32(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCryptoLibVerifyWithECDsa(t *testing.T) {
|
func TestCryptoLibVerifyWithECDsa(t *testing.T) {
|
||||||
t.Run("R1", func(t *testing.T) {
|
t.Run("R1 sha256", func(t *testing.T) {
|
||||||
testECDSAVerify(t, Secp256r1)
|
testECDSAVerify(t, Secp256r1Sha256)
|
||||||
})
|
})
|
||||||
t.Run("K1", func(t *testing.T) {
|
t.Run("K1 sha256", func(t *testing.T) {
|
||||||
testECDSAVerify(t, Secp256k1)
|
testECDSAVerify(t, Secp256k1Sha256)
|
||||||
|
})
|
||||||
|
t.Run("R1 keccak256", func(t *testing.T) {
|
||||||
|
testECDSAVerify(t, Secp256r1Keccak256)
|
||||||
|
})
|
||||||
|
t.Run("K1 keccak256", func(t *testing.T) {
|
||||||
|
testECDSAVerify(t, Secp256k1Keccak256)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testECDSAVerify(t *testing.T, curve NamedCurve) {
|
func testECDSAVerify(t *testing.T, curve NamedCurveHash) {
|
||||||
var (
|
var (
|
||||||
priv *keys.PrivateKey
|
priv *keys.PrivateKey
|
||||||
err error
|
err error
|
||||||
c = newCrypto()
|
c = newCrypto()
|
||||||
ic = &interop.Context{VM: vm.New()}
|
ic = &interop.Context{VM: vm.New()}
|
||||||
actual stackitem.Item
|
actual stackitem.Item
|
||||||
|
hasher HashFunc
|
||||||
)
|
)
|
||||||
switch curve {
|
switch curve {
|
||||||
case Secp256k1:
|
case Secp256k1Sha256:
|
||||||
priv, err = keys.NewSecp256k1PrivateKey()
|
priv, err = keys.NewSecp256k1PrivateKey()
|
||||||
case Secp256r1:
|
hasher = hash.Sha256
|
||||||
|
case Secp256r1Sha256:
|
||||||
priv, err = keys.NewPrivateKey()
|
priv, err = keys.NewPrivateKey()
|
||||||
|
hasher = hash.Sha256
|
||||||
|
case Secp256k1Keccak256:
|
||||||
|
priv, err = keys.NewSecp256k1PrivateKey()
|
||||||
|
hasher = Keccak256
|
||||||
|
case Secp256r1Keccak256:
|
||||||
|
priv, err = keys.NewPrivateKey()
|
||||||
|
hasher = Keccak256
|
||||||
default:
|
default:
|
||||||
t.Fatal("unknown curve")
|
t.Fatal("unknown curve/hash")
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -162,7 +178,7 @@ func testECDSAVerify(t *testing.T, curve NamedCurve) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := []byte("test message")
|
msg := []byte("test message")
|
||||||
sign := priv.Sign(msg)
|
sign := priv.SignHash(hasher(msg))
|
||||||
|
|
||||||
t.Run("bad message item", func(t *testing.T) {
|
t.Run("bad message item", func(t *testing.T) {
|
||||||
runCase(t, true, false, stackitem.NewInterop("cheburek"), priv.PublicKey().Bytes(), sign, int64(curve))
|
runCase(t, true, false, stackitem.NewInterop("cheburek"), priv.PublicKey().Bytes(), sign, int64(curve))
|
||||||
|
@ -183,7 +199,7 @@ func testECDSAVerify(t *testing.T, curve NamedCurve) {
|
||||||
runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)))
|
runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)))
|
||||||
})
|
})
|
||||||
t.Run("unknown curve", func(t *testing.T) {
|
t.Run("unknown curve", func(t *testing.T) {
|
||||||
runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, int64(123))
|
runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, int64(124))
|
||||||
})
|
})
|
||||||
t.Run("invalid signature", func(t *testing.T) {
|
t.Run("invalid signature", func(t *testing.T) {
|
||||||
s := priv.Sign(msg)
|
s := priv.Sign(msg)
|
||||||
|
@ -254,3 +270,13 @@ func TestCryptolib_ScalarFromBytes_Compat(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKeccak256(t *testing.T) {
|
||||||
|
input := []byte("hello")
|
||||||
|
data := Keccak256(input)
|
||||||
|
|
||||||
|
expected := "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
|
||||||
|
actual := hex.EncodeToString(data.BytesBE())
|
||||||
|
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
|
@ -32,8 +32,6 @@ type Designate struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
|
|
||||||
// p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant.
|
|
||||||
p2pSigExtensionsEnabled bool
|
|
||||||
// initialNodeRoles defines a set of node roles that should be defined at the contract
|
// initialNodeRoles defines a set of node roles that should be defined at the contract
|
||||||
// deployment (initialization).
|
// deployment (initialization).
|
||||||
initialNodeRoles map[noderoles.Role]keys.PublicKeys
|
initialNodeRoles map[noderoles.Role]keys.PublicKeys
|
||||||
|
@ -99,14 +97,14 @@ func copyDesignationCache(src, dst *DesignationCache) {
|
||||||
|
|
||||||
func (s *Designate) isValidRole(r noderoles.Role) bool {
|
func (s *Designate) isValidRole(r noderoles.Role) bool {
|
||||||
return r == noderoles.Oracle || r == noderoles.StateValidator ||
|
return r == noderoles.Oracle || r == noderoles.StateValidator ||
|
||||||
r == noderoles.NeoFSAlphabet || (s.p2pSigExtensionsEnabled && r == noderoles.P2PNotary)
|
r == noderoles.NeoFSAlphabet || r == noderoles.P2PNotary
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.Role]keys.PublicKeys) *Designate {
|
func newDesignate(initialNodeRoles map[noderoles.Role]keys.PublicKeys) *Designate {
|
||||||
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
||||||
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
defer s.BuildHFSpecificMD(s.ActiveIn())
|
||||||
|
|
||||||
s.initialNodeRoles = initialNodeRoles
|
s.initialNodeRoles = initialNodeRoles
|
||||||
defer s.UpdateHash()
|
|
||||||
|
|
||||||
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
||||||
manifest.NewParameter("role", smartcontract.IntegerType),
|
manifest.NewParameter("role", smartcontract.IntegerType),
|
||||||
|
@ -120,9 +118,11 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
|
||||||
md = newMethodAndPrice(s.designateAsRole, 1<<15, callflag.States|callflag.AllowNotify)
|
md = newMethodAndPrice(s.designateAsRole, 1<<15, callflag.States|callflag.AllowNotify)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
s.AddEvent(DesignationEventName,
|
eDesc := newEventDescriptor(DesignationEventName,
|
||||||
manifest.NewParameter("Role", smartcontract.IntegerType),
|
manifest.NewParameter("Role", smartcontract.IntegerType),
|
||||||
manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
|
manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
|
||||||
|
eMD := newEvent(eDesc)
|
||||||
|
s.AddEvent(eMD)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,11 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
|
||||||
// Initialize initializes Designation contract. It is called once at native Management's OnPersist
|
// Initialize initializes Designation contract. It is called once at native Management's OnPersist
|
||||||
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
||||||
// data in the storage.
|
// data in the storage.
|
||||||
func (s *Designate) Initialize(ic *interop.Context) error {
|
func (s *Designate) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != s.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
cache := &DesignationCache{}
|
cache := &DesignationCache{}
|
||||||
ic.DAO.SetCache(s.ID, cache)
|
ic.DAO.SetCache(s.ID, cache)
|
||||||
|
|
||||||
|
@ -153,10 +157,7 @@ func (s *Designate) Initialize(ic *interop.Context) error {
|
||||||
// we can fetch the roles data right from the storage.
|
// we can fetch the roles data right from the storage.
|
||||||
func (s *Designate) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
func (s *Designate) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
||||||
cache := &DesignationCache{}
|
cache := &DesignationCache{}
|
||||||
roles := []noderoles.Role{noderoles.Oracle, noderoles.NeoFSAlphabet, noderoles.StateValidator}
|
roles := []noderoles.Role{noderoles.Oracle, noderoles.NeoFSAlphabet, noderoles.StateValidator, noderoles.P2PNotary}
|
||||||
if s.p2pSigExtensionsEnabled {
|
|
||||||
roles = append(roles, noderoles.P2PNotary)
|
|
||||||
}
|
|
||||||
for _, r := range roles {
|
for _, r := range roles {
|
||||||
err := s.updateCachedRoleData(cache, d, r)
|
err := s.updateCachedRoleData(cache, d, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -182,9 +183,7 @@ func (s *Designate) PostPersist(ic *interop.Context) error {
|
||||||
s.notifyRoleChanged(&cache.oracles, noderoles.Oracle)
|
s.notifyRoleChanged(&cache.oracles, noderoles.Oracle)
|
||||||
s.notifyRoleChanged(&cache.stateVals, noderoles.StateValidator)
|
s.notifyRoleChanged(&cache.stateVals, noderoles.StateValidator)
|
||||||
s.notifyRoleChanged(&cache.neofsAlphabet, noderoles.NeoFSAlphabet)
|
s.notifyRoleChanged(&cache.neofsAlphabet, noderoles.NeoFSAlphabet)
|
||||||
if s.p2pSigExtensionsEnabled {
|
s.notifyRoleChanged(&cache.notaries, noderoles.P2PNotary)
|
||||||
s.notifyRoleChanged(&cache.notaries, noderoles.P2PNotary)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache.rolesChangedFlag = false
|
cache.rolesChangedFlag = false
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -32,17 +32,25 @@ func Call(ic *interop.Context) error {
|
||||||
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
|
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
meta = c.Metadata()
|
genericMeta = c.Metadata()
|
||||||
activeIn = c.ActiveIn()
|
activeIn = c.ActiveIn()
|
||||||
)
|
)
|
||||||
if activeIn != nil {
|
if activeIn != nil {
|
||||||
height, ok := ic.Hardforks[activeIn.String()]
|
height, ok := ic.Hardforks[activeIn.String()]
|
||||||
// Persisting block must not be taken into account, native contract can be called
|
// Persisting block must not be taken into account, native contract can be called
|
||||||
// only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled.
|
// only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled.
|
||||||
if !ok || ic.BlockHeight() < height {
|
if !ok || ic.BlockHeight() < height {
|
||||||
return fmt.Errorf("native contract %s is active after hardfork %s", meta.Name, activeIn.String())
|
return fmt.Errorf("native contract %s is active after hardfork %s", genericMeta.Name, activeIn.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var current config.Hardfork
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
if !ic.IsHardforkEnabled(hf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = hf
|
||||||
|
}
|
||||||
|
meta := genericMeta.HFSpecificContractMD(¤t)
|
||||||
m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
|
m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("method not found")
|
return fmt.Errorf("method not found")
|
||||||
|
|
|
@ -31,7 +31,7 @@ func newLedger() *Ledger {
|
||||||
var l = &Ledger{
|
var l = &Ledger{
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
|
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
|
||||||
}
|
}
|
||||||
defer l.UpdateHash()
|
defer l.BuildHFSpecificMD(l.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("currentHash", smartcontract.Hash256Type)
|
desc := newDescriptor("currentHash", smartcontract.Hash256Type)
|
||||||
md := newMethodAndPrice(l.currentHash, 1<<15, callflag.ReadStates)
|
md := newMethodAndPrice(l.currentHash, 1<<15, callflag.ReadStates)
|
||||||
|
@ -81,7 +81,7 @@ func (l *Ledger) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (l *Ledger) Initialize(ic *interop.Context) error {
|
func (l *Ledger) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ func newManagement() *Management {
|
||||||
var m = &Management{
|
var m = &Management{
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
||||||
}
|
}
|
||||||
defer m.UpdateHash()
|
defer m.BuildHFSpecificMD(m.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
||||||
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
||||||
|
@ -164,9 +164,17 @@ func newManagement() *Management {
|
||||||
m.AddMethod(md, desc)
|
m.AddMethod(md, desc)
|
||||||
|
|
||||||
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
|
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
|
||||||
m.AddEvent(contractDeployNotificationName, hashParam)
|
eDesc := newEventDescriptor(contractDeployNotificationName, hashParam)
|
||||||
m.AddEvent(contractUpdateNotificationName, hashParam)
|
eMD := newEvent(eDesc)
|
||||||
m.AddEvent(contractDestroyNotificationName, hashParam)
|
m.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor(contractUpdateNotificationName, hashParam)
|
||||||
|
eMD = newEvent(eDesc)
|
||||||
|
m.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor(contractDestroyNotificationName, hashParam)
|
||||||
|
eMD = newEvent(eDesc)
|
||||||
|
m.AddEvent(eMD)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +228,11 @@ func (m *Management) getContractByID(ic *interop.Context, args []stackitem.Item)
|
||||||
// GetContract returns a contract with the given hash from the given DAO.
|
// GetContract returns a contract with the given hash from the given DAO.
|
||||||
func GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
func GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
||||||
cache := d.GetROCache(ManagementContractID).(*ManagementCache)
|
cache := d.GetROCache(ManagementContractID).(*ManagementCache)
|
||||||
|
return getContract(cache, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContract returns a contract with the given hash from provided RO or RW cache.
|
||||||
|
func getContract(cache *ManagementCache, hash util.Uint160) (*state.Contract, error) {
|
||||||
cs, ok := cache.contracts[hash]
|
cs, ok := cache.contracts[hash]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, storage.ErrKeyNotFound
|
return nil, storage.ErrKeyNotFound
|
||||||
|
@ -583,27 +596,92 @@ func updateContractCache(cache *ManagementCache, cs *state.Contract) {
|
||||||
func (m *Management) OnPersist(ic *interop.Context) error {
|
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
var cache *ManagementCache
|
var cache *ManagementCache
|
||||||
for _, native := range ic.Natives {
|
for _, native := range ic.Natives {
|
||||||
activeIn := native.ActiveIn()
|
var (
|
||||||
if !(activeIn == nil && ic.Block.Index == 0 ||
|
activeIn = native.ActiveIn()
|
||||||
activeIn != nil && ic.IsHardforkActivation(*activeIn)) {
|
isDeploy bool
|
||||||
|
isUpdate bool
|
||||||
|
latestHF config.Hardfork
|
||||||
|
currentActiveHFs []config.Hardfork
|
||||||
|
)
|
||||||
|
activeHFs := native.Metadata().ActiveHFs
|
||||||
|
isDeploy = activeIn == nil && ic.Block.Index == 0 ||
|
||||||
|
activeIn != nil && ic.IsHardforkActivation(*activeIn)
|
||||||
|
if !isDeploy {
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
|
||||||
|
isUpdate = true
|
||||||
|
activation := hf // avoid loop variable pointer exporting.
|
||||||
|
activeIn = &activation // reuse ActiveIn variable for the initialization hardfork.
|
||||||
|
// Break immediately since native Initialize should be called starting from the first hardfork in a raw
|
||||||
|
// (if there are multiple hardforks with the same enabling height).
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Search for the latest active hardfork to properly construct manifest and
|
||||||
|
// initialize natives for the range of active hardforks.
|
||||||
|
for _, hf := range config.Hardforks {
|
||||||
|
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
|
||||||
|
latestHF = hf
|
||||||
|
currentActiveHFs = append(currentActiveHFs, hf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !(isDeploy || isUpdate) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
md := native.Metadata()
|
md := native.Metadata()
|
||||||
cs := &state.Contract{
|
hfSpecificMD := md.HFSpecificContractMD(&latestHF)
|
||||||
ContractBase: md.ContractBase,
|
base := hfSpecificMD.ContractBase
|
||||||
}
|
var cs *state.Contract
|
||||||
if err := native.Initialize(ic); err != nil {
|
switch {
|
||||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
case isDeploy:
|
||||||
|
cs = &state.Contract{
|
||||||
|
ContractBase: base,
|
||||||
|
}
|
||||||
|
case isUpdate:
|
||||||
|
if cache == nil {
|
||||||
|
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
|
||||||
|
}
|
||||||
|
oldcontract, err := getContract(cache, md.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to retrieve native %s from cache: %w", md.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
contract := *oldcontract // Make a copy, don't ruin cached contract and cache.
|
||||||
|
contract.NEF = base.NEF
|
||||||
|
contract.Manifest = base.Manifest
|
||||||
|
contract.UpdateCounter++
|
||||||
|
cs = &contract
|
||||||
}
|
}
|
||||||
err := putContractState(ic.DAO, cs, false) // Perform cache update manually.
|
err := putContractState(ic.DAO, cs, false) // Perform cache update manually.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to put contract state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deploy hardfork (contract's ActiveIn) is not a part of contract's active hardforks and
|
||||||
|
// allowed to be nil, this, a special initialization call for it.
|
||||||
|
if isDeploy {
|
||||||
|
if err := native.Initialize(ic, activeIn, hfSpecificMD); err != nil {
|
||||||
|
return fmt.Errorf("initializing %s native contract at HF %v: %w", md.Name, activeIn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The rest of activating hardforks also require initialization.
|
||||||
|
for _, hf := range currentActiveHFs {
|
||||||
|
if err := native.Initialize(ic, &hf, hfSpecificMD); err != nil {
|
||||||
|
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
|
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
|
||||||
}
|
}
|
||||||
updateContractCache(cache, cs)
|
updateContractCache(cache, cs)
|
||||||
|
|
||||||
|
ntfName := contractDeployNotificationName
|
||||||
|
if isUpdate {
|
||||||
|
ntfName = contractUpdateNotificationName
|
||||||
|
}
|
||||||
|
m.emitNotification(ic, ntfName, cs.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -666,7 +744,11 @@ func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize implements the Contract interface.
|
// Initialize implements the Contract interface.
|
||||||
func (m *Management) Initialize(ic *interop.Context) error {
|
func (m *Management) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != m.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
|
||||||
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
|
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
||||||
mgmt.Policy = newPolicy(false)
|
mgmt.Policy = newPolicy(false)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||||
ic := &interop.Context{DAO: d}
|
ic := &interop.Context{DAO: d}
|
||||||
err := mgmt.Initialize(ic)
|
err := mgmt.Initialize(ic, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}))
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil))
|
||||||
script := []byte{byte(opcode.RET)}
|
script := []byte{byte(opcode.RET)}
|
||||||
sender := util.Uint160{1, 2, 3}
|
sender := util.Uint160{1, 2, 3}
|
||||||
ne, err := nef.NewFile(script)
|
ne, err := nef.NewFile(script)
|
||||||
|
@ -97,9 +97,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
mgmt := newManagement()
|
mgmt := newManagement()
|
||||||
mgmt.Policy = newPolicy(false)
|
mgmt.Policy = newPolicy(false)
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||||
err := mgmt.Initialize(&interop.Context{DAO: d})
|
err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}))
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil))
|
||||||
err = mgmt.InitializeCache(0, d)
|
err = mgmt.InitializeCache(0, d)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
|
||||||
initialSupply: init,
|
initialSupply: init,
|
||||||
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
||||||
}
|
}
|
||||||
defer g.UpdateHash()
|
defer g.BuildHFSpecificMD(g.ActiveIn())
|
||||||
|
|
||||||
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
|
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
|
||||||
nep17.symbol = "GAS"
|
nep17.symbol = "GAS"
|
||||||
|
@ -83,7 +83,11 @@ func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes a GAS contract.
|
// Initialize initializes a GAS contract.
|
||||||
func (g *GAS) Initialize(ic *interop.Context) error {
|
func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != g.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
if err := g.nep17TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
|
||||||
// newNEO returns NEO native contract.
|
// newNEO returns NEO native contract.
|
||||||
func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
n := &NEO{}
|
n := &NEO{}
|
||||||
defer n.UpdateHash()
|
defer n.BuildHFSpecificMD(n.ActiveIn())
|
||||||
|
|
||||||
nep17 := newNEP17Native(nativenames.Neo, neoContractID)
|
nep17 := newNEP17Native(nativenames.Neo, neoContractID)
|
||||||
nep17.symbol = "NEO"
|
nep17.symbol = "NEO"
|
||||||
|
@ -233,7 +233,7 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("getCommitteeAddress", smartcontract.Hash160Type)
|
desc = newDescriptor("getCommitteeAddress", smartcontract.Hash160Type)
|
||||||
md = newMethodAndPrice(n.getCommitteeAddress, 1<<16, callflag.ReadStates)
|
md = newMethodAndPrice(n.getCommitteeAddress, 1<<16, callflag.ReadStates, config.HFCockatrice)
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType)
|
desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType)
|
||||||
|
@ -258,27 +258,39 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||||
md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States)
|
md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States)
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
n.AddEvent("CandidateStateChanged",
|
eDesc := newEventDescriptor("CandidateStateChanged",
|
||||||
manifest.NewParameter("pubkey", smartcontract.PublicKeyType),
|
manifest.NewParameter("pubkey", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("registered", smartcontract.BoolType),
|
manifest.NewParameter("registered", smartcontract.BoolType),
|
||||||
manifest.NewParameter("votes", smartcontract.IntegerType),
|
manifest.NewParameter("votes", smartcontract.IntegerType),
|
||||||
)
|
)
|
||||||
n.AddEvent("Vote",
|
eMD := newEvent(eDesc)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor("Vote",
|
||||||
manifest.NewParameter("account", smartcontract.Hash160Type),
|
manifest.NewParameter("account", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("from", smartcontract.PublicKeyType),
|
manifest.NewParameter("from", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("to", smartcontract.PublicKeyType),
|
manifest.NewParameter("to", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||||
)
|
)
|
||||||
n.AddEvent("CommitteeChanged",
|
eMD = newEvent(eDesc)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
|
eDesc = newEventDescriptor("CommitteeChanged",
|
||||||
manifest.NewParameter("old", smartcontract.ArrayType),
|
manifest.NewParameter("old", smartcontract.ArrayType),
|
||||||
manifest.NewParameter("new", smartcontract.ArrayType),
|
manifest.NewParameter("new", smartcontract.ArrayType),
|
||||||
)
|
)
|
||||||
|
eMD = newEvent(eDesc, config.HFCockatrice)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes a NEO contract.
|
// Initialize initializes a NEO contract.
|
||||||
func (n *NEO) Initialize(ic *interop.Context) error {
|
func (n *NEO) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != n.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := n.nep17TokenNative.Initialize(ic); err != nil {
|
if err := n.nep17TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
|
@ -45,8 +46,9 @@ func (c *nep17TokenNative) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNEP17Native(name string, id int32) *nep17TokenNative {
|
func newNEP17Native(name string, id int32) *nep17TokenNative {
|
||||||
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id)}
|
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id, func(m *manifest.Manifest) {
|
||||||
n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName}
|
m.SupportedStandards = []string{manifest.NEP17StandardName}
|
||||||
|
})}
|
||||||
|
|
||||||
desc := newDescriptor("symbol", smartcontract.StringType)
|
desc := newDescriptor("symbol", smartcontract.StringType)
|
||||||
md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag)
|
md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag)
|
||||||
|
@ -77,7 +79,9 @@ func newNEP17Native(name string, id int32) *nep17TokenNative {
|
||||||
md.StorageFee = 50
|
md.StorageFee = 50
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
n.AddEvent("Transfer", transferParams...)
|
eDesc := newEventDescriptor("Transfer", transferParams...)
|
||||||
|
eMD := newEvent(eDesc)
|
||||||
|
n.AddEvent(eMD)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -319,12 +323,53 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMethodAndPrice(f interop.Method, cpuFee int64, flags callflag.CallFlag) *interop.MethodAndPrice {
|
// newMethodAndPrice builds method with the provided descriptor and ActiveFrom/ActiveTill hardfork
|
||||||
return &interop.MethodAndPrice{
|
// values consequently specified via activations. [config.HFDefault] specfied as ActiveFrom is treated
|
||||||
Func: f,
|
// as active starting from the genesis block.
|
||||||
CPUFee: cpuFee,
|
func newMethodAndPrice(f interop.Method, cpuFee int64, flags callflag.CallFlag, activations ...config.Hardfork) *interop.MethodAndPrice {
|
||||||
RequiredFlags: flags,
|
md := &interop.MethodAndPrice{
|
||||||
|
HFSpecificMethodAndPrice: interop.HFSpecificMethodAndPrice{
|
||||||
|
Func: f,
|
||||||
|
CPUFee: cpuFee,
|
||||||
|
RequiredFlags: flags,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
if len(activations) > 0 {
|
||||||
|
if activations[0] != config.HFDefault {
|
||||||
|
md.ActiveFrom = &activations[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(activations) > 1 {
|
||||||
|
md.ActiveTill = &activations[1]
|
||||||
|
}
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEventDescriptor(name string, ps ...manifest.Parameter) *manifest.Event {
|
||||||
|
if len(ps) == 0 {
|
||||||
|
ps = []manifest.Parameter{}
|
||||||
|
}
|
||||||
|
return &manifest.Event{
|
||||||
|
Name: name,
|
||||||
|
Parameters: ps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEvent builds event with the provided descriptor and ActiveFrom/ActiveTill hardfork
|
||||||
|
// values consequently specified via activations.
|
||||||
|
func newEvent(desc *manifest.Event, activations ...config.Hardfork) interop.Event {
|
||||||
|
md := interop.Event{
|
||||||
|
HFSpecificEvent: interop.HFSpecificEvent{
|
||||||
|
MD: desc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if len(activations) > 0 {
|
||||||
|
md.ActiveFrom = &activations[0]
|
||||||
|
}
|
||||||
|
if len(activations) > 1 {
|
||||||
|
md.ActiveTill = &activations[1]
|
||||||
|
}
|
||||||
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
func toBigInt(s stackitem.Item) *big.Int {
|
func toBigInt(s stackitem.Item) *big.Int {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
@ -19,7 +20,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func newNativeClient(t *testing.T, name string) *neotest.ContractInvoker {
|
func newNativeClient(t *testing.T, name string) *neotest.ContractInvoker {
|
||||||
bc, acc := chain.NewSingle(t)
|
return newCustomNativeClient(t, name, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCustomNativeClient(t *testing.T, name string, f func(cfg *config.Blockchain)) *neotest.ContractInvoker {
|
||||||
|
bc, acc := chain.NewSingleWithCustomConfig(t, f)
|
||||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||||
|
|
||||||
return e.CommitteeInvoker(e.NativeHash(t, name))
|
return e.CommitteeInvoker(e.NativeHash(t, name))
|
||||||
|
|
893
pkg/core/native/native_test/cryptolib_verification_test.go
Normal file
893
pkg/core/native/native_test/cryptolib_verification_test.go
Normal file
|
@ -0,0 +1,893 @@
|
||||||
|
package native_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
"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/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestCryptoLib_KoblitzVerificationScript builds transaction with custom witness that contains
|
||||||
|
// the Koblitz tx signature bytes and Koblitz signature verification script.
|
||||||
|
// This test ensures that transaction signed by Koblitz key passes verification and can
|
||||||
|
// be successfully accepted to the chain.
|
||||||
|
func TestCryptoLib_KoblitzVerificationScript(t *testing.T) {
|
||||||
|
check := func(
|
||||||
|
t *testing.T,
|
||||||
|
buildVerificationScript func(t *testing.T, pub *keys.PublicKey) []byte,
|
||||||
|
constructMsg func(t *testing.T, magic uint32, tx hash.Hashable) []byte,
|
||||||
|
) {
|
||||||
|
c := newGasClient(t)
|
||||||
|
gasInvoker := c.WithSigners(c.Committee)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
// Consider the user that is able to sign txs only with Secp256k1 private key.
|
||||||
|
// Let this user build, sign and push a GAS transfer transaction from its account
|
||||||
|
// to some other account.
|
||||||
|
pk, err := keys.NewSecp256k1PrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Firstly, we need to build the N3 user's account address based on the user's public key.
|
||||||
|
// The address itself is Hash160 from the verification script corresponding to the user's public key.
|
||||||
|
// Since user's private key belongs to Koblitz curve, we can't use System.Crypto.CheckSig interop
|
||||||
|
// in the verification script. Likely, we have a 'verifyWithECDsa' method in native CriptoLib contract
|
||||||
|
// that is able to check Koblitz signature. So let's build custom verification script based on this call.
|
||||||
|
// The script should call 'verifyWithECDsa' method of native CriptoLib contract with Koblitz curve identifier
|
||||||
|
// and check the provided message signature against the user's Koblitz public key.
|
||||||
|
vrfBytes := buildVerificationScript(t, pk.PublicKey())
|
||||||
|
|
||||||
|
// Construct the user's account script hash. It's effectively a verification script hash.
|
||||||
|
from := hash.Hash160(vrfBytes)
|
||||||
|
|
||||||
|
// Supply this account with some initial balance so that the user is able to pay for his transactions.
|
||||||
|
gasInvoker.Invoke(t, true, "transfer", c.Committee.ScriptHash(), from, 10000_0000_0000, nil)
|
||||||
|
|
||||||
|
// Construct transaction that transfers 5 GAS from the user's account to some other account.
|
||||||
|
to := util.Uint160{1, 2, 3}
|
||||||
|
amount := 5
|
||||||
|
tx := gasInvoker.PrepareInvokeNoSign(t, "transfer", from, to, amount, nil)
|
||||||
|
tx.Signers = []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: from,
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
neotest.AddNetworkFee(t, e.Chain, tx)
|
||||||
|
neotest.AddSystemFee(e.Chain, tx, -1)
|
||||||
|
|
||||||
|
// Add some more network fee to pay for the witness verification. This value may be calculated precisely,
|
||||||
|
// but let's keep some inaccurate value for the test.
|
||||||
|
tx.NetworkFee += 540_0000
|
||||||
|
|
||||||
|
// This transaction (along with the network magic) should be signed by the user's Koblitz private key.
|
||||||
|
msg := constructMsg(t, uint32(e.Chain.GetConfig().Magic), tx)
|
||||||
|
|
||||||
|
// The user has to sign the hash of the message by his Koblitz key.
|
||||||
|
// Please, note that this Keccak256 hash may easily be replaced by sha256 hash if needed.
|
||||||
|
signature := pk.SignHash(native.Keccak256(msg))
|
||||||
|
|
||||||
|
// Ensure that signature verification passes. This line here is just for testing purposes,
|
||||||
|
// it won't be present in the real code.
|
||||||
|
require.True(t, pk.PublicKey().Verify(signature, native.Keccak256(msg).BytesBE()))
|
||||||
|
|
||||||
|
// Build invocation witness script for the user's account.
|
||||||
|
invBytes := buildKoblitzInvocationScript(t, [][]byte{signature})
|
||||||
|
|
||||||
|
// Construct witness for signer #0 (the user itself).
|
||||||
|
tx.Scripts = []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: invBytes,
|
||||||
|
VerificationScript: vrfBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add transaction to the chain. No error is expected on new block addition. Note, that this line performs
|
||||||
|
// all those checks that are executed during transaction acceptance in the real network.
|
||||||
|
e.AddNewBlock(t, tx)
|
||||||
|
|
||||||
|
// Double-check: ensure funds have been transferred.
|
||||||
|
e.CheckGASBalance(t, to, big.NewInt(int64(amount)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The proposed preferable witness verification script
|
||||||
|
// (110 bytes, 2154270 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
check(t, buildKoblitzVerificationScript, constructMessage)
|
||||||
|
|
||||||
|
// Below presented some variations of verification scripts that were also considered, but
|
||||||
|
// they are not as good as the first one.
|
||||||
|
|
||||||
|
// The simplest witness verification script with low length and low execution cost
|
||||||
|
// (98 bytes, 2092530 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256([var-bytes-network-magic, txHash-bytes-BE]).
|
||||||
|
check(t, buildKoblitzVerificationScriptSimpleSingleHash, constructMessageNoHash)
|
||||||
|
|
||||||
|
// Even more simple witness verification script with low length and low execution cost
|
||||||
|
// (95 bytes, 2092320 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256([var-bytes-network-magic, txHash-bytes-BE]).
|
||||||
|
// The difference is that network magic is a static value, thus, both verification script and
|
||||||
|
// user address are network-specific.
|
||||||
|
check(t, buildKoblitzVerificationScriptSimpleSingleHashStaticMagic, constructMessageNoHash)
|
||||||
|
|
||||||
|
// More complicated verification script with higher length and higher execution cost
|
||||||
|
// (136 bytes, 4120620 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256(sha256([var-bytes-network-magic, txHash-bytes-BE])).
|
||||||
|
check(t, buildKoblitzVerificationScriptSimple, constructMessageSimple)
|
||||||
|
|
||||||
|
// Witness verification script that follows the existing standard CheckSig account generation rules
|
||||||
|
// and has larger length and higher execution cost.
|
||||||
|
// (186 bytes, 5116020 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256(sha256([4-bytes-network-magic-LE, txHash-bytes-BE]))
|
||||||
|
check(t, buildKoblitzVerificationScriptCompat, constructMessageCompat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScript builds witness verification script for Koblitz public key.
|
||||||
|
// This method checks
|
||||||
|
//
|
||||||
|
// keccak256([4-bytes-network-magic-LE, txHash-bytes-BE])
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
func buildKoblitzVerificationScript(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = [4-network-magic-bytes-LE, tx-hash-BE]
|
||||||
|
// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic (Integer stackitem), can have 0-5 bytes length serialized.
|
||||||
|
// Convert network magic to 4-bytes-length LE byte array representation.
|
||||||
|
emit.Int(vrf.BinWriter, 0x100000000)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.ADD, // some new number that is 5 bytes at least when serialized, but first 4 bytes are intact network value (LE).
|
||||||
|
opcode.PUSH4, opcode.LEFT) // cut the first 4 bytes out of a number that is at least 5 bytes long, the result is 4-bytes-length LE network representation.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (110 bytes length, always constant length, with constant length of signed data):
|
||||||
|
// NEO-GO-VM > loadbase64 ABhQDCECoIi/qx5LS+3n1GJFcoYbQByyDDsU6QaHvYhiJypOYWZBxfug4AMAAAAAAQAAAJ4UjUEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
|
||||||
|
// READY: loaded 110 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 122 (7a) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 02a088bfab1e4b4bede7d4624572861b401cb20c3b14e90687bd8862272a4e6166
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 PUSHINT64 4294967296 (0000000001000000)
|
||||||
|
// 52 ADD
|
||||||
|
// 53 PUSH4
|
||||||
|
// 54 LEFT
|
||||||
|
// 55 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 60 PUSH0
|
||||||
|
// 61 PICKITEM
|
||||||
|
// 62 CAT
|
||||||
|
// 63 PUSH4
|
||||||
|
// 64 PACK
|
||||||
|
// 65 PUSH0
|
||||||
|
// 66 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 83 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 105 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScriptSimpleSingleHash builds witness verification script for Koblitz public key.
|
||||||
|
// This method differs from buildKoblitzVerificationScriptCompat in that it checks
|
||||||
|
//
|
||||||
|
// keccak256([var-bytes-network-magic, txHash-bytes-BE])
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
func buildKoblitzVerificationScriptSimpleSingleHash(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
// vrf is witness verification script corresponding to the pk.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = [network-magic-bytes, tx.Hash()]
|
||||||
|
// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (98 bytes length, always constant length, with variable length of signed data):
|
||||||
|
// NEO-GO-VM > loadbase64 ABZQDCEDY9ekgSWnbN6m4JjJ8SjoKSDtQo5ftMrx1/gcFsrQwgVBxfug4EEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
|
||||||
|
// READY: loaded 98 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 122 (7a) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 0363d7a48125a76cdea6e098c9f128e82920ed428e5fb4caf1d7f81c16cad0c205
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 48 PUSH0
|
||||||
|
// 49 PICKITEM
|
||||||
|
// 50 CAT
|
||||||
|
// 51 PUSH4
|
||||||
|
// 52 PACK
|
||||||
|
// 53 PUSH0
|
||||||
|
// 54 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 71 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 93 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScriptSimpleSingleHashStaticMagic builds witness verification script for Koblitz public key.
|
||||||
|
// This method differs from buildKoblitzVerificationScriptCompat in that it checks
|
||||||
|
//
|
||||||
|
// keccak256([var-bytes-network-magic, txHash-bytes-BE])
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
//
|
||||||
|
// and it uses static magic value (simple PUSHINT* + magic, or PUSHDATA1 + magicBytes is also possible)
|
||||||
|
// which results in network-specific verification script and, consequently, network-specific user address.
|
||||||
|
func buildKoblitzVerificationScriptSimpleSingleHashStaticMagic(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
// vrf is witness verification script corresponding to the pk.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = [network-magic-bytes, tx.Hash()]
|
||||||
|
// Firstly, push static network magic (it's 42 for unit test chain).
|
||||||
|
emit.Int(vrf.BinWriter, 42)
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (95 bytes length, always constant length, with variable length of signed data):
|
||||||
|
// NEO-GO-VM > loadbase64 ABZQDCECluEwgK3pKiq3IjOMKiSe6Ng6FPZJxoMhZkFl8GvREL0AKkEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
|
||||||
|
// READY: loaded 95 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 122 (7a) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 0296e13080ade92a2ab722338c2a249ee8d83a14f649c68321664165f06bd110bd
|
||||||
|
// 38 PUSHINT8 42 (2a)
|
||||||
|
// 40 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 45 PUSH0
|
||||||
|
// 46 PICKITEM
|
||||||
|
// 47 CAT
|
||||||
|
// 48 PUSH4
|
||||||
|
// 49 PACK
|
||||||
|
// 50 PUSH0
|
||||||
|
// 51 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 68 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 90 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScriptSimple builds witness verification script for Koblitz public key.
|
||||||
|
// This method differs from buildKoblitzVerificationScriptCompat in that it checks
|
||||||
|
//
|
||||||
|
// keccak256(sha256([var-bytes-network-magic, txHash-bytes-BE]))
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
//
|
||||||
|
// It produces constant-length verification script (136 bytes) independently of the network parameters.
|
||||||
|
// However, the length of signed message is variable and depends on the network magic (since network
|
||||||
|
// magic Integer stackitem being converted to Buffer has the resulting byte slice length that depends on
|
||||||
|
// the magic).
|
||||||
|
func buildKoblitzVerificationScriptSimple(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
// vrf is witness verification script corresponding to the pk.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = Sha256([network-magic-bytes, tx.Hash()])
|
||||||
|
// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM, // pick 0-th transaction item (the transaction hash).
|
||||||
|
opcode.CAT, // concatenate network magic and transaction hash; this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
opcode.PUSH1, // push 1 (the number of arguments of 'sha256' method of native CryptoLib).
|
||||||
|
opcode.PACK) // pack arguments for 'sha256' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "sha256", callflag.NoneFlag) // emit the call to 'sha256' itself.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (136 bytes length, always constant length, with variable length of signed data):
|
||||||
|
// NEO-GO-VM 0 > loadbase64 ABZQDCEDp38Tevu0to16RQqloo/jNfgExYmoCElLS2JuuYcH831Bxfug4EEtUQgwEM6LEcAfDAZzaGEyNTYMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1IUwB8MD3ZlcmlmeVdpdGhFQ0RzYQwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUg==
|
||||||
|
// READY: loaded 136 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 122 (7a) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 03a77f137afbb4b68d7a450aa5a28fe335f804c589a808494b4b626eb98707f37d
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 48 PUSH0
|
||||||
|
// 49 PICKITEM
|
||||||
|
// 50 CAT
|
||||||
|
// 51 PUSH1
|
||||||
|
// 52 PACK
|
||||||
|
// 53 PUSH0
|
||||||
|
// 54 PUSHDATA1 736861323536 ("sha256")
|
||||||
|
// 62 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 84 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
// 89 PUSH4
|
||||||
|
// 90 PACK
|
||||||
|
// 91 PUSH0
|
||||||
|
// 92 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 109 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 131 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScript builds custom verification script for the provided Koblitz public key.
|
||||||
|
// It checks that the following message is signed by the provided public key:
|
||||||
|
//
|
||||||
|
// keccak256(sha256([4-bytes-network-magic-LE, txHash-bytes-BE]))
|
||||||
|
//
|
||||||
|
// It produces constant-length verification script (186 bytes) independently of the network parameters.
|
||||||
|
func buildKoblitzVerificationScriptCompat(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = Sha256([4-bytes-network-magic-LE, tx.Hash()])
|
||||||
|
// Firstly, convert network magic (uint32) to LE buffer.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
|
||||||
|
// First byte: n & 0xFF
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DUP)
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF) // TODO: this can be optimize in order not to allocate 0xFF every time, but need to compare execution price.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND,
|
||||||
|
opcode.SWAP, // Swap with the original network n.
|
||||||
|
opcode.PUSH8,
|
||||||
|
opcode.SHR)
|
||||||
|
// Second byte: n >> 8 & 0xFF
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DUP)
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND,
|
||||||
|
opcode.SWAP, // Swap with the n >> 8.
|
||||||
|
opcode.PUSH8,
|
||||||
|
opcode.SHR)
|
||||||
|
// Third byte: n >> 16 & 0xFF
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DUP)
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND,
|
||||||
|
opcode.SWAP, // Swap with the n >> 16.
|
||||||
|
opcode.PUSH8,
|
||||||
|
opcode.SHR)
|
||||||
|
// Fourth byte: n >> 24 & 0xFF
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF) // no DUP is needed since it's the last shift.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND)
|
||||||
|
// Put these 4 bytes into buffer.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.NEWBUFFER) // allocate new 4-bytes-length buffer.
|
||||||
|
emit.Opcodes(vrf.BinWriter,
|
||||||
|
// Set fourth byte.
|
||||||
|
opcode.DUP, opcode.PUSH3,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM,
|
||||||
|
// Set third byte.
|
||||||
|
opcode.DUP, opcode.PUSH2,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM,
|
||||||
|
// Set second byte.
|
||||||
|
opcode.DUP, opcode.PUSH1,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM,
|
||||||
|
// Set first byte.
|
||||||
|
opcode.DUP, opcode.PUSH0,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM)
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM, // pick 0-th transaction item (the transaction hash).
|
||||||
|
opcode.CAT, // concatenate network magic and transaction hash.
|
||||||
|
opcode.PUSH1, // push 1 (the number of arguments of 'sha256' method of native CryptoLib).
|
||||||
|
opcode.PACK) // pack arguments for 'sha256' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "sha256", callflag.NoneFlag) // emit the call to 'sha256' itself.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (186 bytes length, always constant length, the length of signed data is also always constant):
|
||||||
|
// NEO-GO-VM 0 > loadbase64 ABZQDCECYn75w2MePMuPvExbbEnjjM7eWnmvseGwcI+7lYp4AtdBxfug4EoB/wCRUBipSgH/AJFQGKlKAf8AkVAYqQH/AJEUiEoTE1LQShITUtBKERNS0EoQE1LQQS1RCDAQzosRwB8MBnNoYTI1NgwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUhTAHwwPdmVyaWZ5V2l0aEVDRHNhDBQb9XWrEYlohBNhCjWhKIbN4LZsckFifVtS
|
||||||
|
// READY: loaded 186 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 122 (7a) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 02627ef9c3631e3ccb8fbc4c5b6c49e38ccede5a79afb1e1b0708fbb958a7802d7
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 DUP
|
||||||
|
// 44 PUSHINT16 255 (ff00)
|
||||||
|
// 47 AND
|
||||||
|
// 48 SWAP
|
||||||
|
// 49 PUSH8
|
||||||
|
// 50 SHR
|
||||||
|
// 51 DUP
|
||||||
|
// 52 PUSHINT16 255 (ff00)
|
||||||
|
// 55 AND
|
||||||
|
// 56 SWAP
|
||||||
|
// 57 PUSH8
|
||||||
|
// 58 SHR
|
||||||
|
// 59 DUP
|
||||||
|
// 60 PUSHINT16 255 (ff00)
|
||||||
|
// 63 AND
|
||||||
|
// 64 SWAP
|
||||||
|
// 65 PUSH8
|
||||||
|
// 66 SHR
|
||||||
|
// 67 PUSHINT16 255 (ff00)
|
||||||
|
// 70 AND
|
||||||
|
// 71 PUSH4
|
||||||
|
// 72 NEWBUFFER
|
||||||
|
// 73 DUP
|
||||||
|
// 74 PUSH3
|
||||||
|
// 75 PUSH3
|
||||||
|
// 76 ROLL
|
||||||
|
// 77 SETITEM
|
||||||
|
// 78 DUP
|
||||||
|
// 79 PUSH2
|
||||||
|
// 80 PUSH3
|
||||||
|
// 81 ROLL
|
||||||
|
// 82 SETITEM
|
||||||
|
// 83 DUP
|
||||||
|
// 84 PUSH1
|
||||||
|
// 85 PUSH3
|
||||||
|
// 86 ROLL
|
||||||
|
// 87 SETITEM
|
||||||
|
// 88 DUP
|
||||||
|
// 89 PUSH0
|
||||||
|
// 90 PUSH3
|
||||||
|
// 91 ROLL
|
||||||
|
// 92 SETITEM
|
||||||
|
// 93 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 98 PUSH0
|
||||||
|
// 99 PICKITEM
|
||||||
|
// 100 CAT
|
||||||
|
// 101 PUSH1
|
||||||
|
// 102 PACK
|
||||||
|
// 103 PUSH0
|
||||||
|
// 104 PUSHDATA1 736861323536 ("sha256")
|
||||||
|
// 112 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 134 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
// 139 PUSH4
|
||||||
|
// 140 PACK
|
||||||
|
// 141 PUSH0
|
||||||
|
// 142 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 159 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 181 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzInvocationScript builds witness invocation script for the transaction signatures. The signature
|
||||||
|
// itself may be produced by public key over any curve (not required Koblitz, the algorithm is the same).
|
||||||
|
// The signatures expected to be sorted by public key (if multiple signatures are provided).
|
||||||
|
func buildKoblitzInvocationScript(t *testing.T, signatures [][]byte) []byte {
|
||||||
|
//Exactly like during standard
|
||||||
|
// signature verification, the resulting script pushes Koblitz signature bytes onto stack.
|
||||||
|
inv := io.NewBufBinWriter()
|
||||||
|
for _, sig := range signatures {
|
||||||
|
emit.Bytes(inv.BinWriter, sig) // message signature bytes.
|
||||||
|
}
|
||||||
|
require.NoError(t, inv.Err)
|
||||||
|
|
||||||
|
return inv.Bytes()
|
||||||
|
// Here's an example of the resulting single witness invocation script (66 bytes length, always constant length):
|
||||||
|
// NEO-GO-VM > loadbase64 DEBMGKU/MdSizlzaVNDUUbd1zMZQJ43eTaZ4vBCpmkJ/wVh1TYrAWEbFyHhkqq+aYxPCUS43NKJdJTXavcjB8sTP
|
||||||
|
// READY: loaded 66 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHDATA1 4c18a53f31d4a2ce5cda54d0d451b775ccc650278dde4da678bc10a99a427fc158754d8ac05846c5c87864aaaf9a6313c2512e3734a25d2535dabdc8c1f2c4cf <<
|
||||||
|
//
|
||||||
|
// Here's an example of the 3 out of 4 multisignature invocation script (66 * m bytes length, always constant length):
|
||||||
|
// NEO-GO-VM > loadbase64 DEBsPMY3+7sWyZf0gCVcqPzwZ79p+KpeylgtbYIrXp4Tdi6E/8q3DIrEgK7DdVe3YdbfE+VPrpwym/ufBb8MRTB6DED5B9OZDGWdJApRfuy9LeUTa2mLsXP7mBRa181g0Jo7beylWzVgDqHHF2PilECMcLmRbFRknmQm4KgiGkDE+O6ZDEAYt61O2dMfasJHiQD95M5b4mR6NBnDsMTo2e59H3y4YguroVLiUxnQSc4qu9LWvEIKr4/ytjCCuANXOkJmSw8C
|
||||||
|
// READY: loaded 198 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHDATA1 6c3cc637fbbb16c997f480255ca8fcf067bf69f8aa5eca582d6d822b5e9e13762e84ffcab70c8ac480aec37557b761d6df13e54fae9c329bfb9f05bf0c45307a <<
|
||||||
|
// 66 PUSHDATA1 f907d3990c659d240a517eecbd2de5136b698bb173fb98145ad7cd60d09a3b6deca55b35600ea1c71763e294408c70b9916c54649e6426e0a8221a40c4f8ee99
|
||||||
|
// 132 PUSHDATA1 18b7ad4ed9d31f6ac2478900fde4ce5be2647a3419c3b0c4e8d9ee7d1f7cb8620baba152e25319d049ce2abbd2d6bc420aaf8ff2b63082b803573a42664b0f02
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessage constructs message for signing that consists of the
|
||||||
|
// unhashed constant 4-bytes length LE magic and transaction hash bytes:
|
||||||
|
//
|
||||||
|
// [4-bytes-network-magic-LE, txHash-bytes-BE]
|
||||||
|
func constructMessage(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
return hash.GetSignedData(magic, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessageNoHash constructs message for signing that consists of the
|
||||||
|
// unhashed magic and transaction hash bytes:
|
||||||
|
//
|
||||||
|
// [var-bytes-network-magic, txHash-bytes-BE]
|
||||||
|
func constructMessageNoHash(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
m := big.NewInt(int64(magic))
|
||||||
|
return append(m.Bytes(), tx.Hash().BytesBE()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessageCompat constructs message for signing that does not follow N3 rules,
|
||||||
|
// but entails smaller verification script size and smaller verification price:
|
||||||
|
//
|
||||||
|
// sha256([var-bytes-network-magic, txHash-bytes-BE])
|
||||||
|
func constructMessageSimple(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
m := big.NewInt(int64(magic))
|
||||||
|
return hash.Sha256(append(m.Bytes(), tx.Hash().BytesBE()...)).BytesBE()
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessageCompat constructs message for signing following the N3 rules:
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE])
|
||||||
|
func constructMessageCompat(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
return hash.NetSha256(magic, tx).BytesBE()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCryptoLib_KoblitzMultisigVerificationScript builds transaction with custom witness that contains
|
||||||
|
// the Koblitz tx multisignature bytes and Koblitz multisignature verification script.
|
||||||
|
// This test ensures that transaction signed by m out of n Koblitz keys passes verification and can
|
||||||
|
// be successfully accepted to the chain.
|
||||||
|
func TestCryptoLib_KoblitzMultisigVerificationScript(t *testing.T) {
|
||||||
|
check := func(
|
||||||
|
t *testing.T,
|
||||||
|
buildVerificationScript func(t *testing.T, m int, pub keys.PublicKeys) []byte,
|
||||||
|
constructMsg func(t *testing.T, magic uint32, tx hash.Hashable) []byte,
|
||||||
|
) {
|
||||||
|
c := newGasClient(t)
|
||||||
|
gasInvoker := c.WithSigners(c.Committee)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
// Consider 4 users willing to sign 3/4 multisignature transaction Secp256k1 private keys.
|
||||||
|
const (
|
||||||
|
n = 4
|
||||||
|
m = 3
|
||||||
|
)
|
||||||
|
pks := make([]*keys.PrivateKey, n)
|
||||||
|
for i := range pks {
|
||||||
|
var err error
|
||||||
|
pks[i], err = keys.NewSecp256k1PrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
// Sort private keys by their public keys.
|
||||||
|
sort.Slice(pks, func(i, j int) bool {
|
||||||
|
return pks[i].PublicKey().Cmp(pks[j].PublicKey()) < 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// Firstly, we need to build the N3 multisig account address based on the users' public keys.
|
||||||
|
// Pubs must be sorted, exactly like for the standard CheckMultisig.
|
||||||
|
pubs := make(keys.PublicKeys, n)
|
||||||
|
for i := range pks {
|
||||||
|
pubs[i] = pks[i].PublicKey()
|
||||||
|
}
|
||||||
|
vrfBytes := buildVerificationScript(t, m, pubs)
|
||||||
|
|
||||||
|
// Construct the user's account script hash. It's effectively a verification script hash.
|
||||||
|
from := hash.Hash160(vrfBytes)
|
||||||
|
|
||||||
|
// Supply this account with some initial balance so that the user is able to pay for his transactions.
|
||||||
|
gasInvoker.Invoke(t, true, "transfer", c.Committee.ScriptHash(), from, 10000_0000_0000, nil)
|
||||||
|
|
||||||
|
// Construct transaction that transfers 5 GAS from the user's account to some other account.
|
||||||
|
to := util.Uint160{1, 2, 3}
|
||||||
|
amount := 5
|
||||||
|
tx := gasInvoker.PrepareInvokeNoSign(t, "transfer", from, to, amount, nil)
|
||||||
|
tx.Signers = []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: from,
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
neotest.AddNetworkFee(t, e.Chain, tx)
|
||||||
|
neotest.AddSystemFee(e.Chain, tx, -1)
|
||||||
|
|
||||||
|
// Add some more network fee to pay for the witness verification. This value may be calculated precisely,
|
||||||
|
// but let's keep some inaccurate value for the test.
|
||||||
|
tx.NetworkFee = 8995470
|
||||||
|
|
||||||
|
// This transaction (along with the network magic) should be signed by the user's Koblitz private key.
|
||||||
|
msg := constructMsg(t, uint32(e.Chain.GetConfig().Magic), tx)
|
||||||
|
|
||||||
|
// The users have to sign the hash of the message by their Koblitz key. Collect m signatures from first m keys.
|
||||||
|
// Signatures must be sorted by public key.
|
||||||
|
sigs := make([][]byte, m)
|
||||||
|
for i := range sigs {
|
||||||
|
j := i
|
||||||
|
if i > 0 {
|
||||||
|
j++ // Add some shift to ensure that verification script works correctly.
|
||||||
|
}
|
||||||
|
if i > 3 {
|
||||||
|
j++ // Add more shift for large number of public keys for the same purpose.
|
||||||
|
}
|
||||||
|
sigs[i] = pks[j].SignHash(native.Keccak256(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build invocation witness script for the signatures.
|
||||||
|
invBytes := buildKoblitzInvocationScript(t, sigs)
|
||||||
|
|
||||||
|
// Construct witness for signer #0 (the multisig account itself).
|
||||||
|
tx.Scripts = []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: invBytes,
|
||||||
|
VerificationScript: vrfBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add transaction to the chain. No error is expected on new block addition. Note, that this line performs
|
||||||
|
// all those checks that are executed during transaction acceptance in the real network.
|
||||||
|
e.AddNewBlock(t, tx)
|
||||||
|
|
||||||
|
// Double-check: ensure funds have been transferred.
|
||||||
|
e.CheckGASBalance(t, to, big.NewInt(int64(amount)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The proposed multisig verification script.
|
||||||
|
// (264 bytes, 8390070 GAS including Invocation script execution for 3/4 multisig).
|
||||||
|
// The user has to sign the keccak256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
check(t, buildKoblitzMultisigVerificationScript, constructMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzMultisigVerificationScript builds witness verification script for m signatures out of n Koblitz public keys.
|
||||||
|
// Public keys must be sorted. Signatures (pushed by witness Invocation script) must be sorted by public keys.
|
||||||
|
// It checks m out of n multisignature of the following message:
|
||||||
|
//
|
||||||
|
// keccak256([4-bytes-network-magic-LE, txHash-bytes-BE])
|
||||||
|
func buildKoblitzMultisigVerificationScript(t *testing.T, m int, pubs keys.PublicKeys) []byte {
|
||||||
|
if len(pubs) == 0 {
|
||||||
|
t.Fatalf("empty pubs list")
|
||||||
|
}
|
||||||
|
if m > len(pubs) {
|
||||||
|
t.Fatalf("m must be not greater than the number of public keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(pubs) // public keys must be sorted.
|
||||||
|
cryptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// In fact, the following algorithm is implemented via NeoVM instructions:
|
||||||
|
//
|
||||||
|
// func Check(sigs []interop.Signature) bool {
|
||||||
|
// if m != len(sigs) {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// var pubs []interop.PublicKey = []interop.PublicKey{...}
|
||||||
|
// msg := append(convert.ToBytes(runtime.GetNetwork()), runtime.GetScriptContainer().Hash...)
|
||||||
|
// var sigCnt = 0
|
||||||
|
// var pubCnt = 0
|
||||||
|
// for ; sigCnt < m && pubCnt < n; { // sigs must be sorted by pub
|
||||||
|
// sigCnt += crypto.VerifyWithECDsa(msg, pubs[pubCnt], sigs[sigCnt], crypto.Secp256k1Keccak256)
|
||||||
|
// pubCnt++
|
||||||
|
// }
|
||||||
|
// return sigCnt == m
|
||||||
|
// }
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
|
||||||
|
// Start the same way as regular multisig script.
|
||||||
|
emit.Int(vrf.BinWriter, int64(m)) // push m.
|
||||||
|
for _, pub := range pubs {
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // push public keys in compressed form.
|
||||||
|
}
|
||||||
|
emit.Int(vrf.BinWriter, int64(n)) // push n.
|
||||||
|
|
||||||
|
// Initialize slots for local variables. Locals slot scheme:
|
||||||
|
// LOC0 -> sigs
|
||||||
|
// LOC1 -> pubs
|
||||||
|
// LOC2 -> msg (ByteString)
|
||||||
|
// LOC3 -> sigCnt (Integer)
|
||||||
|
// LOC4 -> pubCnt (Integer)
|
||||||
|
// LOC5 -> n
|
||||||
|
// LOC6 -> m
|
||||||
|
emit.InitSlot(vrf.BinWriter, 7, 0)
|
||||||
|
|
||||||
|
// Store n.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.STLOC5)
|
||||||
|
|
||||||
|
// Pack public keys and store at LOC1.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC5)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PACK, opcode.STLOC1)
|
||||||
|
|
||||||
|
// Store m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.STLOC6)
|
||||||
|
|
||||||
|
// Check the number of signatures is m. Abort the execution if not.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DEPTH) // push the number of signatures onto stack.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // load m.
|
||||||
|
emit.Instruction(vrf.BinWriter, opcode.JMPEQ, []byte{0}) // here and below short jumps are sufficient.
|
||||||
|
sigsLenCheckEndOffset := vrf.Len() // offset of the signatures count check.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.ABORT) // abort execution if length of the signatures not equal to m.
|
||||||
|
|
||||||
|
// Start the check.
|
||||||
|
checkStartOffset := vrf.Len()
|
||||||
|
|
||||||
|
// Pack signatures and store at LOC0.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // load m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PACK, opcode.STLOC0)
|
||||||
|
|
||||||
|
// Get message and store it at LOC2.
|
||||||
|
// msg = [4-network-magic-bytes-LE, tx-hash-BE]
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic (Integer stackitem), can have 0-5 bytes length serialized.
|
||||||
|
// Convert network magic to 4-bytes-length LE byte array representation.
|
||||||
|
emit.Int(vrf.BinWriter, 0x100000000)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.ADD, // some new number that is 5 bytes at least when serialized, but first 4 bytes are intact network value (LE).
|
||||||
|
opcode.PUSH4, opcode.LEFT) // cut the first 4 bytes out of a number that is at least 5 bytes long, the result is 4-bytes-length LE network representation.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.STLOC2) // store msg as a local variable #2.
|
||||||
|
|
||||||
|
// Initialize local variables: sigCnt, pubCnt.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.STLOC3, // initialize sigCnt.
|
||||||
|
opcode.PUSH0, opcode.STLOC4) // initialize pubCnt.
|
||||||
|
|
||||||
|
// Loop condition check.
|
||||||
|
loopStartOffset := vrf.Len()
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC3) // load sigCnt.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // push m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.GE, // sigCnt >= m
|
||||||
|
opcode.LDLOC4) // load pubCnt
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC5) // push n.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.GE, // pubCnt >= n
|
||||||
|
opcode.OR) // sigCnt >= m || pubCnt >= n
|
||||||
|
emit.Instruction(vrf.BinWriter, opcode.JMPIF, []byte{0}) // jump to the end of the script if (sigCnt >= m || pubCnt >= n).
|
||||||
|
loopConditionOffset := vrf.Len()
|
||||||
|
|
||||||
|
// Loop start. Prepare arguments and call CryptoLib's verifyWithECDsa.
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC0, // load signatures.
|
||||||
|
opcode.LDLOC3, // load sigCnt.
|
||||||
|
opcode.PICKITEM, // pick signature at index sigCnt.
|
||||||
|
opcode.LDLOC1, // load pubs.
|
||||||
|
opcode.LDLOC4, // load pubCnt.
|
||||||
|
opcode.PICKITEM, // pick pub at index pubCnt.
|
||||||
|
opcode.LDLOC2, // load msg.
|
||||||
|
opcode.PUSH4, opcode.PACK) // pack 4 arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, cryptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
|
||||||
|
// Update loop variables.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC3, opcode.ADD, opcode.STLOC3, // increment sigCnt if signature is valid.
|
||||||
|
opcode.LDLOC4, opcode.INC, opcode.STLOC4) // increment pubCnt.
|
||||||
|
|
||||||
|
// End of the loop.
|
||||||
|
emit.Instruction(vrf.BinWriter, opcode.JMP, []byte{0}) // jump to the start of cycle.
|
||||||
|
loopEndOffset := vrf.Len()
|
||||||
|
|
||||||
|
// Return condition: the number of valid signatures should be equal to m.
|
||||||
|
progRetOffset := vrf.Len()
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC3) // load sigCnt.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // push m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.NUMEQUAL) // push m == sigCnt.
|
||||||
|
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
script := vrf.Bytes()
|
||||||
|
|
||||||
|
// Set JMP* instructions offsets. "-1" is for short JMP parameter offset. JMP parameters
|
||||||
|
// are relative offsets.
|
||||||
|
script[sigsLenCheckEndOffset-1] = byte(checkStartOffset - sigsLenCheckEndOffset + 2)
|
||||||
|
script[loopEndOffset-1] = byte(loopStartOffset - loopEndOffset + 2)
|
||||||
|
script[loopConditionOffset-1] = byte(progRetOffset - loopConditionOffset + 2)
|
||||||
|
|
||||||
|
return script
|
||||||
|
// Here's an example of the resulting single witness invocation script (264 bytes length, the length may vary depending on m/n):
|
||||||
|
// NEO-GO-VM > loadbase64 EwwhAg1khs9yqTuG8R7dEj8/GhCqKwkL+6shSOczeaHENFo8DCECibz2wVNY1zRkRCbn+Qr87lQFjStnrQrwv1CSoea/91sMIQPiiV+wNGl5g5SVULR+BM/G2n6WO0WrGIsq+GBRqQHYwAwhAuwZz40NwnerrmSusUUgNqsZiv0WFj3KQE1BYd7lU7mDFFcHAHVtwHF2Q24oAzhuwHBBxfug4AMAAAAAAQAAAJ4UjUEtUQgwEM6LchBzEHRrbrhsbbiSJEIAGGhrzmlszmoUwB8MD3ZlcmlmeVdpdGhFQ0RzYQwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUmuec2ycdCK5a26z
|
||||||
|
// READY: loaded 264 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSH3 <<
|
||||||
|
// 1 PUSHDATA1 020d6486cf72a93b86f11edd123f3f1a10aa2b090bfbab2148e73379a1c4345a3c
|
||||||
|
// 36 PUSHDATA1 0289bcf6c15358d734644426e7f90afcee54058d2b67ad0af0bf5092a1e6bff75b
|
||||||
|
// 71 PUSHDATA1 03e2895fb034697983949550b47e04cfc6da7e963b45ab188b2af86051a901d8c0
|
||||||
|
// 106 PUSHDATA1 02ec19cf8d0dc277abae64aeb1452036ab198afd16163dca404d4161dee553b983
|
||||||
|
// 141 PUSH4
|
||||||
|
// 142 INITSLOT 7 local, 0 arg
|
||||||
|
// 145 STLOC5
|
||||||
|
// 146 LDLOC5
|
||||||
|
// 147 PACK
|
||||||
|
// 148 STLOC1
|
||||||
|
// 149 STLOC6
|
||||||
|
// 150 DEPTH
|
||||||
|
// 151 LDLOC6
|
||||||
|
// 152 JMPEQ 155 (3/03)
|
||||||
|
// 154 ABORT
|
||||||
|
// 155 LDLOC6
|
||||||
|
// 156 PACK
|
||||||
|
// 157 STLOC0
|
||||||
|
// 158 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 163 PUSHINT64 4294967296 (0000000001000000)
|
||||||
|
// 172 ADD
|
||||||
|
// 173 PUSH4
|
||||||
|
// 174 LEFT
|
||||||
|
// 175 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 180 PUSH0
|
||||||
|
// 181 PICKITEM
|
||||||
|
// 182 CAT
|
||||||
|
// 183 STLOC2
|
||||||
|
// 184 PUSH0
|
||||||
|
// 185 STLOC3
|
||||||
|
// 186 PUSH0
|
||||||
|
// 187 STLOC4
|
||||||
|
// 188 LDLOC3
|
||||||
|
// 189 LDLOC6
|
||||||
|
// 190 GE
|
||||||
|
// 191 LDLOC4
|
||||||
|
// 192 LDLOC5
|
||||||
|
// 193 GE
|
||||||
|
// 194 OR
|
||||||
|
// 195 JMPIF 261 (66/42)
|
||||||
|
// 197 PUSHINT8 122 (7a)
|
||||||
|
// 199 LDLOC0
|
||||||
|
// 200 LDLOC3
|
||||||
|
// 201 PICKITEM
|
||||||
|
// 202 LDLOC1
|
||||||
|
// 203 LDLOC4
|
||||||
|
// 204 PICKITEM
|
||||||
|
// 205 LDLOC2
|
||||||
|
// 206 PUSH4
|
||||||
|
// 207 PACK
|
||||||
|
// 208 PUSH0
|
||||||
|
// 209 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 226 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 248 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
// 253 LDLOC3
|
||||||
|
// 254 ADD
|
||||||
|
// 255 STLOC3
|
||||||
|
// 256 LDLOC4
|
||||||
|
// 257 INC
|
||||||
|
// 258 STLOC4
|
||||||
|
// 259 JMP 188 (-71/b9)
|
||||||
|
// 261 LDLOC3
|
||||||
|
// 262 LDLOC6
|
||||||
|
// 263 NUMEQUAL
|
||||||
|
}
|
|
@ -6,9 +6,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
ojson "github.com/nspcc-dev/go-ordered-json"
|
||||||
"github.com/nspcc-dev/neo-go/internal/contracts"
|
"github.com/nspcc-dev/neo-go/internal/contracts"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"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/storage"
|
||||||
|
@ -30,10 +34,47 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// defaultCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
|
||||||
|
// under assumption that all hardforks are disabled.
|
||||||
|
defaultCSS = map[string]string{
|
||||||
|
nativenames.Management: `{"id":-1,"hash":"0xfffdc93764dbaddd97c48f252a53ea4643faa3fd","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"ContractManagement","abi":{"methods":[{"name":"deploy","offset":0,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Array","safe":false},{"name":"deploy","offset":7,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Array","safe":false},{"name":"destroy","offset":14,"parameters":[],"returntype":"Void","safe":false},{"name":"getContract","offset":21,"parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getContractById","offset":28,"parameters":[{"name":"id","type":"Integer"}],"returntype":"Array","safe":true},{"name":"getContractHashes","offset":35,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getMinimumDeploymentFee","offset":42,"parameters":[],"returntype":"Integer","safe":true},{"name":"hasMethod","offset":49,"parameters":[{"name":"hash","type":"Hash160"},{"name":"method","type":"String"},{"name":"pcount","type":"Integer"}],"returntype":"Boolean","safe":true},{"name":"setMinimumDeploymentFee","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"update","offset":63,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"update","offset":70,"parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Deploy","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Update","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Destroy","parameters":[{"name":"Hash","type":"Hash160"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.StdLib: `{"id":-2,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"StdLib","abi":{"methods":[{"name":"atoi","offset":0,"parameters":[{"name":"value","type":"String"}],"returntype":"Integer","safe":true},{"name":"atoi","offset":7,"parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"base58CheckDecode","offset":14,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58CheckEncode","offset":21,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base58Decode","offset":28,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58Encode","offset":35,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base64Decode","offset":42,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base64Encode","offset":49,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"deserialize","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"itoa","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"String","safe":true},{"name":"itoa","offset":70,"parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","safe":true},{"name":"jsonDeserialize","offset":77,"parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"jsonSerialize","offset":84,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"memoryCompare","offset":91,"parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":98,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":105,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":112,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","safe":true},{"name":"serialize","offset":119,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"strLen","offset":126,"parameters":[{"name":"str","type":"String"}],"returntype":"Integer","safe":true},{"name":"stringSplit","offset":133,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","safe":true},{"name":"stringSplit","offset":140,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":2135988409},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":42,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":49,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":63,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curve","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Ledger: `{"id":-4,"hash":"0xda65b600f7124ce6c79950c1772a36403104f2be","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"LedgerContract","abi":{"methods":[{"name":"currentHash","offset":0,"parameters":[],"returntype":"Hash256","safe":true},{"name":"currentIndex","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getBlock","offset":14,"parameters":[{"name":"indexOrHash","type":"ByteArray"}],"returntype":"Array","safe":true},{"name":"getTransaction","offset":21,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","safe":true},{"name":"getTransactionFromBlock","offset":28,"parameters":[{"name":"blockIndexOrHash","type":"ByteArray"},{"name":"txIndex","type":"Integer"}],"returntype":"Array","safe":true},{"name":"getTransactionHeight","offset":35,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","safe":true},{"name":"getTransactionSigners","offset":42,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","safe":true},{"name":"getTransactionVMState","offset":49,"parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":65467259},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getGasPerBlock","offset":49,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":56,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":63,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":70,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":77,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":84,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":91,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":98,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":105,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":112,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":119,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":126,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Gas: `{"id":-6,"hash":"0xd2a4cff31913016155e38e474a2c06d08be276cf","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"GasToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"symbol","offset":14,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Policy: `{"id":-7,"hash":"0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"PolicyContract","abi":{"methods":[{"name":"blockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false},{"name":"getAttributeFee","offset":7,"parameters":[{"name":"attributeType","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"getExecFeeFactor","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"getFeePerByte","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"getStoragePrice","offset":28,"parameters":[],"returntype":"Integer","safe":true},{"name":"isBlocked","offset":35,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":true},{"name":"setAttributeFee","offset":42,"parameters":[{"name":"attributeType","type":"Integer"},{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setExecFeeFactor","offset":49,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setFeePerByte","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setStoragePrice","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"unblockAccount","offset":70,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Oracle: `{"id":-9,"hash":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"OracleContract","abi":{"methods":[{"name":"finish","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"getPrice","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"request","offset":14,"parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setPrice","offset":21,"parameters":[{"name":"price","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":28,"parameters":[],"returntype":"Boolean","safe":true}],"events":[{"name":"OracleRequest","parameters":[{"name":"Id","type":"Integer"},{"name":"RequestContract","type":"Hash160"},{"name":"Url","type":"String"},{"name":"Filter","type":"String"}]},{"name":"OracleResponse","parameters":[{"name":"Id","type":"Integer"},{"name":"OriginalTx","type":"Hash256"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Notary: `{"id":-10,"hash":"0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"Notary","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"expirationOf","offset":7,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"getMaxNotValidBeforeDelta","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"lockDepositUntil","offset":21,"parameters":[{"name":"address","type":"Hash160"},{"name":"till","type":"Integer"}],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"setMaxNotValidBeforeDelta","offset":35,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":42,"parameters":[{"name":"signature","type":"Signature"}],"returntype":"Boolean","safe":true},{"name":"withdraw","offset":49,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
}
|
||||||
|
// cockatriceCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
|
||||||
|
// under assumption that hardforks from Aspidochelone to Cockatrice (included) are enabled.
|
||||||
|
cockatriceCSS = map[string]string{
|
||||||
|
nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"keccak256","offset":42,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":49,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":70,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1325686241},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":77,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":84,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":91,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":98,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":105,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":112,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":119,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":126,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":133,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for k, v := range defaultCSS {
|
||||||
|
if _, ok := cockatriceCSS[k]; !ok {
|
||||||
|
cockatriceCSS[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
return newNativeClient(t, nativenames.Management)
|
return newNativeClient(t, nativenames.Management)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newCustomManagementClient returns native Management invoker backed with chain with
|
||||||
|
// specified custom configuration.
|
||||||
|
func newCustomManagementClient(t *testing.T, f func(cfg *config.Blockchain)) *neotest.ContractInvoker {
|
||||||
|
return newCustomNativeClient(t, nativenames.Management, f)
|
||||||
|
}
|
||||||
|
|
||||||
func TestManagement_MinimumDeploymentFee(t *testing.T) {
|
func TestManagement_MinimumDeploymentFee(t *testing.T) {
|
||||||
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
testGetSet(t, newManagementClient(t), "MinimumDeploymentFee", 10_00000000, 0, 0)
|
||||||
}
|
}
|
||||||
|
@ -43,6 +84,253 @@ func TestManagement_MinimumDeploymentFeeCache(t *testing.T) {
|
||||||
testGetSetCache(t, c, "MinimumDeploymentFee", 10_00000000)
|
testGetSetCache(t, c, "MinimumDeploymentFee", 10_00000000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestManagement_GenesisNativeState(t *testing.T) {
|
||||||
|
// check ensures that contract state stored in native Management matches the expected one.
|
||||||
|
check := func(t *testing.T, c *neotest.ContractInvoker, expected map[string]string) {
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
h := state.CreateNativeContractHash(name)
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
si := stack[0]
|
||||||
|
var cs = &state.Contract{}
|
||||||
|
require.NoError(t, cs.FromStackItem(si), name)
|
||||||
|
jBytes, err := ojson.Marshal(cs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
|
||||||
|
}, "getContract", h.BytesBE())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("disabled hardforks", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("remotely enabled hardforks", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 100500,
|
||||||
|
config.HFBasilisk.String(): 100500,
|
||||||
|
config.HFCockatrice.String(): 100500,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("Aspidochelone enabled", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("Basilisk enabled", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, defaultCSS)
|
||||||
|
})
|
||||||
|
t.Run("Cockatrice enabled", func(t *testing.T) {
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): 0,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
check(t, mgmt, cockatriceCSS)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
|
||||||
|
const cockatriceHeight = 3
|
||||||
|
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
e := mgmt.Executor
|
||||||
|
|
||||||
|
// Check Deploy notifications.
|
||||||
|
aer, err := mgmt.Chain.GetAppExecResults(e.GetBlockByIndex(t, 0).Hash(), trigger.OnPersist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(aer))
|
||||||
|
var expected []state.NotificationEvent
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
switch name {
|
||||||
|
case nativenames.Gas:
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.GasToken,
|
||||||
|
Name: "Transfer",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Null{},
|
||||||
|
stackitem.Make(mgmt.Validator.ScriptHash()),
|
||||||
|
stackitem.Make(mgmt.Chain.GetConfig().InitialGASSupply),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
case nativenames.Neo:
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.NeoToken,
|
||||||
|
Name: "Transfer",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Null{},
|
||||||
|
stackitem.Make(mgmt.Validator.ScriptHash()),
|
||||||
|
stackitem.Make(native.NEOTotalSupply),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.ContractManagement,
|
||||||
|
Name: "Deploy",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Make(state.CreateNativeContractHash(name)),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, aer[0].Events)
|
||||||
|
|
||||||
|
// Generate some blocks and check Update notifications.
|
||||||
|
cockatriceBlock := mgmt.GenerateNewBlocks(t, cockatriceHeight)[cockatriceHeight-1]
|
||||||
|
aer, err = mgmt.Chain.GetAppExecResults(cockatriceBlock.Hash(), trigger.OnPersist)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(aer))
|
||||||
|
expected = expected[:0]
|
||||||
|
for _, name := range []string{nativenames.CryptoLib, nativenames.Neo} {
|
||||||
|
expected = append(expected, state.NotificationEvent{
|
||||||
|
ScriptHash: nativehashes.ContractManagement,
|
||||||
|
Name: "Update",
|
||||||
|
Item: stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Make(state.CreateNativeContractHash(name)),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, aer[0].Events)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagement_NativeUpdate(t *testing.T) {
|
||||||
|
const cockatriceHeight = 3
|
||||||
|
c := newCustomManagementClient(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add some blocks up to the Cockatrice enabling height and check the default natives state.
|
||||||
|
for i := 0; i < cockatriceHeight-1; i++ {
|
||||||
|
c.AddNewBlock(t)
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
h := state.CreateNativeContractHash(name)
|
||||||
|
cs := c.Chain.GetContractState(h)
|
||||||
|
require.NotNil(t, cs, name)
|
||||||
|
jBytes, err := ojson.Marshal(cs)
|
||||||
|
require.NoError(t, err, name)
|
||||||
|
require.Equal(t, defaultCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Cockatrice block and check the updated native state.
|
||||||
|
c.AddNewBlock(t)
|
||||||
|
for _, name := range nativenames.All {
|
||||||
|
h := state.CreateNativeContractHash(name)
|
||||||
|
cs := c.Chain.GetContractState(h)
|
||||||
|
require.NotNil(t, cs, name)
|
||||||
|
if name == nativenames.Neo || name == nativenames.CryptoLib {
|
||||||
|
// A tiny hack to reuse cockatriceCSS map in the check below.
|
||||||
|
require.Equal(t, uint16(1), cs.UpdateCounter, name)
|
||||||
|
cs.UpdateCounter--
|
||||||
|
}
|
||||||
|
jBytes, err := ojson.Marshal(cs)
|
||||||
|
require.NoError(t, err, name)
|
||||||
|
require.Equal(t, cockatriceCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManagement_NativeUpdate_Call(t *testing.T) {
|
||||||
|
const (
|
||||||
|
cockatriceHeight = 3
|
||||||
|
method = "getCommitteeAddress"
|
||||||
|
)
|
||||||
|
c := newCustomNativeClient(t, nativenames.Neo, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Invoke Cockatrice-dependant method before Cockatrice should fail.
|
||||||
|
for i := 0; i < cockatriceHeight-1; i++ {
|
||||||
|
c.InvokeFail(t, "at instruction 45 (SYSCALL): System.Contract.Call failed: method not found: getCommitteeAddress/0", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke Cockatrice-dependant method at Cockatrice should be OK.
|
||||||
|
tx := c.NewUnsignedTx(t, c.Hash, method)
|
||||||
|
c.SignTx(t, tx, 1_0000_0000, c.Signers...)
|
||||||
|
c.AddNewBlock(t, tx)
|
||||||
|
c.CheckHalt(t, tx.Hash(), stackitem.Make(c.CommitteeHash))
|
||||||
|
|
||||||
|
// Invoke Cockatrice-dependant method after Cockatrice should be OK.
|
||||||
|
c.Invoke(t, c.CommitteeHash, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockchain_GetNatives ensures that Blockchain's GetNatives API works as expected with
|
||||||
|
// different block heights depending on hardfork settings. This test is located here since it
|
||||||
|
// depends on defaultCSS and cockatriceCSS.
|
||||||
|
func TestBlockchain_GetNatives(t *testing.T) {
|
||||||
|
const cockatriceHeight = 3
|
||||||
|
bc, acc := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) {
|
||||||
|
cfg.Hardforks = map[string]uint32{
|
||||||
|
config.HFAspidochelone.String(): 0,
|
||||||
|
config.HFBasilisk.String(): 0,
|
||||||
|
config.HFCockatrice.String(): cockatriceHeight,
|
||||||
|
}
|
||||||
|
cfg.P2PSigExtensions = true
|
||||||
|
})
|
||||||
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||||
|
|
||||||
|
// Check genesis-based native contract states.
|
||||||
|
natives := bc.GetNatives()
|
||||||
|
require.Equal(t, len(nativenames.All), len(natives))
|
||||||
|
for _, cs := range natives {
|
||||||
|
csFull := state.Contract{
|
||||||
|
ContractBase: cs.ContractBase,
|
||||||
|
UpdateCounter: 0,
|
||||||
|
}
|
||||||
|
jBytes, err := ojson.Marshal(csFull)
|
||||||
|
require.NoError(t, err, cs.Manifest.Name)
|
||||||
|
require.Equal(t, defaultCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check native state after update.
|
||||||
|
e.GenerateNewBlocks(t, cockatriceHeight)
|
||||||
|
natives = bc.GetNatives()
|
||||||
|
require.Equal(t, len(nativenames.All), len(natives))
|
||||||
|
for _, cs := range natives {
|
||||||
|
csFull := state.Contract{
|
||||||
|
ContractBase: cs.ContractBase,
|
||||||
|
UpdateCounter: 0, // Since we're comparing only state.NativeContract part, set the update counter to 0 to match the cockatriceCSS.
|
||||||
|
}
|
||||||
|
jBytes, err := ojson.Marshal(csFull)
|
||||||
|
require.NoError(t, err, cs.Manifest.Name)
|
||||||
|
require.Equal(t, cockatriceCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestManagement_ContractCache(t *testing.T) {
|
func TestManagement_ContractCache(t *testing.T) {
|
||||||
c := newManagementClient(t)
|
c := newManagementClient(t)
|
||||||
managementInvoker := c.WithSigners(c.Committee)
|
managementInvoker := c.WithSigners(c.Committee)
|
||||||
|
|
|
@ -39,7 +39,7 @@ func newNeoCommitteeClient(t *testing.T, expectedGASBalance int) *neotest.Contra
|
||||||
e := neotest.NewExecutor(t, bc, validators, committee)
|
e := neotest.NewExecutor(t, bc, validators, committee)
|
||||||
|
|
||||||
if expectedGASBalance > 0 {
|
if expectedGASBalance > 0 {
|
||||||
e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, 100_0000_0000, nil)
|
e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, expectedGASBalance, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.CommitteeInvoker(e.NativeHash(t, nativenames.Neo))
|
return e.CommitteeInvoker(e.NativeHash(t, nativenames.Neo))
|
||||||
|
|
70
pkg/core/native/nativehashes/gen.go
Normal file
70
pkg/core/native/nativehashes/gen.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
//go:build ignore
|
||||||
|
|
||||||
|
// This program generates hashes.go. It can be invoked by running
|
||||||
|
// go generate.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
// srcTmpl is a nativehashes package template.
|
||||||
|
const srcTmpl = `// Code generated by "go generate go run gen.go"; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run gen.go
|
||||||
|
|
||||||
|
// package nativehashes contains hashes of all native contracts in their LE and Uint160 representation.
|
||||||
|
package nativehashes
|
||||||
|
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
|
||||||
|
// Hashes of all native contracts.
|
||||||
|
var (
|
||||||
|
{{- range .Natives }}
|
||||||
|
// {{ .Name }} is a hash of native {{ .Name }} contract.
|
||||||
|
{{ .Name }} = {{ .Hash }}
|
||||||
|
{{- end }}
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Config contains parameters for the nativehashes package generation.
|
||||||
|
Config struct {
|
||||||
|
Natives []NativeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// NativeInfo contains information about native contract needed for
|
||||||
|
// nativehashes package generation.
|
||||||
|
NativeInfo struct {
|
||||||
|
Name string
|
||||||
|
Hash string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// srcTemplate is a parsed nativehashes package template.
|
||||||
|
var srcTemplate = template.Must(template.New("nativehashes").Parse(srcTmpl))
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f, err := os.Create("hashes.go")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var cfg = Config{Natives: make([]NativeInfo, len(nativenames.All))}
|
||||||
|
for i, name := range nativenames.All {
|
||||||
|
var hash = state.CreateNativeContractHash(name)
|
||||||
|
cfg.Natives[i] = NativeInfo{
|
||||||
|
Name: name,
|
||||||
|
Hash: fmt.Sprintf("%#v", hash),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcTemplate.Execute(f, cfg)
|
||||||
|
}
|
32
pkg/core/native/nativehashes/hashes.go
Normal file
32
pkg/core/native/nativehashes/hashes.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Code generated by "go generate go run gen.go"; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run gen.go
|
||||||
|
|
||||||
|
// package nativehashes contains hashes of all native contracts in their LE and Uint160 representation.
|
||||||
|
package nativehashes
|
||||||
|
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
|
||||||
|
// Hashes of all native contracts.
|
||||||
|
var (
|
||||||
|
// ContractManagement is a hash of native ContractManagement contract.
|
||||||
|
ContractManagement = util.Uint160{0xfd, 0xa3, 0xfa, 0x43, 0x46, 0xea, 0x53, 0x2a, 0x25, 0x8f, 0xc4, 0x97, 0xdd, 0xad, 0xdb, 0x64, 0x37, 0xc9, 0xfd, 0xff}
|
||||||
|
// StdLib is a hash of native StdLib contract.
|
||||||
|
StdLib = util.Uint160{0xc0, 0xef, 0x39, 0xce, 0xe0, 0xe4, 0xe9, 0x25, 0xc6, 0xc2, 0xa0, 0x6a, 0x79, 0xe1, 0x44, 0xd, 0xd8, 0x6f, 0xce, 0xac}
|
||||||
|
// CryptoLib is a hash of native CryptoLib contract.
|
||||||
|
CryptoLib = util.Uint160{0x1b, 0xf5, 0x75, 0xab, 0x11, 0x89, 0x68, 0x84, 0x13, 0x61, 0xa, 0x35, 0xa1, 0x28, 0x86, 0xcd, 0xe0, 0xb6, 0x6c, 0x72}
|
||||||
|
// LedgerContract is a hash of native LedgerContract contract.
|
||||||
|
LedgerContract = util.Uint160{0xbe, 0xf2, 0x4, 0x31, 0x40, 0x36, 0x2a, 0x77, 0xc1, 0x50, 0x99, 0xc7, 0xe6, 0x4c, 0x12, 0xf7, 0x0, 0xb6, 0x65, 0xda}
|
||||||
|
// NeoToken is a hash of native NeoToken contract.
|
||||||
|
NeoToken = util.Uint160{0xf5, 0x63, 0xea, 0x40, 0xbc, 0x28, 0x3d, 0x4d, 0xe, 0x5, 0xc4, 0x8e, 0xa3, 0x5, 0xb3, 0xf2, 0xa0, 0x73, 0x40, 0xef}
|
||||||
|
// GasToken is a hash of native GasToken contract.
|
||||||
|
GasToken = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2}
|
||||||
|
// PolicyContract is a hash of native PolicyContract contract.
|
||||||
|
PolicyContract = util.Uint160{0x7b, 0xc6, 0x81, 0xc0, 0xa1, 0xf7, 0x1d, 0x54, 0x34, 0x57, 0xb6, 0x8b, 0xba, 0x8d, 0x5f, 0x9f, 0xdd, 0x4e, 0x5e, 0xcc}
|
||||||
|
// RoleManagement is a hash of native RoleManagement contract.
|
||||||
|
RoleManagement = util.Uint160{0xe2, 0x95, 0xe3, 0x91, 0x54, 0x4c, 0x17, 0x8a, 0xd9, 0x4f, 0x3, 0xec, 0x4d, 0xcd, 0xff, 0x78, 0x53, 0x4e, 0xcf, 0x49}
|
||||||
|
// OracleContract is a hash of native OracleContract contract.
|
||||||
|
OracleContract = util.Uint160{0x58, 0x87, 0x17, 0x11, 0x7e, 0xa, 0xa8, 0x10, 0x72, 0xaf, 0xab, 0x71, 0xd2, 0xdd, 0x89, 0xfe, 0x7c, 0x4b, 0x92, 0xfe}
|
||||||
|
// Notary is a hash of native Notary contract.
|
||||||
|
Notary = util.Uint160{0x3b, 0xec, 0x35, 0x31, 0x11, 0x9b, 0xba, 0xd7, 0x6d, 0xd0, 0x44, 0x92, 0xb, 0xd, 0xe6, 0xc3, 0x19, 0x4f, 0xe1, 0xc1}
|
||||||
|
)
|
|
@ -14,6 +14,20 @@ const (
|
||||||
StdLib = "StdLib"
|
StdLib = "StdLib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// All contains the list of all native contract names ordered by the contract ID.
|
||||||
|
var All = []string{
|
||||||
|
Management,
|
||||||
|
StdLib,
|
||||||
|
CryptoLib,
|
||||||
|
Ledger,
|
||||||
|
Neo,
|
||||||
|
Gas,
|
||||||
|
Policy,
|
||||||
|
Designation,
|
||||||
|
Oracle,
|
||||||
|
Notary,
|
||||||
|
}
|
||||||
|
|
||||||
// IsValid checks if the name is a valid native contract's name.
|
// IsValid checks if the name is a valid native contract's name.
|
||||||
func IsValid(name string) bool {
|
func IsValid(name string) bool {
|
||||||
return name == Management ||
|
return name == Management ||
|
||||||
|
|
|
@ -73,7 +73,7 @@ func copyNotaryCache(src, dst *NotaryCache) {
|
||||||
// newNotary returns Notary native contract.
|
// newNotary returns Notary native contract.
|
||||||
func newNotary() *Notary {
|
func newNotary() *Notary {
|
||||||
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
||||||
defer n.UpdateHash()
|
defer n.BuildHFSpecificMD(n.ActiveIn())
|
||||||
|
|
||||||
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
||||||
manifest.NewParameter("from", smartcontract.Hash160Type),
|
manifest.NewParameter("from", smartcontract.Hash160Type),
|
||||||
|
@ -127,7 +127,11 @@ func (n *Notary) Metadata() *interop.ContractMD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes Notary native contract and implements the Contract interface.
|
// Initialize initializes Notary native contract and implements the Contract interface.
|
||||||
func (n *Notary) Initialize(ic *interop.Context) error {
|
func (n *Notary) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
|
||||||
|
if hf != n.ActiveIn() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
||||||
|
|
||||||
cache := &NotaryCache{
|
cache := &NotaryCache{
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue