Compare commits

...

226 commits

Author SHA1 Message Date
fe7a767e8f
[#131] nns: Declare getAllRecords() as safe
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-21 10:23:31 +03:00
60b81c4bf6
[#131] *: Reformat code
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-21 10:23:31 +03:00
a2c2791146
[#129] frostfsid: Return subject by an additional address
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-19 17:41:06 +03:00
7a8c64b966
[#118] frostfsid: Restrict keys to a single subject
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-11-13 21:35:15 +03:00
8b586081eb
[#125] CODEOWNERS: Refine ownership
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-07 09:24:36 +03:00
4666a953b3 [#124] Stop using obsolete .github directory
This commit is a part of multi-repo cleanup effort:
TrueCloudLab/frostfs-infra#136

Signed-off-by: Vitaliy Potyarkin <v.potyarkin@yadro.com>
2024-11-06 15:17:04 +03:00
48f06df25a
[#119] nns/docs: Integrate FrostfsID into NNS
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-11-02 11:25:09 +03:00
645b4cb3c8
[#119] nns: Integrate FrostfsID into NNS
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-11-02 11:24:49 +03:00
ffd2763094
[#119] frostfsid: Add 'getSubjectKV'
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-11-02 11:24:10 +03:00
5f956751d4 [#120] Add waiter to frostfsid client
Signed-off-by: Vladimir Domnich <v.domnich@yadro.com>
2024-10-22 12:40:40 +03:00
3f4f8feca7
[#115] nns: Allow register TLD from nested domains
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-10-15 17:08:18 +03:00
a90d54c332
[#115] nns: Allow register TLD from nested domains
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-10-11 18:44:43 +03:00
81853bd242 Release v0.20.0
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-30 12:37:12 +03:00
d3a85dd028 [#112] go.mod: Update go to 1.22
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-30 12:32:44 +03:00
ff2d165c28 [#114] nns: Add docs
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-09-27 17:51:29 +03:00
9b532320b1 [#114] nns: Restrict 'DeleteDomain'
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-09-27 17:51:13 +03:00
8b7f44adef [#114] nns: Add 'DeleteRecord'
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-09-27 17:50:26 +03:00
3e221b973a [#113] Regenerate wrappers
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-09 10:27:09 +03:00
d36120014d [#113] .forgejo: Add generate-wrappers action
Make it easier to maintain auto-generated code in the actual state.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-09 10:26:56 +03:00
2a1bf77d74 [#113] go.mod: Update neo-go
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-09 09:35:31 +03:00
82641e2e99 [#109] nns: Add notification sending
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-09-09 06:24:55 +00:00
6e244fac04 [#110] nns: Remove unused config
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-09-06 12:06:19 +03:00
c142971bfd [#107] Add client method ListFullSubjects
Signed-off-by: d.zverev <d.zverev@yadro.com>
2024-08-27 13:18:00 +00:00
cee957e85a [#105] policy: Add ListChainNames method
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2024-08-27 12:03:05 +03:00
0befe361fe [#104] rpcclient: Regenerate wrappers
Introduced in #102.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-08-19 18:08:51 +03:00
ba7329c3a7 [#103] common: Disallow downgrading contracts
`PrevVersion` marks suitable version that we can upgrade from.
However, we can have multiple minor versions, so, currently an upgrade
from v0.19.3 to v0.19.1 is possible. Prevent this with an additional
check.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-08-19 13:57:03 +03:00
82e04b6c32 [#102] nns: Support global domain
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-08-16 16:38:54 +03:00
b2eb585bb6 [#102] nns: Support global domain
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-08-16 16:37:17 +03:00
49e5270f67 [#92] nns: Mention domain in panic messages
Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2024-06-21 16:12:49 +03:00
2aba66806c [#93] frostfsid: Move struct parsers to separate file
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-06-21 14:33:04 +03:00
ba4ef7bd22 [#89] frostfsid: Add description for FrostFS ID
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-05-24 21:32:20 +03:00
db36131800 [#90] policy: Add constants for more targets
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2024-04-17 11:01:07 +03:00
a3d5e02f20 [#81] Add domain in the error message in NNS
Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2024-04-16 18:05:46 +03:00
6eb492025b [#86] .forgejo: Update go version
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-04-09 14:57:29 +03:00
1addbfef2d [#86] .forgejo: Update DCO action version
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-04-09 14:57:29 +03:00
e7a05a49ff [#XX] client: Terminate session in ReadIteratorItems
* Make an invoker terminate session by its ID before return, otherwise,
  it may lead to `max session capacity reached error`.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-04-09 14:15:39 +03:00
694daebb19 [#84] policy: Fix IteratorChainsByPrefix method
* If numeric mapping does not exists, then assign id to 0.
* Add check to unit-test.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-04-04 15:43:19 +03:00
2574b2840e [#84] rpcclient: Regenerate interface for Policy
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-04-04 10:26:28 +03:00
42344eaa69 [#83] container: Remove outdated migration code
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-03-14 10:03:23 +03:00
e27b8ad306 [#83] policy: Allow to update contract
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-03-14 10:03:23 +03:00
5119f655fe [#82] common: Update version
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-03-13 20:57:09 +03:00
c9c53bb9ec [#17] frostfs: Remove method alphabetAddress
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2024-03-05 12:59:03 +03:00
43c90af97d [#78] policy: Fix counter key prefix name
* Fix counterKey: "counter" may conflict with 'c' prefix.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-02-20 11:58:33 +03:00
d9f523ee07 [#78] policy: Introduce ListTargets method for Policy contract
* Introduce a new method ListTargets that lists targets by kind.
* Slightly fix key mapping - also concatenate kind to prefix.
* Write unit-tests.
* Regenerate rpcclient.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-02-20 11:57:34 +03:00
55a4163951 [#76] frostfsid: Add GetGroupByName method
Signed-off-by: Marina Biryukova <m.biryukova@yadro.com>
2024-02-01 16:06:17 +03:00
6e72d0b3b4 [#75] policy: Fix compiler error
Refs #71

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-01-30 16:37:37 +03:00
e0f6fe3bc9 [#61] policy: Shorten policy contract keys
* Map long entity names to 8-bytes long numbers.
* Add desctiption to docs.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-01-30 12:27:57 +00:00
074a241272 [#74] proxy: Allow to own NNS domains
Because `Verify` is flexible enough, any funds transferred to the proxy
contract can be moved out with the help of the committee. Thus,
implementing onNEP11Payment() is enough.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-01-29 14:03:06 +03:00
2efebf4206 [#74] tests: Do not compile proxy contract twice
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-01-29 14:02:18 +03:00
60e8abbf49 [#74] tests: Actualize proxy contract deploy parameters
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-01-29 14:02:18 +03:00
93781f1149 [#66] policy: Support IteratorChainsByPrefix method
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-01-26 17:41:04 +03:00
da8ec5b447 [#73] nns: Allow 2-byte domain names
frostfs-node uses `.ns` domain, must be supported.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-01-25 16:21:58 +03:00
f2a82aa635 [#69] frostfsid: Add new client init function
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2024-01-15 11:29:15 +03:00
a7c45fdd0d [#68] policy: Add Version() method
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-01-11 15:39:19 +03:00
5363aaf16a [#68] rpcclient: Regenerate wrappers after neo-go update
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-01-11 15:39:19 +03:00
86e6d4d334 [#64] commonclient: Use partial unwrap in ReadIteratorItems()
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-12-29 14:06:46 +00:00
8c7925d3c0 [#64] go.mod: Update neo-go
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-12-29 14:06:46 +00:00
bce7ef18c8 [#63] frostfsid/client: Support proxy
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-12-18 11:43:46 +03:00
23e85d11c4 [#53] proxy: Allow using proxy by trusted accounts
It was reverted because `Verify` with arguments was not well supported
by the client software. Thanks to recent `System.Runtime.CurrentSigners`
call from neo-go v0.104.0 we can take the best of both worlds.

Verify with argument still looks better (less overhead), but this
implementation should work too. Sadly, `overloads` are not of much use
here because verification routines take the _first_ method from the
manifest, albeit with arbitrary number of arguments.

This reverts commit a0b73150c6 with some
changes on-top.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-12-15 14:34:26 +03:00
4dcb575caa [#56] Add multi-level domain name support
Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2023-12-13 15:22:12 +03:00
897f538a3c [#48] frostfsid: add GetSubjectByName method
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-12-11 09:36:47 +00:00
3fb511ac15 [#48] frostfsid: Support empty namespaces
Require ns to create subject.
Since we don't allow move subject from one ns to another -
drop add/remove subject to/from namespace

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-12-11 09:36:47 +00:00
a0b73150c6 Revert "[#53] proxy: Allow using proxy by trusted accounts"
This reverts commit bc3186575f.
2023-12-08 11:10:00 +03:00
bc3186575f [#53] proxy: Allow using proxy by trusted accounts
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-12-07 10:22:47 +03:00
d7cb550a5e [#53] common: Use interop.Hash160 in address producing functions
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-12-06 19:47:43 +03:00
91b36a7eb3 [#53] go.mod: Update neo-go to the laster master
Pickup new neotest features.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-12-06 19:47:43 +03:00
94cd3dca83 [#53] go.mod: Resolve ambiguous import
There was a problem with `go mod tidy`.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-12-06 19:47:15 +03:00
a1b61d3949 [#55] Makefile: Add config.yml to NEF dependencies
It can affects manifest, so recompilation is needed.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-29 09:22:01 +03:00
f28d918727 [#55] frostfsid: Add missing safe methods
All of them can be called in read-only context.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-29 09:22:01 +03:00
7864fc3c4d [#55] policy: Fix typo in ErrNotAuthorized
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-29 09:21:58 +03:00
edf3c26047 [#54] policy: Accept []byte as name
Strings cannot contain non-UTF8 bytes, this is ensured in JSON
marshaling/unmarshaling. At the same time using human-readable strings
can be rather restrictive, because we have 64-byte key limit.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-28 13:49:57 +03:00
f30fb324ff [#55] policy: Swap signature check order
While implementing the changes for FrostFS ID it became obvious, that
committee signature check can be rather costly (`getCommittee` call +
multiaddress construction + checking witness). In real scenarious it
will mostly fail, so it makes sense do it last.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-28 13:49:57 +03:00
0dda536d4a [#55] policy: Fix admin processing in _deploy
Refactoring remnants, there is a single admin now.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-28 13:49:57 +03:00
43097d2152 [#55] frostfsid: Use single admin instead of many
Autorization can be dedicated to a separate contract, iterating over
multiple keys can be costly. Also add committee as "default" admin:
everything is allowed for it.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-28 13:49:56 +03:00
03d0c10852 [#55] Apply gofumpt
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-28 12:32:33 +03:00
21bfe3ebf0 [#52] policy: Regenerate RPC wrapper
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-28 12:32:33 +03:00
e56e8d02ee [#52] policy: Add admin
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-24 10:34:22 +03:00
20f86e96b2 [#51] policy: Fix tests
We don't want to return Null from contract when list is empty
because generated policy client cannot parse this.

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-20 15:43:07 +03:00
5cc34f98e9 [#51] policy: Support Get and ListByPrefix methods
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-17 14:36:48 +03:00
dd5919348d [#48] frostfsid: Update storage scheme doc
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-09 17:39:25 +03:00
e2e406932f [#48] frostfsid: Support Group updating
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-09 16:14:11 +03:00
501b0c5e3c [#48] container: Don't invoke frostfsid contract
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-08 15:44:10 +03:00
8affa716e0 [#48] frostfsid: Add client tests
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-08 15:44:10 +03:00
95fe7781d5 [#48] frostfsid: Add user-friendly client
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-08 15:44:06 +03:00
b76f592095 [#48] Add commonclient package
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-08 15:44:01 +03:00
5cc810096f [#48] frostfsid: Add tests
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-08 15:40:39 +03:00
c8b14d1376 [#48] frostfsid: Generate wrappers
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-08 15:39:21 +03:00
edfab37677 [#48] frostfsid: Update contract
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-08 15:39:19 +03:00
0ea65ca637 [#50] Drop audit and reputation contracts
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-07 16:20:43 +03:00
d890a7eba4 [#50] Replace interface{} with any
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-07 15:18:48 +03:00
dacac0b53d [#50] Makefile: Add code formatting targets
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-07 15:00:05 +03:00
6e9c770142 [#50] Drop notaryless deploy parameter
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-07 14:56:21 +03:00
09281e3ef3 [#49] go.mod: Bump go version to 1.20
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-03 10:38:27 +03:00
9ed3845aa9 [#44] policy: Initial implementation
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-11-02 09:54:36 +03:00
901d5a4083 [#47] Generate RPC bindings to contracts
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-31 10:28:38 +00:00
e09df69ffe [#47] go.mod: Update neo-go to v0.103.0
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-31 10:28:38 +00:00
e834a66117 [#45] balance: Fix inconsistent fee of transfer operations
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-10-24 13:42:30 +03:00
5124555f05 [#45] nns: Fix inconsistent fee of register operations
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-10-24 13:28:11 +03:00
184fcdc5a7 [#45] container: Add test of inconsistent container creation fee
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-10-24 13:28:11 +03:00
bab6b619d0 [#42] container: Make GAS costs more predictable in Delete()
Persisting a transaction is done in 2 stages:
1. TestInvoke
2. Sign and send to the network.
3. At some point the tx is persisted.
Some time passes between 1 and 3, this could lead to different GAS
costs. It is a known issue for container delete: different epoch can
have different size in bytes and thus different cost to store.
Here we introduce fixed-length encoding for integers, so that the
problem can be avoided.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-24 13:28:10 +03:00
2be81b1def [#42] common: Add routines for fixed-width uint64 marshaling
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-24 13:28:10 +03:00
ab0a899a28 [#42] container: Add failing tests for different epoch deletion
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-24 13:28:10 +03:00
0b3ea05f76 [#43] .forgejo: Name all actions
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-06 09:36:46 +03:00
c13c01a5c0 [#43] .forgejo: Fix dco action
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-10-06 09:31:29 +03:00
1777c93c9e Release v0.18.0
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-09-14 13:33:12 +03:00
45c3710bd2 [#38] .forgejo: Update DCO action
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-08-17 15:31:34 +03:00
391c1cb5d4 [#38] container: Fix DelInfo owner type
It is not a 20-byte scripthash, it is a 25-byte slice
(prefix + script-hash + checksum).

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-08-11 16:18:01 +03:00
c6a7820363 container: Add DeletionInfo() method
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-08-10 17:13:07 +00:00
2b1fa53b67 tests: Move owner ID calculation to a separate function
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-08-10 17:13:07 +00:00
6e8ae742ae *: Use multiline lists in yaml config
It is quite hard to navigate in a single-line list of length 10.
Viewing diffs is also more pleasant when a single line has added.
Also, sort everything by alphabet.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-08-10 17:13:07 +00:00
077823ee84 go.mod: Update neo-go
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-08-09 10:09:18 +03:00
36f3d39c40 [#32] Add forgejo workflows
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-06-27 16:47:46 +03:00
bcf22c50a1 [#10] Use UpdateWithData call wrapper in Update methods
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-06-19 11:17:51 +03:00
2da0ff5750 [#27] Add public key as argument for container_contract.Delete() method
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-06-01 14:08:21 +03:00
b89c3fd584 [#23] Remove unused notifications from container contract
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-05-18 08:25:32 +00:00
6a19989aa6 [#1] tests: Update domain email after rebranding
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-05-17 10:52:10 +03:00
f786040322 [#1] container: Update domain email after rebranding
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-05-17 10:52:09 +03:00
a8029c71cd [#21] Document contract storage schemes
Signed-off-by: Airat Arifullin a.arifullin@yadro.com
2023-05-12 14:51:21 +03:00
8537293e11 [#20] subnet: Drop contract
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-04-17 14:29:30 +03:00
Evgenii Stratonikov
e19fe15ed8 [#19] common: Update current version
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2023-04-13 11:24:52 +03:00
Evgenii Stratonikov
8d17306c3c Release v0.17.0
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2023-04-13 11:22:42 +03:00
cbe0f01e08 [#18] Add Issue Template
Add bug report and feature request templates

Signed-off-by: Liza <e.chichindaeva@yadro.com>
2023-03-23 12:20:08 +03:00
b9be2ac036 [#7] Refactor common.CheckAlphabetWitness()
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-03-14 12:09:24 +03:00
d8530284fd [#7] Remove notary disabled code
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-03-14 12:09:22 +03:00
6b13a83736 [#14] Bump contracts version
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2023-03-14 10:59:55 +03:00
d626660c1a [#12] Use non-specific contract names
Allows easier project name updates in the future.

Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-03-09 19:56:33 +00:00
19a8ef2d02 Rename package name
Due to source code relocation from GitHub.

Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-03-07 14:06:21 +03:00
Evgenii Stratonikov
4f3c08f552 [#300] container: Allow to iterate over container list
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2023-01-16 14:40:21 +03:00
03bff785d2 [#293] container: Add IterateContainerSizes method
Add method that allows to iterate over estimation records.

Update tests to assert that list of estimations built with existing methods
is identical to estimations from iterator.

Signed-off-by: Vladimir Domnich <v.domnich@yadro.com>
2023-01-16 14:40:21 +03:00
1a4fa7e421 [#296] container: Increase default expiration time
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-01-16 14:40:21 +03:00
0ba830f48f [#291] Debian packaging
Debian package contains compiled contracts and manifests with
corresponding directories, which will be placed into
/var/lib/neofs/contract for further usage.

Depends on neo-go package to build.

Signed-off-by: Dmitriy Zabolotskiy <d.zabolotskiy@yadro.com>
2023-01-16 14:40:21 +03:00
4077921794 [TrueCloudLab#3] *: Use frostfs in docs
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
0c5723964f [TrueCloudLab#3] makefile: Rename archive to frostfs
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
e1cd043248 [TrueCloudLab#3] netmap: Rename neofs to frostfs
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
9c004cab2e [TrueCloudLab#3] container: Rename neofs to frostfs
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
4052e4873b [TrueCloudLab#3] balance: Rename neofs to frostfs
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
a21630c1d7 [TrueCloudLab#3] neofsid: Rename contract to frostfsid
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
402c13a607 [TrueCloudLab#3] processing: Rename neofs to frostfs
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
0093e89ad9 [TrueCloudLab#3] neofs: Rename contract to frostfs
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-01-11 17:19:35 +03:00
e95c598dfc Change logo
Signed-off-by: Stanislav Bogatyrev <s.bogatyrev@yadro.com>
2023-01-09 11:43:08 +03:00
Evgenii Stratonikov
6c805c1b4e Move from nspcc-dev to TrueCloudLab
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-12-13 11:12:48 +03:00
Evgenii Stratonikov
8ca71d22b2 *: goimports -w
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-12-13 11:12:48 +03:00
Evgenii Stratonikov
9052ec62d6 [#285] Makefile: Install neo-go in the current directory
`go install` doesn't have `-o` flag but it respects `GOBIN` environment
variable.

Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-11-23 20:29:17 +03:00
Evgenii Stratonikov
beaef7b10d [#284] *: Update version and remove the old migration code
Less chances to forget anything in the next release.

Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-10-18 09:54:53 +03:00
Evgenii Stratonikov
38246cd54f [#284] tests: Add a test for checking VERSION file
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-10-18 09:54:53 +03:00
Evgenii Stratonikov
363d2a2a3a [#284] go.mod: Update neo-go to v0.99.4
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-10-18 09:54:53 +03:00
Pavel Karpy
a904bcdd27 [#286] Actualize version-dependent info after the release
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2022-10-17 21:31:13 +03:00
Pavel Karpy
6398b16a53 [#283] Make v0.16.0 release named
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2022-10-17 14:45:35 +03:00
Evgenii Stratonikov
7588f67496 Release v0.16.0
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-10-17 14:14:33 +03:00
Evgenii Stratonikov
c3b00e0011 [#278] netmap: Migrate from the old storage format
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-10-17 13:36:15 +03:00
anastasia prasolova
55ffc8f53c Add CODEOWNERS file
Signed-off-by: anastasia prasolova <anastasia@nspcc.ru>
2022-10-16 21:12:20 +03:00
Evgenii Stratonikov
b62dae136e [#280] common: Update version number
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-10-05 09:27:00 +03:00
Leonard Lyubich
f276e24137 Fix typo in the CHANGELOG.md
Call changelog section with unreleased changes `Unreleased`. Also make
it as a diff link.

Signed-off-by: Leonard Lyubich <ctulhurider@gmail.com>
2022-09-30 17:31:58 +04:00
Leonard Lyubich
d95bc53589 [#269] netmap: Return same type from reading methods
There is a need to return similar structure of information about the
storage nodes from the contract storage readers. In previous
implementation some methods didn't return node state which can differ
with the one encoded in the node's BLOB.

Define `Node` structure of the information about the storage nodes
recorded in the contract storage. Return `[]Node` from all related
methods.

Also improve docs of touched contract methods.

Signed-off-by: Leonard Lyubich <ctulhurider@gmail.com>
2022-09-30 17:31:58 +04:00
anastasia prasolova
4d487a069e [nspcc-dev/nspcc-infra#840]: Add DCO check
Signed-off-by: anastasia prasolova <anastasia@nspcc.ru>
2022-09-22 20:10:51 +03:00
Evgenii Stratonikov
9785f9b2c7 [#271] netmap: Allow to move node back to Online state
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-09-21 07:29:44 +03:00
Evgenii Stratonikov
cb684994fc [#269] netmap: Support Maintenance node state
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-09-21 07:29:44 +03:00
Evgenii Stratonikov
2d0101033b [#270] netmap: Export NodeState type
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-09-21 07:29:44 +03:00
Anna Shaleva
70ae2c94e1 [#265] container: Adjust container-contract permissions
Remove "addRoot" method from the list of allowed methods, because
NNS doesn't have it since
4b86891d57.
2022-09-07 12:16:49 +03:00
Pavel Karpy
86157d801b [#264] make: Fix commit abbrev length in version
Make it uniform across all our repos.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2022-09-06 12:14:03 +03:00
Evgenii Stratonikov
b4fdd854b7 [#259] nns: Check admin in Renew
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-08-29 12:03:24 +03:00
Evgenii Stratonikov
5b5ff8cb33 [#261] nns: Set expiration time based on arguments
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-08-29 12:03:24 +03:00
Evgenii Stratonikov
bed1a88dee [#261] Release v0.15.5
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-08-23 12:29:47 +03:00
Evgenii Stratonikov
cb0d7179f4 [#261] go.mod: Update neo-go to v0.99.2
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-08-23 12:29:47 +03:00
73e2119180 [#255] Filter version tags for version calculation
Signed-off-by: anikeev-yadro <a.anikeev@yadro.com>
2022-08-12 16:30:02 +03:00
Evgenii Stratonikov
02c2ce3966 [#253] go.mod: Update neo-go to v0.99.1
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-07-28 20:53:02 +03:00
Evgenii Stratonikov
564f147ea1 [#252] Release v0.15.4
Signed-off-by: Evgenii Stratonikov <evgeniy@morphbits.ru>
2022-07-27 12:14:20 +03:00
Evgenii Stratonikov
927fec8cdc [#251] Release v0.15.3
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-07-22 14:25:54 +03:00
Evgenii Stratonikov
6e2ff648d9 [#251] go.mod: Update neo-go
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-07-22 14:25:54 +03:00
Evgenii Stratonikov
0b8e112ea3 [#250] Makefile: allow to build from the source archive
Close #249.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-07-18 13:01:52 +03:00
Evgenii Stratonikov
69b308e792 [#248] *: Fix integer types
When using contracts as a dependency, out-of-range `int` can cause
problems on 32-bit architectures. Contract byte-code hasn't changed.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-07-06 14:28:47 +03:00
Evgenii Stratonikov
a92ad8b8f0 [#246] Release v0.15.2
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-06-07 14:14:29 +03:00
Evgenii Stratonikov
53d9a24afa [#246] netmap: Remove outdated update logic
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-06-07 14:14:29 +03:00
Evgenii Stratonikov
fdc0ffcbb2 [#246] go.mod: Update neo-go to v0.99.0
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-06-07 14:14:29 +03:00
Evgenii Stratonikov
f1f2101dc6 [#242] container: allow to fetch total amount of countainers
There are 2 approaches:
1. Use `storage.Find` to enumerate all containers.
2. Store a counter by a separate key.

Here we implemented 1, because this method is readonly (thus GAS cost it
not a problem) and simpler to implement.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-05-24 13:31:18 +03:00
Elizaveta Chichindaeva
335b04d9a6 [#240] English Check
Signed-off-by: Elizaveta Chichindaeva <elizaveta@nspcc.ru>
2022-04-26 23:22:06 +03:00
Alex Vanin
c75315808b Release v0.15.1
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2022-04-13 15:43:24 +03:00
Evgenii Stratonikov
fd70f28b47 [#236] *: Notify user on success
Add notifications to:
- container put, delete, setEACL
- netmap addPeer, updateState

Because notifications are limited in size (currently arguments should be
less than 1024 bytes) provide only minimal information, such as entity
ID (container ID or node public key).

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-04-13 12:14:35 +03:00
Angira Kekteeva
0aa7fd4189 [#238] nns: Fix maxDomainNameFragmentLength
maxDomainNameFragmentLength means maximum length of label of domain name

A label may contain zero to 63 characters. The null label, of length
zero, is reserved for the root zone. The full domain name may not exceed
the length of 253 characters in its textual representation

Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
2022-04-05 14:57:06 +03:00
Evgenii Stratonikov
4a0f0d7408 [#232] netmap: Allow to configure snapshot history size
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-29 09:44:38 +03:00
Evgenii Stratonikov
5fc7474447 [#232] *: Remove old update code
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-29 09:44:38 +03:00
Evgenii Stratonikov
33d5568511 [#234] *: Fix some lint issues
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-28 10:27:25 +03:00
Evgenii Stratonikov
fe5c470a08 [#234] go.mod: Update neo-go version
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-28 10:27:25 +03:00
Alex Vanin
12527e51ae Release v0.15.0 - Heuksando (흑산도, 黑山島)
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2022-03-23 18:48:14 +03:00
Alex Vanin
9ed3c538a0 [#231] Lower minimal contract version update to v0.14.0
Main chain NeoFS contract was not updated to v0.14.2. Current
contract version is v0.14.0 However, there is no functional
changes between v0.14.0 and v0.14.2. To avoid NeoFS contract
update, this commit lowers minimal contract version.

All other contracts have been updated to v0.14.2 and they are not
affected by this change.

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2022-03-23 18:48:14 +03:00
Alex Vanin
2b624a38cc [#231] Remove previous contract update code
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2022-03-23 18:48:14 +03:00
Evgenii Stratonikov
e9cfe6194f [#222] netmap: Store public keys in _deploy
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-23 12:04:47 +03:00
Evgenii Stratonikov
1beee7eb0b [#228] common: Bump contract version
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-23 11:33:27 +03:00
Evgenii Stratonikov
aee1a5d77c [#222] common: Optimize RemoveVote
Use single `REMOVE` instead of multiple `APPEND` opcodes.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-23 11:33:27 +03:00
Evgenii Stratonikov
53795324dc [#222] neofs: Remove key argument from getNodes
We always use `alphabetKey`, it makes sense to use it directly.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-23 11:33:27 +03:00
Evgenii Stratonikov
03afb80a14 [#222] *: Replace IRNode with raw public keys
Only leave `IRNode` in neofs contract because it is public.
The newly added conversion in `AlphabetList` shouldn't be a problem
because this is a read method.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-23 11:33:27 +03:00
Evgenii Stratonikov
01a7163d1e [#229] netmap: Improve UpdateState in notary-enabled environment
Require `UpdateState` to be called by both storage node and the alphabet
in notary-enabled environment, fail if only one of the signatures is present.
`UpdateStateIR` can be use for force updates by the alphabet.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-22 12:56:16 +03:00
Evgenii Stratonikov
624cc0f1c4 [#224] netmap: Increase snapshot history size to 10
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 18:57:43 +03:00
Evgenii Stratonikov
666e1d6d8d [#224] netmap: Add tests for NewEpoch
Ensure snapshots are handled properly.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 18:57:43 +03:00
Evgenii Stratonikov
9884f8e68f [#224] netmap: Refactor snapshot processing
Swap keys instead of unmarshaling/marshaling snapshot during `NewEpoch`.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 18:57:43 +03:00
Evgenii Stratonikov
f6766a144f [#225] netmap: Do not emit some events in notary-enabled mode
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-17 16:32:20 +03:00
Evgenii Stratonikov
a4f9d52cfc [#225] netmap: Rename Register to AddPeerIR
Similar to `UpdateState`/`UpdateStateIR` pair.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-17 16:32:20 +03:00
Evgenii Stratonikov
9662f9f4db [#225] netmap: Split UpdateState method for notary-enabled environment
Split methods similar to `AddPeer`/`Register` pair from b104a2ccbc .

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-17 16:32:20 +03:00
Alex Vanin
5758dadaa9 Release v0.14.2
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2022-02-07 19:08:48 +03:00
Evgenii Stratonikov
b0872bb54c *: remove update migration code
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-02-07 17:49:07 +03:00
Evgenii Stratonikov
74e4bbb6b9 [#196] nns: remove duplicate records in _deploy
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-02-07 16:08:14 +03:00
Evgenii Stratonikov
d48709fcbb go.mod: update neo-go version
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-02-03 15:32:03 +03:00
Evgenii Stratonikov
f530bdf721 Release v0.14.1
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-27 10:52:42 +03:00
Evgenii Stratonikov
30be805896 [#220] reputation: remove storage migration
It was there to provide update to `v0.13.1`, not needed now.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-27 10:52:42 +03:00
Evgenii Stratonikov
26d51b526a [#220] subnet: append version in Update
Current contract doesn't provide version in arguments, thus disable
check in `_deploy`.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-27 10:52:42 +03:00
Pavel Karpy
912eaab8d8 [#198] test: Adopt neotest changes
This reverts commit 8418183498.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2022-01-21 11:03:47 +03:00
Evgenii Stratonikov
676daa1782 [#215] container: remove old estimations when adding new ones
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-21 10:39:30 +03:00
Evgenii Stratonikov
7bca6bf782 [#215] container/tests: add tests for container size estimation
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-21 10:39:30 +03:00
Evgenii Stratonikov
53f102344f [#215] container/tests: add helper for dummy container creation
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-21 10:39:30 +03:00
Evgenii Stratonikov
72da098876 [#215] container: remove old estimations without intermediate array
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-21 10:39:30 +03:00
Evgenii Stratonikov
413a7eabfe [#215] go.mod: update neo-go version
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-21 10:39:30 +03:00
Alex Vanin
f5531561b8 Release v0.14.0 - Geojedo (거제도, 巨濟島)
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2022-01-14 10:59:25 +03:00
Alex Vanin
3c7b505f28 go.mod: use latest tagged version of neo-go
While we use unreleased version of neo-go,
we can't compile it with neo-go v0.98.0

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2022-01-14 10:59:25 +03:00
Alex Vanin
8418183498 Revert "[#198] test: Adopt neotest changes"
This reverts commit 17907f9457.
2022-01-14 10:59:25 +03:00
Evgenii Stratonikov
24d24b9db7 go.mod: update neo-go
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-11 14:25:37 +03:00
Evgenii Stratonikov
ce391b108d [#209] *: use current block index in GetDesignatedByRole
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-11 11:57:00 +03:00
Evgenii Stratonikov
b8d2569f4d [#208] neofs: delete old OwnerKey
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-11 11:55:04 +03:00
Evgenii Stratonikov
d9bfe32847 [#208] neofs: drop version check
Should be restored after mainnet update.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-11 11:55:04 +03:00
Evgenii Stratonikov
74cc2d0aea [#204] *: check contract version during update
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-11 11:55:04 +03:00
Evgenii Stratonikov
8226bd4e43 [#207] neofsid: remove old transition code
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-28 13:32:28 +03:00
Evgenii Stratonikov
1944a4332b [#202] *: remove owner from contract storage
It is no longer used and should've be removed on update.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-28 13:32:28 +03:00
Evgenii Stratonikov
c1adea2484 [#205] proxy: do not store netmap hash
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-27 14:24:32 +03:00
Pavel Karpy
8718e146c9 [#200] nns: Provide additional data in Update
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2021-12-14 14:53:07 +03:00
Pavel Karpy
d78edb66db [#199] test: Use storage.RemovePrefix flag
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2021-12-14 14:52:23 +03:00
Pavel Karpy
17907f9457 [#198] test: Adopt neotest changes
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2021-12-14 14:51:43 +03:00
Pavel Karpy
ae17343d3a [#198] go.mod: Update neo-go
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
2021-12-14 14:51:43 +03:00
123 changed files with 15347 additions and 5006 deletions

View file

@ -0,0 +1,45 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: community, triage, bug
assignees: ''
---
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory -->
<!--- If no reason/fix/additions for the bug can be suggested, -->
<!--- uncomment the following phrase: -->
<!--- No fix can be suggested by a QA engineer. Further solutions shall be up to developers. -->
## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. -->
1.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Regression
<!-- Is this issue a regression? (Yes / No) -->
<!-- If Yes, optionally please include version or commit id or PR# that caused this regression, if you have these details. -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
* Version used:
* Server setup and configuration:
* Operating System and version (`uname -a`):

View file

@ -0,0 +1 @@
blank_issues_enabled: false

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: community, triage
assignees: ''
---
## Is your feature request related to a problem? Please describe.
<!--- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
## Describe the solution you'd like
<!--- A clear and concise description of what you want to happen. -->
## Describe alternatives you've considered
<!--- A clear and concise description of any alternative solutions or features you've considered. -->
## Additional context
<!--- Add any other context or screenshots about the feature request here. -->

70
.forgejo/logo.svg Normal file
View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 184.2 51.8" style="enable-background:new 0 0 184.2 51.8;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;}
.st2{fill:#01E397;}
.st3{display:inline;fill:#010032;}
.st4{display:inline;fill:#00E599;}
.st5{display:inline;fill:#00AF92;}
.st6{fill:#00C3E5;}
</style>
<g id="Layer_2">
<g id="Layer_1-2" class="st0">
<g class="st1">
<path class="st2" d="M146.6,18.3v7.2h10.9V29h-10.9v10.7h-4V14.8h18v3.5H146.6z"/>
<path class="st2" d="M180,15.7c1.7,0.9,3,2.2,4,3.8l-3,2.7c-0.6-1.3-1.5-2.4-2.6-3.3c-1.3-0.7-2.8-1-4.3-1
c-1.4-0.1-2.8,0.3-4,1.1c-0.9,0.5-1.5,1.5-1.4,2.6c0,1,0.5,1.9,1.4,2.4c1.5,0.8,3.2,1.3,4.9,1.5c1.9,0.3,3.7,0.8,5.4,1.6
c1.2,0.5,2.2,1.3,2.9,2.3c0.6,1,1,2.2,0.9,3.4c0,1.4-0.5,2.7-1.3,3.8c-0.9,1.2-2.1,2.1-3.5,2.6c-1.7,0.6-3.4,0.9-5.2,0.8
c-5,0-8.6-1.6-10.7-5l2.9-2.8c0.7,1.4,1.8,2.5,3.1,3.3c1.5,0.7,3.1,1.1,4.7,1c1.5,0.1,2.9-0.2,4.2-0.9c0.9-0.5,1.5-1.5,1.5-2.6
c0-0.9-0.5-1.8-1.3-2.2c-1.5-0.7-3.1-1.2-4.8-1.5c-1.9-0.3-3.7-0.8-5.5-1.5c-1.2-0.5-2.2-1.4-3-2.4c-0.6-1-1-2.2-0.9-3.4
c0-1.4,0.4-2.7,1.2-3.8c0.8-1.2,2-2.2,3.3-2.8c1.6-0.7,3.4-1.1,5.2-1C176.1,14.3,178.2,14.8,180,15.7z"/>
</g>
<path class="st3" d="M73.3,16.3c1.9,1.9,2.9,4.5,2.7,7.1v15.9h-4V24.8c0-2.6-0.5-4.5-1.6-5.7c-1.2-1.2-2.8-1.8-4.5-1.7
c-1.3,0-2.5,0.3-3.7,0.8c-1.2,0.7-2.2,1.7-2.9,2.9c-0.8,1.5-1.1,3.2-1.1,4.9v13.3h-4V15.1l3.6,1.5v1.7c0.8-1.5,2.1-2.6,3.6-3.3
c1.5-0.8,3.2-1.2,4.9-1.1C68.9,13.8,71.3,14.7,73.3,16.3z"/>
<path class="st3" d="M104.4,28.3H85.6c0.1,2.2,1,4.3,2.5,5.9c1.5,1.4,3.5,2.2,5.6,2.1c1.6,0.1,3.2-0.2,4.6-0.9
c1.1-0.6,2-1.6,2.5-2.8l3.3,1.8c-0.9,1.7-2.3,3.1-4,4c-2,1-4.2,1.5-6.4,1.4c-3.7,0-6.7-1.1-8.8-3.4s-3.2-5.5-3.2-9.6s1-7.2,3-9.5
s5-3.4,8.7-3.4c2.1-0.1,4.2,0.5,6.1,1.5c1.6,1,3,2.5,3.8,4.2c0.9,1.8,1.3,3.9,1.3,5.9C104.6,26.4,104.6,27.4,104.4,28.3z
M88.1,19.3c-1.4,1.5-2.2,3.4-2.4,5.5h15.1c-0.2-2-1-3.9-2.3-5.5c-1.4-1.3-3.2-2-5.1-1.9C91.5,17.3,89.6,18,88.1,19.3z"/>
<path class="st3" d="M131,17.3c2.2,2.3,3.2,5.5,3.2,9.5s-1,7.3-3.2,9.6s-5.1,3.4-8.8,3.4s-6.7-1.1-8.9-3.4s-3.2-5.5-3.2-9.6
s1.1-7.2,3.2-9.5s5.1-3.4,8.9-3.4S128.9,15,131,17.3z M116.2,19.9c-1.5,2-2.2,4.4-2.1,6.9c-0.2,2.5,0.6,5,2.1,7
c1.5,1.7,3.7,2.7,6,2.6c2.3,0.1,4.4-0.9,5.9-2.6c1.5-2,2.3-4.5,2.1-7c0.1-2.5-0.6-4.9-2.1-6.9c-1.5-1.7-3.6-2.7-5.9-2.6
C119.9,17.2,117.7,18.2,116.2,19.9z"/>
<polygon class="st4" points="0,9.1 0,43.7 22.5,51.8 22.5,16.9 46.8,7.9 24.8,0 "/>
<polygon class="st5" points="24.3,17.9 24.3,36.8 46.8,44.9 46.8,9.6 "/>
</g>
<g>
<g>
<path class="st6" d="M41.6,17.5H28.2v6.9h10.4v3.3H28.2v10.2h-3.9V14.2h17.2V17.5z"/>
<path class="st6" d="M45.8,37.9v-18h3.3l0.4,3.2c0.5-1.2,1.2-2.1,2.1-2.7c0.9-0.6,2.1-0.9,3.5-0.9c0.4,0,0.7,0,1.1,0.1
c0.4,0.1,0.7,0.2,0.9,0.3l-0.5,3.4c-0.3-0.1-0.6-0.2-0.9-0.2C55.4,23,54.9,23,54.4,23c-0.7,0-1.5,0.2-2.2,0.6
c-0.7,0.4-1.3,1-1.8,1.8s-0.7,1.8-0.7,3v9.5H45.8z"/>
<path class="st6" d="M68.6,19.6c1.8,0,3.3,0.4,4.6,1.1c1.3,0.7,2.4,1.8,3.1,3.2s1.1,3.1,1.1,5c0,1.9-0.4,3.6-1.1,5
c-0.8,1.4-1.8,2.5-3.1,3.2c-1.3,0.7-2.9,1.1-4.6,1.1s-3.3-0.4-4.6-1.1c-1.3-0.7-2.4-1.8-3.2-3.2c-0.8-1.4-1.2-3.1-1.2-5
c0-1.9,0.4-3.6,1.2-5s1.8-2.5,3.2-3.2C65.3,19.9,66.8,19.6,68.6,19.6z M68.6,22.6c-1.1,0-2,0.2-2.8,0.7c-0.8,0.5-1.3,1.2-1.7,2.1
s-0.6,2.1-0.6,3.5c0,1.3,0.2,2.5,0.6,3.4s1,1.7,1.7,2.2s1.7,0.7,2.8,0.7c1.1,0,2-0.2,2.7-0.7c0.7-0.5,1.3-1.2,1.7-2.2
s0.6-2.1,0.6-3.4c0-1.4-0.2-2.5-0.6-3.5s-1-1.6-1.7-2.1C70.6,22.8,69.6,22.6,68.6,22.6z"/>
<path class="st6" d="M89.2,38.3c-1.8,0-3.4-0.3-4.9-1c-1.5-0.7-2.7-1.7-3.5-3l2.7-2.3c0.5,1,1.3,1.8,2.3,2.4
c1,0.6,2.2,0.9,3.6,0.9c1.1,0,2-0.2,2.6-0.6c0.6-0.4,1-0.9,1-1.6c0-0.5-0.2-0.9-0.5-1.2s-0.9-0.6-1.7-0.8l-3.8-0.8
c-1.9-0.4-3.3-1-4.1-1.9c-0.8-0.9-1.2-1.9-1.2-3.3c0-1,0.3-1.9,0.9-2.7c0.6-0.8,1.4-1.5,2.5-2s2.5-0.8,4-0.8c1.8,0,3.3,0.3,4.6,1
c1.3,0.6,2.2,1.5,2.9,2.7l-2.7,2.2c-0.5-1-1.1-1.7-2-2.1c-0.9-0.5-1.8-0.7-2.8-0.7c-0.8,0-1.4,0.1-2,0.3c-0.6,0.2-1,0.5-1.3,0.8
c-0.3,0.3-0.4,0.7-0.4,1.2c0,0.5,0.2,0.9,0.5,1.3s1,0.6,1.9,0.8l4.1,0.9c1.7,0.3,2.9,0.9,3.7,1.7c0.7,0.8,1.1,1.8,1.1,2.9
c0,1.2-0.3,2.2-0.9,3c-0.6,0.9-1.5,1.6-2.6,2C92.1,38.1,90.7,38.3,89.2,38.3z"/>
<path class="st6" d="M112.8,19.9v3H99.3v-3H112.8z M106.6,14.6v17.9c0,0.9,0.2,1.5,0.7,1.9c0.5,0.4,1.1,0.6,1.9,0.6
c0.6,0,1.2-0.1,1.7-0.3c0.5-0.2,0.9-0.5,1.3-0.8l0.9,2.8c-0.6,0.5-1.2,0.9-2,1.1c-0.8,0.3-1.7,0.4-2.7,0.4c-1,0-2-0.2-2.8-0.5
s-1.5-0.9-2-1.6c-0.5-0.8-0.7-1.7-0.8-3V15.7L106.6,14.6z"/>
<path d="M137.9,17.5h-13.3v6.9h10.4v3.3h-10.4v10.2h-3.9V14.2h17.2V17.5z"/>
<path d="M150.9,13.8c2.1,0,4,0.4,5.5,1.2c1.6,0.8,2.9,2,4,3.5l-2.6,2.5c-0.9-1.4-1.9-2.4-3.1-3c-1.1-0.6-2.5-0.9-4-0.9
c-1.2,0-2.1,0.2-2.8,0.5c-0.7,0.3-1.3,0.7-1.6,1.2c-0.3,0.5-0.5,1.1-0.5,1.7c0,0.7,0.3,1.4,0.8,1.9c0.5,0.6,1.5,1,2.9,1.3
l4.8,1.1c2.3,0.5,3.9,1.3,4.9,2.3c1,1,1.4,2.3,1.4,3.9c0,1.5-0.4,2.7-1.2,3.8c-0.8,1.1-1.9,1.9-3.3,2.5s-3.1,0.9-5,0.9
c-1.7,0-3.2-0.2-4.5-0.6c-1.3-0.4-2.5-1-3.5-1.8c-1-0.7-1.8-1.6-2.5-2.6l2.7-2.7c0.5,0.8,1.1,1.6,1.9,2.2
c0.8,0.7,1.7,1.2,2.7,1.5c1,0.4,2.2,0.5,3.4,0.5c1.1,0,2.1-0.1,2.9-0.4c0.8-0.3,1.4-0.7,1.8-1.2c0.4-0.5,0.6-1.1,0.6-1.9
c0-0.7-0.2-1.3-0.7-1.8c-0.5-0.5-1.3-0.9-2.6-1.2l-5.2-1.2c-1.4-0.3-2.6-0.8-3.6-1.3c-0.9-0.6-1.6-1.3-2.1-2.1s-0.7-1.8-0.7-2.8
c0-1.3,0.4-2.6,1.1-3.7c0.7-1.1,1.8-2,3.2-2.6C147.3,14.1,148.9,13.8,150.9,13.8z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -0,0 +1,22 @@
name: Code generation
on: [pull_request]
jobs:
wrappers:
name: Generate wrappers
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.23'
- name: Generate wrappers
run: make generate-wrappers
# The command seems to be non-deterministic.
# However, with >20 runs I haven't been able to reproduce the issue.
# This `git diff` is here to print diff in case we catch the behaviour again.
- name: Print diff
run: git diff HEAD
- name: Check that nothing has changed
run: git diff-index --exit-code HEAD

View file

@ -0,0 +1,21 @@
name: DCO action
on: [pull_request]
jobs:
dco:
name: DCO
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: '1.23'
- name: Run commit format checker
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
with:
from: 'origin/${{ github.event.pull_request.base.ref }}'

View file

@ -0,0 +1,21 @@
name: Tests
on: [pull_request]
jobs:
tests:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
go_versions: [ '1.22', '1.23' ]
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '${{ matrix.go_versions }}'
- name: Run tests
run: make test

129
.github/logo.svg vendored
View file

@ -1,129 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="logo_fs.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg57"
version="1.1"
viewBox="0 0 105 25"
height="25mm"
width="105mm">
<defs
id="defs51">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath434">
<path
d="M 0,0 H 1366 V 768 H 0 Z"
id="path432" />
</clipPath>
</defs>
<sodipodi:namedview
inkscape:window-maximized="0"
inkscape:window-y="0"
inkscape:window-x="130"
inkscape:window-height="1040"
inkscape:window-width="1274"
height="50mm"
units="mm"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="344.49897"
inkscape:cx="468.64708"
inkscape:zoom="0.7"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata54">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<g
id="g424"
transform="matrix(0.35277777,0,0,-0.35277777,63.946468,10.194047)">
<path
d="m 0,0 v -8.093 h 12.287 v -3.94 H 0 V -24.067 H -4.534 V 3.898 H 15.677 V 0 Z"
style="fill:#00e396;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path426" />
</g>
<g
transform="matrix(0.35277777,0,0,-0.35277777,-315.43002,107.34005)"
id="g428">
<g
id="g430"
clip-path="url(#clipPath434)">
<g
id="g436"
transform="translate(1112.874,278.2981)">
<path
d="M 0,0 C 1.822,-0.932 3.354,-2.359 4.597,-4.28 L 1.165,-7.373 c -0.791,1.695 -1.779,2.924 -2.966,3.686 -1.186,0.763 -2.768,1.145 -4.745,1.145 -1.949,0 -3.461,-0.389 -4.534,-1.166 -1.074,-0.777 -1.61,-1.772 -1.61,-2.987 0,-1.13 0.523,-2.027 1.568,-2.69 1.045,-0.664 2.909,-1.236 5.593,-1.716 2.514,-0.452 4.512,-1.024 5.995,-1.716 1.483,-0.693 2.564,-1.554 3.242,-2.585 0.677,-1.031 1.016,-2.309 1.016,-3.834 0,-1.639 -0.466,-3.079 -1.398,-4.322 -0.932,-1.243 -2.239,-2.197 -3.919,-2.86 -1.681,-0.664 -3.623,-0.996 -5.826,-0.996 -5.678,0 -9.689,1.892 -12.033,5.678 l 3.178,3.178 c 0.903,-1.695 2.068,-2.939 3.495,-3.729 1.426,-0.791 3.199,-1.186 5.318,-1.186 2.005,0 3.58,0.345 4.724,1.038 1.144,0.692 1.716,1.674 1.716,2.945 0,1.017 -0.516,1.835 -1.547,2.457 -1.031,0.621 -2.832,1.172 -5.402,1.653 -2.571,0.479 -4.618,1.073 -6.143,1.779 -1.526,0.706 -2.635,1.582 -3.326,2.627 -0.693,1.045 -1.039,2.316 -1.039,3.813 0,1.582 0.438,3.023 1.314,4.322 0.875,1.299 2.14,2.33 3.792,3.093 1.653,0.763 3.58,1.144 5.783,1.144 C -4.018,1.398 -1.822,0.932 0,0"
style="fill:#00e396;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path438" />
</g>
<g
id="g440"
transform="translate(993.0239,277.5454)">
<path
d="m 0,0 c 2.054,-1.831 3.083,-4.465 3.083,-7.902 v -17.935 h -4.484 v 16.366 c 0,2.914 -0.626,5.024 -1.877,6.332 -1.253,1.308 -2.924,1.962 -5.016,1.962 -1.495,0 -2.896,-0.327 -4.204,-0.981 -1.308,-0.654 -2.381,-1.719 -3.222,-3.194 -0.841,-1.477 -1.261,-3.335 -1.261,-5.576 v -14.909 h -4.484 V 1.328 l 4.086,-1.674 0.118,-1.84 c 0.933,1.681 2.222,2.923 3.867,3.727 1.643,0.803 3.493,1.205 5.548,1.205 C -4.671,2.746 -2.055,1.83 0,0"
style="fill:#000033;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path442" />
</g>
<g
id="g444"
transform="translate(1027.9968,264.0386)">
<path
d="m 0,0 h -21.128 c 0.261,-2.84 1.205,-5.044 2.83,-6.613 1.625,-1.57 3.727,-2.355 6.305,-2.355 2.054,0 3.763,0.356 5.128,1.065 1.363,0.71 2.288,1.738 2.774,3.083 l 3.755,-1.961 c -1.121,-1.981 -2.616,-3.495 -4.484,-4.54 -1.868,-1.046 -4.259,-1.569 -7.173,-1.569 -4.223,0 -7.538,1.289 -9.948,3.867 -2.41,2.578 -3.615,6.146 -3.615,10.704 0,4.558 1.149,8.127 3.447,10.705 2.298,2.578 5.557,3.867 9.779,3.867 2.615,0 4.876,-0.58 6.782,-1.738 1.905,-1.158 3.343,-2.728 4.315,-4.707 C -0.262,7.827 0.224,5.605 0.224,3.139 0.224,2.092 0.149,1.046 0,0 m -18.298,10.144 c -1.513,-1.457 -2.438,-3.512 -2.775,-6.165 h 16.982 c -0.3,2.615 -1.159,4.661 -2.578,6.137 -1.42,1.476 -3.307,2.214 -5.661,2.214 -2.466,0 -4.455,-0.728 -5.968,-2.186"
style="fill:#000033;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path446" />
</g>
<g
id="g448"
transform="translate(1057.8818,276.4246)">
<path
d="m 0,0 c 2.41,-2.578 3.615,-6.147 3.615,-10.705 0,-4.558 -1.205,-8.126 -3.615,-10.704 -2.41,-2.578 -5.726,-3.867 -9.948,-3.867 -4.222,0 -7.537,1.289 -9.947,3.867 -2.41,2.578 -3.615,6.146 -3.615,10.704 0,4.558 1.205,8.127 3.615,10.705 2.41,2.578 5.725,3.867 9.947,3.867 C -5.726,3.867 -2.41,2.578 0,0 m -16.617,-2.858 c -1.607,-1.906 -2.41,-4.522 -2.41,-7.847 0,-3.326 0.803,-5.94 2.41,-7.846 1.607,-1.905 3.83,-2.858 6.669,-2.858 2.839,0 5.063,0.953 6.67,2.858 1.606,1.906 2.41,4.52 2.41,7.846 0,3.325 -0.804,5.941 -2.41,7.847 C -4.885,-0.953 -7.109,0 -9.948,0 c -2.839,0 -5.062,-0.953 -6.669,-2.858"
style="fill:#000033;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path450" />
</g>
</g>
</g>
<g
id="g452"
transform="matrix(0.35277777,0,0,-0.35277777,5.8329581,6.5590171)">
<path
d="m 0,0 0.001,-38.946 25.286,-9.076 V -8.753 L 52.626,1.321 27.815,10.207 Z"
style="fill:#00e599;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path454" />
</g>
<g
id="g456"
transform="matrix(0.35277777,0,0,-0.35277777,15.479008,10.041927)">
<path
d="M 0,0 V -21.306 L 25.293,-30.364 25.282,9.347 Z"
style="fill:#00b091;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path458" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -1,20 +0,0 @@
name: Go
on:
pull_request:
branches: [ master ]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Test
run: go test -v ./...

4
.gitignore vendored
View file

@ -4,3 +4,7 @@
config.json config.json
/vendor/ /vendor/
.idea .idea
/bin/
# debhelpers
**/.debhelper

View file

@ -1,5 +1,151 @@
# Changelog # Changelog
Changelog for NeoFS Contract Changelog for FrostFS Contract
## [Unreleased]
### Added
### Changed
### Removed
### Updated
### Fixed
## [0.20.0]
### Added
- Add `ListFullSubjects` method to the frostfsid RPC client (#107)
- Add `ListChainNames` method to the policy contract (#105)
- Add `DeleteRecord` method to the nns contract (#114)
- Emit notification on record changes in nns contract (#109)
### Updated
- neo-go to v0.106.3
## [0.18.0] - 2023-09-14 - Academy of Sciences Glacier
### Added
- Documentation for contract storage schema (#21)
### Changed
- `container.Delete()` now accepts public key along with token (#27)
- Allow to check whether container was deleted with `container.DeletionInfo()` (#38)
### Removed
- `subnet` contract (#20)
### Updated
### Fixed
### Updating from v0.17.0
## [0.17.0] - 2023-04-13 - Furtwängler
### Added
### Changed
### Removed
- Notary disabled code from all contracts (#7)
### Updated
- `neo-go` to `v0.99.4`
### Fixed
### Updating from v0.16.0
## [0.16.0] - 2022-10-17 - Anmado (안마도, 鞍馬島)
### Added
- Support `MAINTENANCE` state of storage nodes (#269)
### Changed
- `netmap.Snapshot` and all similar methods return (#269)
### Updated
- NNS contract now sets domain expiration based on `register` arguments (#262)
### Fixed
- NNS `renew` now can only be done by the domain owner
### Updating from v0.15.5
Update deployed `Netmap` contract using `Update` method: storage of the contract
has been incompatibly changed.
## [0.15.5] - 2022-08-23
### Updated
- Update neo-go to v0.99.2 (#261)
- Makefile now takes only `v*` tags into account (#255)
## [0.15.4] - 2022-07-27
Only a version bump to update manifest.
## [0.15.3] - 2022-07-22
### Added
- Allow to build archive from source (#250)
### Changed
- Update neo-go to the latest version
- Use proper type for integer constants (#248)
## [0.15.2] - 2022-06-07
### Added
- `container.Count` method (#242)
### Changed
- Update neo-go to v0.99.0 (#246)
## [0.15.1] - 2022-04-13
### Fixed
- Max domain name fragement length (#238)
### Added
- `netmap.UpdateSnapshotCount` method (#232)
- Notifications of successful container and storage node operations (#236)
### Changed
- Update neo-go to v0.98.2 (#234)
## [0.15.0] - 2022-03-23 - Heuksando (흑산도, 黑山島)
### Fixed
- Split `UpdateState` method to allow Alphabet nodes remove storage nodes from
network map based on consensus decision in notary-enabled environment (#225)
### Changed
- Increase from 2 to 10 stored network maps in netmap contract (#224)
- Use public keys instead of `IRNode` structures in neofs and netmap contracts
(#222)
## [0.14.2] - 2022-02-07
### Fixed
- Remove duplicate records in NNS contract (#196)
### Changed
- Evict container estimations on every put (#215)
- Update neo-go to v0.98.1
## [0.14.1] - 2022-01-24
### Fixed
- Remove migration routine for reputation contract update (#220)
- Remove version check for subnet contract update (#220)
### Added
- Append version to `Update` arguments for subnet contract (#220)
## [0.14.0] - 2022-01-14 - Geojedo (거제도, 巨濟島)
### Fixed
- Sync `Update` method signature in NNS contract (#197)
- Use current block index in all `GetDisgnatedByRole` invocations (#209)
### Added
- Version check during contract update (#204)
### Changed
- Use `storage.RemovePrefix` in subnet contract (#199)
### Removed
- Netmap contract hash usage in proxy contract (#205)
- Legacy contract owner records from contract storage (#202)
## [0.13.2] - 2021-12-14 ## [0.13.2] - 2021-12-14
@ -294,6 +440,17 @@ Preview4-testnet version of NeoFS contracts.
Preview4 compatible contracts. Preview4 compatible contracts.
[Unreleased]: https://github.com/nspcc-dev/neofs-contract/compare/v0.16.0...master
[0.16.0]: https://github.com/nspcc-dev/neofs-contract/compare/v0.15.5...v0.16.0
[0.15.5]: https://github.com/nspcc-dev/neofs-contract/compare/v0.15.4...v0.15.5
[0.15.4]: https://github.com/nspcc-dev/neofs-contract/compare/v0.15.3...v0.15.4
[0.15.3]: https://github.com/nspcc-dev/neofs-contract/compare/v0.15.2...v0.15.3
[0.15.2]: https://github.com/nspcc-dev/neofs-contract/compare/v0.15.1...v0.15.2
[0.15.1]: https://github.com/nspcc-dev/neofs-contract/compare/v0.15.0...v0.15.1
[0.15.0]: https://github.com/nspcc-dev/neofs-contract/compare/v0.14.2...v0.15.0
[0.14.2]: https://github.com/nspcc-dev/neofs-contract/compare/v0.14.1...v0.14.2
[0.14.1]: https://github.com/nspcc-dev/neofs-contract/compare/v0.14.0...v0.14.1
[0.14.0]: https://github.com/nspcc-dev/neofs-contract/compare/v0.13.2...v0.14.0
[0.13.2]: https://github.com/nspcc-dev/neofs-contract/compare/v0.13.1...v0.13.2 [0.13.2]: https://github.com/nspcc-dev/neofs-contract/compare/v0.13.1...v0.13.2
[0.13.1]: https://github.com/nspcc-dev/neofs-contract/compare/v0.13.0...v0.13.1 [0.13.1]: https://github.com/nspcc-dev/neofs-contract/compare/v0.13.0...v0.13.1
[0.13.0]: https://github.com/nspcc-dev/neofs-contract/compare/v0.12.2...v0.13.0 [0.13.0]: https://github.com/nspcc-dev/neofs-contract/compare/v0.12.2...v0.13.0

5
CODEOWNERS Normal file
View file

@ -0,0 +1,5 @@
.forgejo/.* @potyarkin
Makefile @potyarkin
frostfsid/client/.* @dkirillov
.* @TrueCloudLab/storage-core-developers @TrueCloudLab/storage-core-committers @TrueCloudLab/storage-service-developers @TrueCloudLab/storage-service-committers
tests/.* @fyrchik

View file

@ -1,23 +1,34 @@
#!/usr/bin/make -f #!/usr/bin/make -f
SHELL=bash SHELL=bash
GOBIN ?= $(shell go env GOPATH)/bin # GOBIN is used only to install neo-go and allows to override
# the location of written binary.
export GOBIN ?= $(shell pwd)/bin
NEOGO ?= $(GOBIN)/cli NEOGO ?= $(GOBIN)/cli
VERSION?=$(shell git describe --tags) VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
# .deb package versioning
OS_RELEASE = $(shell lsb_release -cs)
PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
sed -E "s/(.*)-(g[a-fA-F0-9]{6,8})(.*)/\1\3~\2/" | \
sed "s/-/~/")-${OS_RELEASE}
.PHONY: all build clean test neo-go .PHONY: all build clean test neo-go
.PHONY: alphabet mainnet morph nns sidechain .PHONY: alphabet mainnet morph nns sidechain
.PHONY: debpackage debclean
build: neo-go all build: neo-go all
all: sidechain mainnet all: sidechain mainnet
sidechain: alphabet morph nns sidechain: alphabet morph nns
alphabet_sc = alphabet alphabet_sc = alphabet
morph_sc = audit balance container neofsid netmap proxy reputation subnet morph_sc = balance container frostfsid netmap proxy policy
mainnet_sc = neofs processing mainnet_sc = frostfs processing
nns_sc = nns nns_sc = nns
all_sc = $(alphabet_sc) $(morph_sc) $(mainnet_sc) $(nns_sc)
define sc_template define sc_template
$(2)$(1)/$(1)_contract.nef: $(2)$(1)/$(1)_contract.go $(2)$(1)/$(1)_contract.nef: $(2)$(1)/$(1)_contract.go $(2)$(1)/config.yml
$(NEOGO) contract compile -i $(2)$(1) -c $(if $(2),$(2),$(1)/)config.yml -m $(2)$(1)/config.json -o $(2)$(1)/$(1)_contract.nef $(NEOGO) contract compile -i $(2)$(1) -c $(if $(2),$(2),$(1)/)config.yml -m $(2)$(1)/config.json -o $(2)$(1)/$(1)_contract.nef
$(if $(2),$(2)$(1)/$(1)_contract.go: alphabet/alphabet.go alphabet/alphabet.tpl $(if $(2),$(2)$(1)/$(1)_contract.go: alphabet/alphabet.go alphabet/alphabet.tpl
@ -39,12 +50,36 @@ neo-go:
@go list -f '{{.Path}}/...@{{.Version}}' -m github.com/nspcc-dev/neo-go \ @go list -f '{{.Path}}/...@{{.Version}}' -m github.com/nspcc-dev/neo-go \
| xargs go install -v | xargs go install -v
generate-wrapper.%:
@mkdir -p ./rpcclient/$*
@# Note, that bindings file is currently missing: is can be emitted by compiler,
@# but this leads to a large amount of code duplication. It can be written by hand,
@# in case we need to override the type of some variables.
@# --config $*/$*.bindings.yml
@# Unfortunately, primitive integer types are not yet supported.
$(NEOGO) contract generate-rpcwrapper --manifest=$*/config.json --out ./rpcclient/$*/client.go
generate-wrappers: build $(foreach sc,$(all_sc),generate-wrapper.$(sc))
test: test:
@go test ./tests/... @go test ./tests/...
# Run all code formatters
fmts: fumpt imports
# Reformat imports
imports:
@echo "⇒ Processing goimports check"
@goimports -w $(all_sc) tests/
fumpt:
@echo "⇒ Processing gofumpt check"
@gofumpt -l -w $(all_sc) tests/
clean: clean:
find . -name '*.nef' -exec rm -rf {} \; find . -name '*.nef' -exec rm -rf {} \;
find . -name 'config.json' -exec rm -rf {} \; find . -name 'config.json' -exec rm -rf {} \;
rm -rf ./bin/
mr_proper: clean mr_proper: clean
for sc in $(alphabet_sc); do\ for sc in $(alphabet_sc); do\
@ -52,6 +87,18 @@ mr_proper: clean
done done
archive: build archive: build
@tar --transform "s|^./|neofs-contract-$(VERSION)/|" \ @tar --transform "s|^./|frostfs-contract-$(VERSION)/|" \
-czf neofs-contract-$(VERSION).tar.gz \ -czf frostfs-contract-$(VERSION).tar.gz \
$(shell find . -name '*.nef' -o -name 'config.json') $(shell find . -name '*.nef' -o -name 'config.json')
# Package for Debian
debpackage:
dch --package frostfs-contract \
--controlmaint \
--newversion $(PKG_VERSION) \
--distribution $(OS_RELEASE) \
"Please see CHANGELOG.md for code changes for $(VERSION)"
dpkg-buildpackage --no-sign -b
debclean:
dh clean

View file

@ -1,35 +1,34 @@
<p align="center"> <p align="center">
<img src="./.github/logo.svg" width="500px" alt="NeoFS"> <img src="./.forgejo/logo.svg" width="500px" alt="FrostFS">
</p> </p>
<p align="center"> <p align="center">
<a href="https://fs.neo.org">NeoFS</a> related smart contracts. <a href="https://frostfs.info">FrostFS</a> related smart contracts.
</p> </p>
--- ---
# Overview # Overview
NeoFS-Contract contains all NeoFS related contracts written for FrostFS-Contract contains all FrostFS related contracts written for
[neo-go](https://github.com/nspcc-dev/neo-go) compiler. These contracts [neo-go](https://github.com/nspcc-dev/neo-go) compiler. These contracts
are deployed both in main chain and side chain. are deployed both in the mainchain and the sidechain.
Main chain contracts: Mainchain contracts:
- neofs - frostfs
- processing - processing
Side chain contracts: Sidechain contracts:
- alphabet - alphabet
- audit - audit
- balance - balance
- container - container
- neofsid - frostfsid
- netmap - netmap
- nns - nns
- proxy - proxy
- reputation - reputation
- subnet
# Getting started # Getting started
@ -37,11 +36,11 @@ Side chain contracts:
To compile smart contracts you need: To compile smart contracts you need:
- [neo-go](https://github.com/nspcc-dev/neo-go) >= 0.98.0 - [neo-go](https://github.com/nspcc-dev/neo-go) >= 0.99.2
## Compilation ## Compilation
To build and compile smart contract run `make all` command. Compiled contracts To build and compile smart contract, run `make all` command. Compiled contracts
`*_contract.nef` and manifest `config.json` files are placed in the `*_contract.nef` and manifest `config.json` files are placed in the
corresponding directories. corresponding directories.
@ -51,13 +50,12 @@ $ make all
/home/user/go/bin/cli contract compile -i audit -c audit/config.yml -m audit/config.json -o audit/audit_contract.nef /home/user/go/bin/cli contract compile -i audit -c audit/config.yml -m audit/config.json -o audit/audit_contract.nef
/home/user/go/bin/cli contract compile -i balance -c balance/config.yml -m balance/config.json -o balance/balance_contract.nef /home/user/go/bin/cli contract compile -i balance -c balance/config.yml -m balance/config.json -o balance/balance_contract.nef
/home/user/go/bin/cli contract compile -i container -c container/config.yml -m container/config.json -o container/container_contract.nef /home/user/go/bin/cli contract compile -i container -c container/config.yml -m container/config.json -o container/container_contract.nef
/home/user/go/bin/cli contract compile -i neofsid -c neofsid/config.yml -m neofsid/config.json -o neofsid/neofsid_contract.nef /home/user/go/bin/cli contract compile -i frostfsid -c frostfsid/config.yml -m frostfsid/config.json -o frostfsid/frostfsid_contract.nef
/home/user/go/bin/cli contract compile -i netmap -c netmap/config.yml -m netmap/config.json -o netmap/netmap_contract.nef /home/user/go/bin/cli contract compile -i netmap -c netmap/config.yml -m netmap/config.json -o netmap/netmap_contract.nef
/home/user/go/bin/cli contract compile -i proxy -c proxy/config.yml -m proxy/config.json -o proxy/proxy_contract.nef /home/user/go/bin/cli contract compile -i proxy -c proxy/config.yml -m proxy/config.json -o proxy/proxy_contract.nef
/home/user/go/bin/cli contract compile -i reputation -c reputation/config.yml -m reputation/config.json -o reputation/reputation_contract.nef /home/user/go/bin/cli contract compile -i reputation -c reputation/config.yml -m reputation/config.json -o reputation/reputation_contract.nef
/home/user/go/bin/cli contract compile -i subnet -c subnet/config.yml -m subnet/config.json -o subnet/subnet_contract.nef
/home/user/go/bin/cli contract compile -i nns -c nns/config.yml -m nns/config.json -o nns/nns_contract.nef /home/user/go/bin/cli contract compile -i nns -c nns/config.yml -m nns/config.json -o nns/nns_contract.nef
/home/user/go/bin/cli contract compile -i neofs -c neofs/config.yml -m neofs/config.json -o neofs/neofs_contract.nef /home/user/go/bin/cli contract compile -i frostfs -c frostfs/config.yml -m frostfs/config.json -o frostfs/frostfs_contract.nef
/home/user/go/bin/cli contract compile -i processing -c processing/config.yml -m processing/config.json -o processing/processing_contract.nef /home/user/go/bin/cli contract compile -i processing -c processing/config.yml -m processing/config.json -o processing/processing_contract.nef
``` ```
@ -69,25 +67,24 @@ $ NEOGO=/home/user/neo-go/bin/neo-go make all
Remove compiled files with `make clean` or `make mr_proper` command. Remove compiled files with `make clean` or `make mr_proper` command.
## Building Debian package
To build Debian package containing compiled contracts, run `make debpackage`
command. Package will install compiled contracts `*_contract.nef` and manifest
`config.json` with corresponding directories to `/var/lib/neofs/contract` for
further usage.
It will download and build neo-go, if needed.
To clean package-related files, use `make debclean`.
# Testing # Testing
Smartcontract tests reside in `tests/` directory. To execute test suite Smartcontract tests reside in `tests/` directory. To execute test suite
after applying changes simply run `make test`. after applying changes, simply run `make test`.
``` ```
$ make test $ make test
ok github.com/nspcc-dev/neofs-contract/tests 0.462s ok git.frostfs.info/TrueCloudLab/frostfs-contract/tests 0.462s
``` ```
# NeoFS API compatibility
|neofs-contract version|supported NeoFS API versions|
|:------------------:|:--------------------------:|
|v0.9.x|[v2.7.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.7.0), [v2.8.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.8.0)|
|v0.10.x|[v2.7.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.7.0), [v2.8.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.8.0)|
|v0.11.x|[v2.7.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.7.0), [v2.8.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.8.0), [v2.9.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.9.0)|
|v0.12.x|[v2.10.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.10.0)|
|v0.13.x|[v2.11.0](https://github.com/nspcc-dev/neofs-api/releases/tag/v2.11.0)|
# License # License
This project is licensed under the GPLv3 License - see the This project is licensed under the GPLv3 License - see the

1
VERSION Normal file
View file

@ -0,0 +1 @@
v0.20.0

View file

@ -1,15 +1,14 @@
package alphabet package alphabet
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo" "github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
) )
const ( const (
@ -19,36 +18,35 @@ const (
indexKey = "index" indexKey = "index"
totalKey = "threshold" totalKey = "threshold"
nameKey = "name" nameKey = "name"
notaryDisabledKey = "notary"
) )
// OnNEP17Payment is a callback for NEP-17 compatible native GAS and NEO // OnNEP17Payment is a callback for NEP-17 compatible native GAS and NEO
// contracts. // contracts.
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { func OnNEP17Payment(from interop.Hash160, amount int, data any) {
caller := runtime.GetCallingScriptHash() caller := runtime.GetCallingScriptHash()
if !common.BytesEqual(caller, []byte(gas.Hash)) && !common.BytesEqual(caller, []byte(neo.Hash)) { if !common.BytesEqual(caller, []byte(gas.Hash)) && !common.BytesEqual(caller, []byte(neo.Hash)) {
common.AbortWithMessage("alphabet contract accepts GAS and NEO only") common.AbortWithMessage("alphabet contract accepts GAS and NEO only")
} }
} }
func _deploy(data interface{}, isUpdate bool) { func _deploy(data any, isUpdate bool) {
ctx := storage.GetContext()
if isUpdate { if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return return
} }
args := data.(struct { args := data.(struct {
notaryDisabled bool addrNetmap interop.Hash160
addrNetmap interop.Hash160 addrProxy interop.Hash160
addrProxy interop.Hash160 name string
name string index int
index int total int
total int
}) })
ctx := storage.GetContext() if len(args.addrNetmap) != interop.Hash160Len || len(args.addrProxy) != interop.Hash160Len {
if len(args.addrNetmap) != interop.Hash160Len || !args.notaryDisabled && len(args.addrProxy) != interop.Hash160Len {
panic("incorrect length of contract script hash") panic("incorrect length of contract script hash")
} }
@ -58,34 +56,26 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, indexKey, args.index) storage.Put(ctx, indexKey, args.index)
storage.Put(ctx, totalKey, args.total) storage.Put(ctx, totalKey, args.total)
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
common.InitVote(ctx)
runtime.Log(args.name + " notary disabled")
}
runtime.Log(args.name + " contract initialized") runtime.Log(args.name + " contract initialized")
} }
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. It can be invoked
// only by committee. // only by committee.
func Update(script []byte, manifest []byte, data interface{}) { func Update(script []byte, manifest []byte, data any) {
if !common.HasUpdateAccess() { if !common.HasUpdateAccess() {
panic("only committee can update contract") panic("only committee can update contract")
} }
contract.Call(interop.Hash160(management.Hash), "update", management.UpdateWithData(script, manifest, common.AppendVersion(data))
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("alphabet contract updated") runtime.Log("alphabet contract updated")
} }
// GAS returns amount of side chain GAS stored in contract account. // GAS returns the amount of the sidechain GAS stored in the contract account.
func Gas() int { func Gas() int {
return gas.BalanceOf(runtime.GetExecutingScriptHash()) return gas.BalanceOf(runtime.GetExecutingScriptHash())
} }
// NEO returns amount of side chain NEO stored in contract account. // NEO returns the amount of sidechain NEO stored in the contract account.
func Neo() int { func Neo() int {
return neo.BalanceOf(runtime.GetExecutingScriptHash()) return neo.BalanceOf(runtime.GetExecutingScriptHash())
} }
@ -103,7 +93,7 @@ func index(ctx storage.Context) int {
return storage.Get(ctx, indexKey).(int) return storage.Get(ctx, indexKey).(int)
} }
func checkPermission(ir []common.IRNode) bool { func checkPermission(ir []interop.PublicKey) bool {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
index := index(ctx) // read from contract memory index := index(ctx) // read from contract memory
@ -112,22 +102,18 @@ func checkPermission(ir []common.IRNode) bool {
} }
node := ir[index] node := ir[index]
return runtime.CheckWitness(node.PublicKey) return runtime.CheckWitness(node)
} }
// Emit method produces side chain GAS and distributes it among Inner Ring nodes // Emit method produces sidechain GAS and distributes it among Inner Ring nodes
// and proxy contract. Can be invoked only by Alphabet node of the Inner Ring. // and proxy contract. It can be invoked only by an Alphabet node of the Inner Ring.
// //
// To produce GAS, alphabet contract transfers all available NEO from contract // To produce GAS, an alphabet contract transfers all available NEO from the contract
// account to itself. If notary enabled, then 50% of the GAS in the contract account // account to itself. 50% of the GAS in the contract account
// transferred to proxy contract. 43.75% of the GAS are equally distributed // are transferred to proxy contract. 43.75% of the GAS are equally distributed
// among all Inner Ring nodes. Remaining 6.25% of the GAS stays in the contract. // among all Inner Ring nodes. Remaining 6.25% of the GAS stay in the contract.
//
// If notary disabled, then 87.5% of the GAS are equally distributed among all
// Inner Ring nodes. Remaining 12.5% of the GAS stays in the contract.
func Emit() { func Emit() {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
alphabet := common.AlphabetNodes() alphabet := common.AlphabetNodes()
if !checkPermission(alphabet) { if !checkPermission(alphabet) {
@ -142,37 +128,28 @@ func Emit() {
gasBalance := gas.BalanceOf(contractHash) gasBalance := gas.BalanceOf(contractHash)
if !notaryDisabled { proxyAddr := storage.Get(ctx, proxyKey).(interop.Hash160)
proxyAddr := storage.Get(ctx, proxyKey).(interop.Hash160)
proxyGas := gasBalance / 2 proxyGas := gasBalance / 2
if proxyGas == 0 { if proxyGas == 0 {
panic("no gas to emit") panic("no gas to emit")
}
if !gas.Transfer(contractHash, proxyAddr, proxyGas, nil) {
runtime.Log("could not transfer GAS to proxy contract")
}
gasBalance -= proxyGas
runtime.Log("utility token has been emitted to proxy contract")
} }
var innerRing []common.IRNode if !gas.Transfer(contractHash, proxyAddr, proxyGas, nil) {
runtime.Log("could not transfer GAS to proxy contract")
if notaryDisabled {
netmapContract := storage.Get(ctx, netmapKey).(interop.Hash160)
innerRing = common.InnerRingNodesFromNetmap(netmapContract)
} else {
innerRing = common.InnerRingNodes()
} }
gasBalance -= proxyGas
runtime.Log("utility token has been emitted to proxy contract")
innerRing := common.InnerRingNodes()
gasPerNode := gasBalance * 7 / 8 / len(innerRing) gasPerNode := gasBalance * 7 / 8 / len(innerRing)
if gasPerNode != 0 { if gasPerNode != 0 {
for _, node := range innerRing { for _, node := range innerRing {
address := contract.CreateStandardAccount(node.PublicKey) address := contract.CreateStandardAccount(node)
if !gas.Transfer(contractHash, address, gasPerNode, nil) { if !gas.Transfer(contractHash, address, gasPerNode, nil) {
runtime.Log("could not transfer GAS to one of IR node") runtime.Log("could not transfer GAS to one of IR node")
} }
@ -182,34 +159,19 @@ func Emit() {
} }
} }
// Vote method votes for side chain committee. Requires multisignature from // Vote method votes for the sidechain committee. It requires multisignature from
// Alphabet nodes of the Inner Ring. // Alphabet nodes of the Inner Ring.
// //
// This method is used when governance changes list of Alphabet nodes of the // This method is used when governance changes the list of Alphabet nodes of the
// Inner Ring. Alphabet nodes share keys with side chain validators, therefore // Inner Ring. Alphabet nodes share keys with sidechain validators, therefore
// it is required to change them as well. To do that NEO holders, which are // it is required to change them as well. To do that, NEO holders (which are
// alphabet contracts, should vote for new committee. // alphabet contracts) should vote for a new committee.
func Vote(epoch int, candidates []interop.PublicKey) { func Vote(epoch int, candidates []interop.PublicKey) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
index := index(ctx) index := index(ctx)
name := name(ctx) name := name(ctx)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("invalid invoker")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
curEpoch := currentEpoch(ctx) curEpoch := currentEpoch(ctx)
if epoch != curEpoch { if epoch != curEpoch {
@ -219,50 +181,21 @@ func Vote(epoch int, candidates []interop.PublicKey) {
candidate := candidates[index%len(candidates)] candidate := candidates[index%len(candidates)]
address := runtime.GetExecutingScriptHash() address := runtime.GetExecutingScriptHash()
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
id := voteID(epoch, candidates)
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
ok := neo.Vote(address, candidate) ok := neo.Vote(address, candidate)
if ok { if ok {
runtime.Log(name + ": successfully voted for validator") runtime.Log(name + ": successfully voted for validator")
} else { } else {
runtime.Log(name + ": vote has been failed") runtime.Log(name + ": vote has been failed")
} }
return
} }
func voteID(epoch interface{}, args []interop.PublicKey) []byte { // Name returns the Glagolitic name of the contract.
var (
result []byte
epochBytes = epoch.([]byte)
)
result = append(result, epochBytes...)
for i := range args {
result = append(result, args[i]...)
}
return crypto.Sha256(result)
}
// Name returns Glagolitic name of the contract.
func Name() string { func Name() string {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return name(ctx) return name(ctx)
} }
// Version returns version of the contract. // Version returns the version of the contract.
func Version() int { func Version() int {
return common.Version return common.Version
} }

View file

@ -1,4 +1,4 @@
name: "NeoFS Alphabet" name: "Alphabet"
safemethods: ["gas", "neo", "name", "version"] safemethods: ["gas", "neo", "name", "version"]
permissions: permissions:
- methods: ["update", "transfer", "vote"] - methods: ["update", "transfer", "vote"]

View file

@ -1,21 +1,31 @@
/* /*
Alphabet contract is a contract deployed in NeoFS side chain. Alphabet contract is a contract deployed in FrostFS sidechain.
Alphabet contract is designed to support GAS producing and voting for new Alphabet contract is designed to support GAS production and vote for new
validators in the side chain. NEO token is required to produce GAS and vote for validators in the sidechain. NEO token is required to produce GAS and vote for
a new committee. If can be distributed among alphabet nodes of Inner Ring. a new committee. It can be distributed among alphabet nodes of the Inner Ring.
However, some of them may be malicious and some NEO can be lost. It will lead However, some of them may be malicious, and some NEO can be lost. It will destabilize
to side chain economic destabilization. To avoid it, all 100 000 000 NEO are the economic of the sidechain. To avoid it, all 100,000,000 NEO are
distributed among all alphabet contracts. distributed among all alphabet contracts.
To identify alphabet contracts, they are named with letters of the Glagolitic. To identify alphabet contracts, they are named with letters of the Glagolitic alphabet.
Names are set at contract deploy. Alphabet nodes of Inner Ring communicate with Names are set at contract deploy. Alphabet nodes of the Inner Ring communicate with
one of the alphabetical contracts to emit GAS. To vote for a new list of side one of the alphabetical contracts to emit GAS. To vote for a new list of side
chain committee, alphabet nodes of Inner Ring create multisignature transactions chain committee, alphabet nodes of the Inner Ring create multisignature transactions
for each alphabet contract. for each alphabet contract.
Contract notifications # Contract notifications
Alphabet contract does not produce notifications to process. Alphabet contract does not produce notifications to process.
# Contract storage scheme
| Key | Value | Description |
|--------------------|------------|-------------------------------------------------|
| `netmapScriptHash` | Hash160 | netmap contract hash |
| `proxyScriptHash` | Hash160 | proxy contract hash |
| `name` | string | assigned glagolitic letter |
| `index` | int | the index of deployed alphabet contract |
| `threshold` | int | the total number of deployed alphabet contracts |
*/ */
package alphabet package alphabet

View file

@ -1,238 +0,0 @@
package audit
import (
"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/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
)
type (
auditHeader struct {
epoch int
cid []byte
from interop.PublicKey
}
)
// Audit key is a combination of epoch, container ID and public key of node that
// executed audit. Together it should be no more than 64 bytes. We can't shrink
// epoch and container ID since we iterate over these values. But we can shrink
// public key by using first bytes of the hashed value.
// V2 format
const maxKeySize = 24 // 24 + 32 (container ID length) + 8 (epoch length) = 64
func (a auditHeader) ID() []byte {
var buf interface{} = a.epoch
hashedKey := crypto.Sha256(a.from)
shortedKey := hashedKey[:maxKeySize]
return append(buf.([]byte), append(a.cid, shortedKey...)...)
}
const (
netmapContractKey = "netmapScriptHash"
notaryDisabledKey = "notary"
)
func _deploy(data interface{}, isUpdate bool) {
if isUpdate {
return
}
args := data.(struct {
notaryDisabled bool
addrNetmap interop.Hash160
})
ctx := storage.GetContext()
if len(args.addrNetmap) != interop.Hash160Len {
panic("incorrect length of contract script hash")
}
storage.Put(ctx, netmapContractKey, args.addrNetmap)
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
runtime.Log("audit contract notary disabled")
}
runtime.Log("audit contract initialized")
}
// Update method updates contract source code and manifest. Can be invoked
// only by committee.
func Update(script []byte, manifest []byte, data interface{}) {
if !common.HasUpdateAccess() {
panic("only committee can update contract")
}
contract.Call(interop.Hash160(management.Hash), "update",
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("audit contract updated")
}
// Put method stores stable marshalled `DataAuditResult` structure. Can be
// invoked only by Inner Ring nodes.
//
// Inner Ring nodes perform audit of the containers and produce `DataAuditResult`
// structures. They are being stored in audit contract and used for settlements
// in later epochs.
func Put(rawAuditResult []byte) {
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var innerRing []common.IRNode
if notaryDisabled {
netmapContract := storage.Get(ctx, netmapContractKey).(interop.Hash160)
innerRing = common.InnerRingNodesFromNetmap(netmapContract)
} else {
innerRing = common.InnerRingNodes()
}
hdr := newAuditHeader(rawAuditResult)
presented := false
for i := range innerRing {
ir := innerRing[i]
if common.BytesEqual(ir.PublicKey, hdr.from) {
presented = true
break
}
}
if !runtime.CheckWitness(hdr.from) || !presented {
panic("put access denied")
}
storage.Put(ctx, hdr.ID(), rawAuditResult)
runtime.Log("audit: result has been saved")
}
// Get method returns stable marshaled DataAuditResult structure.
//
// ID of the DataAuditResult can be obtained from listing methods.
func Get(id []byte) []byte {
ctx := storage.GetReadOnlyContext()
return storage.Get(ctx, id).([]byte)
}
// List method returns list of all available DataAuditResult IDs from
// contract storage.
func List() [][]byte {
ctx := storage.GetReadOnlyContext()
it := storage.Find(ctx, []byte{}, storage.KeysOnly)
return list(it)
}
// ListByEpoch method returns list of DataAuditResult IDs generated in
// specified epoch.
func ListByEpoch(epoch int) [][]byte {
ctx := storage.GetReadOnlyContext()
var buf interface{} = epoch
it := storage.Find(ctx, buf.([]byte), storage.KeysOnly)
return list(it)
}
// ListByCID method returns list of DataAuditResult IDs generated in
// specified epoch for specified container.
func ListByCID(epoch int, cid []byte) [][]byte {
ctx := storage.GetReadOnlyContext()
var buf interface{} = epoch
prefix := append(buf.([]byte), cid...)
it := storage.Find(ctx, prefix, storage.KeysOnly)
return list(it)
}
// ListByNode method returns list of DataAuditResult IDs generated in
// specified epoch for specified container by specified Inner Ring node.
func ListByNode(epoch int, cid []byte, key interop.PublicKey) [][]byte {
ctx := storage.GetReadOnlyContext()
hdr := auditHeader{
epoch: epoch,
cid: cid,
from: key,
}
it := storage.Find(ctx, hdr.ID(), storage.KeysOnly)
return list(it)
}
func list(it iterator.Iterator) [][]byte {
var result [][]byte
ignore := [][]byte{
[]byte(netmapContractKey),
[]byte(notaryDisabledKey),
}
loop:
for iterator.Next(it) {
key := iterator.Value(it).([]byte) // iterator MUST BE `storage.KeysOnly`
for _, ignoreKey := range ignore {
if common.BytesEqual(key, ignoreKey) {
continue loop
}
}
result = append(result, key)
}
return result
}
// Version returns version of the contract.
func Version() int {
return common.Version
}
// readNext reads length from first byte and then reads data (max 127 bytes).
func readNext(input []byte) ([]byte, int) {
var buf interface{} = input[0]
ln := buf.(int)
return input[1 : 1+ln], 1 + ln
}
func newAuditHeader(input []byte) auditHeader {
// V2 format
offset := int(input[1])
offset = 2 + offset + 1 // version prefix + version len + epoch prefix
var buf interface{} = input[offset : offset+8] // [ 8 integer bytes ]
epoch := buf.(int)
offset = offset + 8
// cid is a nested structure with raw bytes
// [ cid struct prefix (wireType + len = 2 bytes), cid value wireType (1 byte), ... ]
cid, cidOffset := readNext(input[offset+2+1:])
// key is a raw byte
// [ public key wireType (1 byte), ... ]
key, _ := readNext(input[offset+2+1+cidOffset+1:])
return auditHeader{
epoch,
cid,
key,
}
}

View file

@ -1,4 +0,0 @@
name: "NeoFS Audit"
safemethods: ["get", "list", "listByEpoch", "listByCID", "listByNode", "version"]
permissions:
- methods: ["update"]

View file

@ -1,22 +0,0 @@
/*
Audit contract is a contract deployed in NeoFS side chain.
Inner Ring nodes perform an audit of the registered containers in every epoch.
If container contains StorageGroup objects, then the Inner Ring node initializes
a series of audit checks. Based on the results of these checks, the Inner Ring
node creates a DataAuditResult structure for the container. The content of this
structure makes it possible to determine which storage nodes were examined and
the status of these checks. Based on this information, container owner is
charged for data storage.
Audit contract is used as reliable and verifiable storage for all
DataAuditResult structures. At the end of the data audit routine, the Inner Ring
nodes send a stable marshaled version of the DataAuditResult structure to the
contract. When Alphabet nodes of the Inner Ring perform settlement operations,
they list and get these AuditResultStructures from the audit contract.
Contract notifications
Alphabet contract does not produce notifications to process.
*/
package audit

View file

@ -1,14 +1,13 @@
package balance package balance
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
) )
type ( type (
@ -22,9 +21,9 @@ type (
CirculationKey string CirculationKey string
} }
// Account structure stores metadata of each NeoFS balance account. // Account structure stores metadata of each FrostFS balance account.
Account struct { Account struct {
// Active balance // Active balance
Balance int Balance int
// Until valid for lock accounts // Until valid for lock accounts
Until int Until int
@ -32,16 +31,22 @@ type (
// account wasn't burnt. // account wasn't burnt.
Parent []byte Parent []byte
} }
// account is a stored view of Account with fixed int size
account struct {
Balance []byte
Until []byte
Parent []byte
}
) )
const ( const (
symbol = "NEOFS" symbol = "FROSTFS"
decimals = 12 decimals = 12
circulation = "MainnetGAS" circulation = "MainnetGAS"
netmapContractKey = "netmapScriptHash" netmapContractKey = "netmapScriptHash"
containerContractKey = "containerScriptHash" containerContractKey = "containerScriptHash"
notaryDisabledKey = "notary"
) )
var token Token var token Token
@ -58,19 +63,20 @@ func init() {
token = createToken() token = createToken()
} }
func _deploy(data interface{}, isUpdate bool) { func _deploy(data any, isUpdate bool) {
ctx := storage.GetContext()
if isUpdate { if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return return
} }
args := data.(struct { args := data.(struct {
notaryDisabled bool addrNetmap interop.Hash160
addrNetmap interop.Hash160 addrContainer interop.Hash160
addrContainer interop.Hash160
}) })
ctx := storage.GetContext()
if len(args.addrNetmap) != interop.Hash160Len || len(args.addrContainer) != interop.Hash160Len { if len(args.addrNetmap) != interop.Hash160Len || len(args.addrContainer) != interop.Hash160Len {
panic("incorrect length of contract script hash") panic("incorrect length of contract script hash")
} }
@ -78,109 +84,67 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, netmapContractKey, args.addrNetmap) storage.Put(ctx, netmapContractKey, args.addrNetmap)
storage.Put(ctx, containerContractKey, args.addrContainer) storage.Put(ctx, containerContractKey, args.addrContainer)
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
common.InitVote(ctx)
runtime.Log("balance contract notary disabled")
}
runtime.Log("balance contract initialized") runtime.Log("balance contract initialized")
} }
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. It can be invoked
// only by committee. // only by committee.
func Update(script []byte, manifest []byte, data interface{}) { func Update(script []byte, manifest []byte, data any) {
if !common.HasUpdateAccess() { if !common.HasUpdateAccess() {
panic("only committee can update contract") panic("only committee can update contract")
} }
contract.Call(interop.Hash160(management.Hash), "update", management.UpdateWithData(script, manifest, common.AppendVersion(data))
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("balance contract updated") runtime.Log("balance contract updated")
} }
// Symbol is a NEP-17 standard method that returns NEOFS token symbol. // Symbol is a NEP-17 standard method that returns FROSTFS token symbol.
func Symbol() string { func Symbol() string {
return token.Symbol return token.Symbol
} }
// Decimals is a NEP-17 standard method that returns precision of NeoFS // Decimals is a NEP-17 standard method that returns precision of FrostFS
// balances. // balances.
func Decimals() int { func Decimals() int {
return token.Decimals return token.Decimals
} }
// TotalSupply is a NEP-17 standard method that returns total amount of main // TotalSupply is a NEP-17 standard method that returns total amount of main
// chain GAS in the NeoFS network. // chain GAS in FrostFS network.
func TotalSupply() int { func TotalSupply() int {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return token.getSupply(ctx) return token.getSupply(ctx)
} }
// BalanceOf is a NEP-17 standard method that returns NeoFS balance of specified // BalanceOf is a NEP-17 standard method that returns FrostFS balance of the specified
// account. // account.
func BalanceOf(account interop.Hash160) int { func BalanceOf(account interop.Hash160) int {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return token.balanceOf(ctx, account) return token.balanceOf(ctx, account)
} }
// Transfer is a NEP-17 standard method that transfers NeoFS balance from one // Transfer is a NEP-17 standard method that transfers FrostFS balance from one
// account to other. Can be invoked only by account owner. // account to another. It can be invoked only by the account owner.
// //
// Produces Transfer and TransferX notifications. TransferX notification // It produces Transfer and TransferX notifications. TransferX notification
// will have empty details field. // will have empty details field.
func Transfer(from, to interop.Hash160, amount int, data interface{}) bool { func Transfer(from, to interop.Hash160, amount int, data any) bool {
ctx := storage.GetContext() ctx := storage.GetContext()
return token.transfer(ctx, from, to, amount, false, nil) return token.transfer(ctx, from, to, amount, false, nil)
} }
// TransferX is a method for NeoFS balance transfers from one account to // TransferX is a method for FrostFS balance to be transferred from one account to
// another. Can be invoked by account owner or by Alphabet nodes. // another. It can be invoked by the account owner or by Alphabet nodes.
// //
// Produces Transfer and TransferX notifications. // It produces Transfer and TransferX notifications.
// //
// TransferX method expands Transfer method by having extra details argument. // TransferX method expands Transfer method by having extra details argument.
// Also TransferX method allows to transfer assets by Alphabet nodes of the // TransferX method also allows to transfer assets by Alphabet nodes of the
// Inner Ring with multi signature. // Inner Ring with multisignature.
func TransferX(from, to interop.Hash160, amount int, details []byte) { func TransferX(from, to interop.Hash160, amount int, details []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
alphabet []common.IRNode
nodeKey []byte
indirectCall bool
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked from inner ring")
}
indirectCall = common.FromKnownContract(
ctx,
runtime.GetCallingScriptHash(),
containerContractKey,
)
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
if notaryDisabled && !indirectCall {
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{from, to, amount}, []byte("transfer"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
result := token.transfer(ctx, from, to, amount, true, details) result := token.transfer(ctx, from, to, amount, true, details)
if !result { if !result {
@ -190,33 +154,18 @@ func TransferX(from, to interop.Hash160, amount int, details []byte) {
runtime.Log("successfully transferred assets") runtime.Log("successfully transferred assets")
} }
// Lock is a method that transfers assets from user account to lock account // Lock is a method that transfers assets from a user account to the lock account
// related to the user. Can be invoked only by Alphabet nodes of the Inner Ring. // related to the user. It can be invoked only by Alphabet nodes of the Inner Ring.
// //
// Produces Lock, Transfer and TransferX notifications. // It produces Lock, Transfer and TransferX notifications.
// //
// Lock method invoked by Alphabet nodes of the Inner Ring when they process // Lock method is invoked by Alphabet nodes of the Inner Ring when they process
// Withdraw notification from NeoFS contract. This should transfer assets // Withdraw notification from FrostFS contract. This should transfer assets
// to new lock account that won't be used for anything besides Unlock and Burn. // to a new lock account that won't be used for anything beside Unlock and Burn.
func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) { func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked from inner ring")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
details := common.LockTransferDetails(txDetails) details := common.LockTransferDetails(txDetails)
@ -226,19 +175,7 @@ func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
Parent: from, Parent: from,
} }
if notaryDisabled { setAccount(ctx, to, lockAccount)
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{txDetails}, []byte("lock"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
common.SetSerialized(ctx, to, lockAccount)
result := token.transfer(ctx, from, to, amount, true, details) result := token.transfer(ctx, from, to, amount, true, details)
if !result { if !result {
@ -250,28 +187,15 @@ func Lock(txDetails []byte, from, to interop.Hash160, amount, until int) {
runtime.Notify("Lock", txDetails, from, to, amount, until) runtime.Notify("Lock", txDetails, from, to, amount, until)
} }
// NewEpoch is a method that checks timeout on lock accounts and return assets // NewEpoch is a method that checks timeout on lock accounts and returns assets
// if lock is not available anymore. Can be invoked only by NewEpoch method // if lock is not available anymore. It can be invoked only by NewEpoch method
// of Netmap contract. // of Netmap contract.
// //
// Produces Transfer and TransferX notifications. // It produces Transfer and TransferX notifications.
func NewEpoch(epochNum int) { func NewEpoch(epochNum int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
if notaryDisabled { common.CheckAlphabetWitness()
indirectCall := common.FromKnownContract(
ctx,
runtime.GetCallingScriptHash(),
netmapContractKey,
)
if !indirectCall {
panic("this method must be invoked from inner ring")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
it := storage.Find(ctx, []byte{}, storage.KeysOnly) it := storage.Find(ctx, []byte{}, storage.KeysOnly)
for iterator.Next(it) { for iterator.Next(it) {
@ -293,49 +217,22 @@ func NewEpoch(epochNum int) {
} }
} }
// Mint is a method that transfers assets to user account from empty account. // Mint is a method that transfers assets to a user account from an empty account.
// Can be invoked only by Alphabet nodes of the Inner Ring. // It can be invoked only by Alphabet nodes of the Inner Ring.
// //
// Produces Mint, Transfer and TransferX notifications. // It produces Mint, Transfer and TransferX notifications.
// //
// Mint method invoked by Alphabet nodes of the Inner Ring when they process // Mint method is invoked by Alphabet nodes of the Inner Ring when they process
// Deposit notification from NeoFS contract. Before that Alphabet nodes should // Deposit notification from FrostFS contract. Before that, Alphabet nodes should
// synchronize precision of main chain GAS contract and Balance contract. // synchronize precision of mainchain GAS contract and Balance contract.
// Mint increases total supply of NEP-17 compatible NeoFS token. // Mint increases total supply of NEP-17 compatible FrostFS token.
func Mint(to interop.Hash160, amount int, txDetails []byte) { func Mint(to interop.Hash160, amount int, txDetails []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked from inner ring")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
details := common.MintTransferDetails(txDetails) details := common.MintTransferDetails(txDetails)
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{txDetails}, []byte("mint"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
ok := token.transfer(ctx, nil, to, amount, true, details) ok := token.transfer(ctx, nil, to, amount, true, details)
if !ok { if !ok {
panic("can't transfer assets") panic("can't transfer assets")
@ -348,51 +245,24 @@ func Mint(to interop.Hash160, amount int, txDetails []byte) {
runtime.Notify("Mint", to, amount) runtime.Notify("Mint", to, amount)
} }
// Burn is a method that transfers assets from user account to empty account. // Burn is a method that transfers assets from a user account to an empty account.
// Can be invoked only by Alphabet nodes of the Inner Ring. // It can be invoked only by Alphabet nodes of the Inner Ring.
// //
// Produces Burn, Transfer and TransferX notifications. // It produces Burn, Transfer and TransferX notifications.
// //
// Burn method invoked by Alphabet nodes of the Inner Ring when they process // Burn method is invoked by Alphabet nodes of the Inner Ring when they process
// Cheque notification from NeoFS contract. It means that locked assets were // Cheque notification from FrostFS contract. It means that locked assets have been
// transferred to user in main chain, therefore lock account should be destroyed. // transferred to the user in the mainchain, therefore the lock account should be destroyed.
// Before that Alphabet nodes should synchronize precision of main chain GAS // Before that, Alphabet nodes should synchronize precision of mainchain GAS
// contract and Balance contract. Burn decreases total supply of NEP-17 // contract and Balance contract. Burn decreases total supply of NEP-17
// compatible NeoFS token. // compatible FrostFS token.
func Burn(from interop.Hash160, amount int, txDetails []byte) { func Burn(from interop.Hash160, amount int, txDetails []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked from inner ring")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
details := common.BurnTransferDetails(txDetails) details := common.BurnTransferDetails(txDetails)
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{txDetails}, []byte("burn"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
ok := token.transfer(ctx, from, nil, amount, true, details) ok := token.transfer(ctx, from, nil, amount, true, details)
if !ok { if !ok {
panic("can't transfer assets") panic("can't transfer assets")
@ -409,7 +279,7 @@ func Burn(from interop.Hash160, amount int, txDetails []byte) {
runtime.Notify("Burn", from, amount) runtime.Notify("Burn", from, amount)
} }
// Version returns version of the contract. // Version returns the version of the contract.
func Version() int { func Version() int {
return common.Version return common.Version
} }
@ -442,14 +312,14 @@ func (t Token) transfer(ctx storage.Context, from, to interop.Hash160, amount in
storage.Delete(ctx, from) storage.Delete(ctx, from)
} else { } else {
amountFrom.Balance = amountFrom.Balance - amount // neo-go#953 amountFrom.Balance = amountFrom.Balance - amount // neo-go#953
common.SetSerialized(ctx, from, amountFrom) setAccount(ctx, from, amountFrom)
} }
} }
if len(to) == 20 { if len(to) == 20 {
amountTo := getAccount(ctx, to) amountTo := getAccount(ctx, to)
amountTo.Balance = amountTo.Balance + amount // neo-go#953 amountTo.Balance = amountTo.Balance + amount // neo-go#953
common.SetSerialized(ctx, to, amountTo) setAccount(ctx, to, amountTo)
} }
runtime.Notify("Transfer", from, to, amount) runtime.Notify("Transfer", from, to, amount)
@ -460,9 +330,7 @@ func (t Token) transfer(ctx storage.Context, from, to interop.Hash160, amount in
// canTransfer returns the amount it can transfer. // canTransfer returns the amount it can transfer.
func (t Token) canTransfer(ctx storage.Context, from, to interop.Hash160, amount int, innerRing bool) (Account, bool) { func (t Token) canTransfer(ctx storage.Context, from, to interop.Hash160, amount int, innerRing bool) (Account, bool) {
var ( emptyAcc := Account{}
emptyAcc = Account{}
)
if !innerRing { if !innerRing {
if len(to) != interop.Hash160Len || !isUsableAddress(from) { if len(to) != interop.Hash160Len || !isUsableAddress(from) {
@ -483,7 +351,7 @@ func (t Token) canTransfer(ctx storage.Context, from, to interop.Hash160, amount
return amountFrom, true return amountFrom, true
} }
// isUsableAddress checks if the sender is either the correct NEO address or SC address. // isUsableAddress checks if the sender is either a correct NEO address or SC address.
func isUsableAddress(addr interop.Hash160) bool { func isUsableAddress(addr interop.Hash160) bool {
if len(addr) == 20 { if len(addr) == 20 {
if runtime.CheckWitness(addr) { if runtime.CheckWitness(addr) {
@ -500,11 +368,24 @@ func isUsableAddress(addr interop.Hash160) bool {
return false return false
} }
func getAccount(ctx storage.Context, key interface{}) Account { func getAccount(ctx storage.Context, key any) Account {
data := storage.Get(ctx, key) data := storage.Get(ctx, key)
if data != nil { if data != nil {
return std.Deserialize(data.([]byte)).(Account) acc := std.Deserialize(data.([]byte)).(account)
return Account{
Balance: common.FromFixedWidth64(acc.Balance),
Until: common.FromFixedWidth64(acc.Until),
Parent: acc.Parent,
}
} }
return Account{} return Account{}
} }
func setAccount(ctx storage.Context, key any, acc Account) {
common.SetSerialized(ctx, key, account{
Balance: common.ToFixedWidth64(acc.Balance),
Until: common.ToFixedWidth64(acc.Until),
Parent: acc.Parent,
})
}

View file

@ -1,6 +1,11 @@
name: "NeoFS Balance" name: "Balance"
supportedstandards: ["NEP-17"] supportedstandards: ["NEP-17"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "version"] safemethods:
- "balanceOf"
- "decimals"
- "symbol"
- "totalSupply"
- "version"
permissions: permissions:
- methods: ["update"] - methods: ["update"]
events: events:

View file

@ -1,79 +1,86 @@
/* /*
Balance contract is a contract deployed in NeoFS side chain. Balance contract is a contract deployed in FrostFS sidechain.
Balance contract stores all NeoFS account balances. It is NEP-17 compatible Balance contract stores all FrostFS account balances. It is a NEP-17 compatible
contract so in can be tracked and controlled by N3 compatible network contract, so it can be tracked and controlled by N3 compatible network
monitors and wallet software. monitors and wallet software.
This contract is used to store all micro transactions in the sidechain, such as This contract is used to store all micro transactions in the sidechain, such as
data audit settlements or container fee payments. It is inefficient to make such data audit settlements or container fee payments. It is inefficient to make such
small payment transactions in main chain. To process small transfers, balance small payment transactions in the mainchain. To process small transfers, balance
contract has higher (12) decimal precision than native GAS contract. contract has higher (12) decimal precision than native GAS contract.
NeoFS balances are synchronized with main chain operations. Deposit produce FrostFS balances are synchronized with mainchain operations. Deposit produces
minting of NEOFS tokens in Balance contract. Withdraw locks some NEOFS tokens minting of FROSTFS tokens in Balance contract. Withdraw locks some FROSTFS tokens
in special lock account. When NeoFS contract transfers GAS assets back to the in a special lock account. When FrostFS contract transfers GAS assets back to the
user, lock account is destroyed with burn operation. user, the lock account is destroyed with burn operation.
Contract notifications # Contract notifications
Transfer notification. This is NEP-17 standard notification. Transfer notification. This is a NEP-17 standard notification.
Transfer: Transfer:
- name: from - name: from
type: Hash160 type: Hash160
- name: to - name: to
type: Hash160 type: Hash160
- name: amount - name: amount
type: Integer type: Integer
TransferX notification. This is enhanced transfer notification with details. TransferX notification. This is an enhanced transfer notification with details.
TransferX: TransferX:
- name: from - name: from
type: Hash160 type: Hash160
- name: to - name: to
type: Hash160 type: Hash160
- name: amount - name: amount
type: Integer type: Integer
- name: details - name: details
type: ByteArray type: ByteArray
Lock notification. This notification is produced when Lock account has been Lock notification. This notification is produced when a lock account is
created. It contains information about main chain transaction that produced created. It contains information about the mainchain transaction that has produced
asset lock, address of lock account and NeoFS epoch number until lock account the asset lock, the address of the lock account and the FrostFS epoch number until which the
is valid. Alphabet nodes of the Inner Ring catch notification and initialize lock account is valid. Alphabet nodes of the Inner Ring catch notification and initialize
Cheque method invocation of the NeoFS contract. Cheque method invocation of FrostFS contract.
Lock: Lock:
- name: txID - name: txID
type: ByteArray type: ByteArray
- name: from - name: from
type: Hash160 type: Hash160
- name: to - name: to
type: Hash160 type: Hash160
- name: amount - name: amount
type: Integer type: Integer
- name: until - name: until
type: Integer type: Integer
Mint notification. This notification is produced when user balance is Mint notification. This notification is produced when user balance is
replenished from deposit in the main chain. replenished from deposit in the mainchain.
Mint:
- name: to
type: Hash160
- name: amount
type: Integer
Mint:
- name: to
type: Hash160
- name: amount
type: Integer
Burn notification. This notification is produced after user balance is reduced Burn notification. This notification is produced after user balance is reduced
when NeoFS contract transferred GAS assets back to the user. when FrostFS contract has transferred GAS assets back to the user.
Burn: Burn:
- name: from - name: from
type: Hash160 type: Hash160
- name: amount - name: amount
type: Integer type: Integer
# Contract storage scheme
| Key | Value | Description |
|-----------------------|------------|----------------------------------|
| `netmapScriptHash` | Hash160 | netmap contract hash |
| `containerScriptHash` | Hash160 | container contract hash |
| circulationKey | int | the token circulation key value |
*/ */
package balance package balance

11
common/common.go Normal file
View file

@ -0,0 +1,11 @@
package common
import (
"github.com/nspcc-dev/neo-go/pkg/interop/util"
)
// BytesEqual compares two slices of bytes by wrapping them into strings,
// which is necessary with new util.Equals interop behaviour, see neo-go#1176.
func BytesEqual(a []byte, b []byte) bool {
return util.Equals(string(a), string(b))
}

View file

@ -6,64 +6,39 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo" "github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles" "github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
) )
type IRNode struct { type IRNode struct {
PublicKey interop.PublicKey PublicKey interop.PublicKey
} }
const irListMethod = "innerRingList" // InnerRingNodes return a list of inner ring nodes from state validator role
// in the sidechain.
// InnerRingInvoker returns public key of inner ring node that invoked contract. func InnerRingNodes() []interop.PublicKey {
// Work around for environments without notary support.
func InnerRingInvoker(ir []IRNode) interop.PublicKey {
for i := 0; i < len(ir); i++ {
node := ir[i]
if runtime.CheckWitness(node.PublicKey) {
return node.PublicKey
}
}
return nil
}
// InnerRingNodes return list of inner ring nodes from state validator role
// in side chain.
func InnerRingNodes() []IRNode {
blockHeight := ledger.CurrentIndex() blockHeight := ledger.CurrentIndex()
list := roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1)) return roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1))
return keysToNodes(list)
} }
// InnerRingNodesFromNetmap gets list of inner ring through // AlphabetNodes returns a list of alphabet nodes from committee in the sidechain.
// calling "innerRingList" method of smart contract. func AlphabetNodes() []interop.PublicKey {
// Work around for environments without notary support. return neo.GetCommittee()
func InnerRingNodesFromNetmap(sc interop.Hash160) []IRNode {
return contract.Call(sc, irListMethod, contract.ReadOnly).([]IRNode)
}
// AlphabetNodes return list of alphabet nodes from committee in side chain.
func AlphabetNodes() []IRNode {
list := neo.GetCommittee()
return keysToNodes(list)
} }
// AlphabetAddress returns multi address of alphabet public keys. // AlphabetAddress returns multi address of alphabet public keys.
func AlphabetAddress() []byte { func AlphabetAddress() interop.Hash160 {
alphabet := neo.GetCommittee() alphabet := neo.GetCommittee()
return Multiaddress(alphabet, false) return Multiaddress(alphabet, false)
} }
// CommitteeAddress returns multi address of committee. // CommitteeAddress returns multi address of committee.
func CommitteeAddress() []byte { func CommitteeAddress() interop.Hash160 {
committee := neo.GetCommittee() committee := neo.GetCommittee()
return Multiaddress(committee, true) return Multiaddress(committee, true)
} }
// Multiaddress returns default multi signature account address for N keys. // Multiaddress returns default multisignature account address for N keys.
// If committee set to true, then it is `M = N/2+1` committee account. // If committee set to true, it is `M = N/2+1` committee account.
func Multiaddress(n []interop.PublicKey, committee bool) []byte { func Multiaddress(n []interop.PublicKey, committee bool) interop.Hash160 {
threshold := len(n)*2/3 + 1 threshold := len(n)*2/3 + 1
if committee { if committee {
threshold = len(n)/2 + 1 threshold = len(n)/2 + 1
@ -71,15 +46,3 @@ func Multiaddress(n []interop.PublicKey, committee bool) []byte {
return contract.CreateMultisigAccount(threshold, n) return contract.CreateMultisigAccount(threshold, n)
} }
func keysToNodes(list []interop.PublicKey) []IRNode {
result := []IRNode{}
for i := range list {
result = append(result, IRNode{
PublicKey: list[i],
})
}
return result
}

View file

@ -1,21 +1,28 @@
package common package common
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop/convert"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
) )
func GetList(ctx storage.Context, key interface{}) [][]byte {
data := storage.Get(ctx, key)
if data != nil {
return std.Deserialize(data.([]byte)).([][]byte)
}
return [][]byte{}
}
// SetSerialized serializes data and puts it into contract storage. // SetSerialized serializes data and puts it into contract storage.
func SetSerialized(ctx storage.Context, key interface{}, value interface{}) { func SetSerialized(ctx storage.Context, key any, value interface{}) {
data := std.Serialize(value) data := std.Serialize(value)
storage.Put(ctx, key, data) storage.Put(ctx, key, data)
} }
// ToFixedWidth64 converts x to bytes such that numbers <= math.MaxUint64
// have constant with of 9.
func ToFixedWidth64(x int) []byte {
data := convert.ToBytes(x)
if x < 0 || len(data) >= 9 {
return data
}
return append(data, make([]byte, 9-len(data))...)
}
// FromFixedWidth64 is a reverse function for ToFixedWidth64.
func FromFixedWidth64(x []byte) int {
return convert.ToInteger(x)
}

View file

@ -31,7 +31,7 @@ func LockTransferDetails(txDetails []byte) []byte {
} }
func UnlockTransferDetails(epoch int) []byte { func UnlockTransferDetails(epoch int) []byte {
var buf interface{} = epoch var buf any = epoch
return append(unlockPrefix, buf.([]byte)...) return append(unlockPrefix, buf.([]byte)...)
} }
@ -39,7 +39,7 @@ func ContainerFeeTransferDetails(cid []byte) []byte {
return append(containerFeePrefix, cid...) return append(containerFeePrefix, cid...)
} }
// AbortWithMessage calls `runtime.Log` with passed message // AbortWithMessage calls `runtime.Log` with the passed message
// and calls `ABORT` opcode. // and calls `ABORT` opcode.
func AbortWithMessage(msg string) { func AbortWithMessage(msg string) {
runtime.Log(msg) runtime.Log(msg)

View file

@ -4,6 +4,9 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
) )
// LegacyOwnerKey is storage key used to store contract owner.
const LegacyOwnerKey = "contractOwner"
// HasUpdateAccess returns true if contract can be updated. // HasUpdateAccess returns true if contract can be updated.
func HasUpdateAccess() bool { func HasUpdateAccess() bool {
return runtime.CheckWitness(CommitteeAddress()) return runtime.CheckWitness(CommitteeAddress())

View file

@ -4,15 +4,15 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
const ( const (
major = 0 major = 0
minor = 13 minor = 20
patch = 2 patch = 0
// Versions from which an update should be performed. // Versions from which an update should be performed.
// These should be used in a group (so prevMinor can be equal to minor if there are // These should be used in a group (so prevMinor can be equal to minor if there are
// any migration routines. // any migration routines.
prevMajor = 0 prevMajor = 0
prevMinor = 12 prevMinor = 19
prevPatch = 2 prevPatch = 3
Version = major*1_000_000 + minor*1_000 + patch Version = major*1_000_000 + minor*1_000 + patch
@ -32,15 +32,15 @@ func CheckVersion(from int) {
if from < PrevVersion { if from < PrevVersion {
panic(ErrVersionMismatch + ": expected >=" + std.Itoa(PrevVersion, 10)) panic(ErrVersionMismatch + ": expected >=" + std.Itoa(PrevVersion, 10))
} }
if from == Version { if Version <= from {
panic(ErrAlreadyUpdated + ": " + std.Itoa(Version, 10)) panic(ErrAlreadyUpdated + ": " + std.Itoa(Version, 10))
} }
} }
// AppendVersion appends current contract version to the list of deploy arguments. // AppendVersion appends current contract version to the list of deploy arguments.
func AppendVersion(data interface{}) []interface{} { func AppendVersion(data any) []interface{} {
if data == nil { if data == nil {
return []interface{}{Version} return []any{Version}
} }
return append(data.([]interface{}), Version) return append(data.([]any), Version)
} }

View file

@ -1,147 +0,0 @@
package common
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
)
type Ballot struct {
// ID of the voting decision.
ID []byte
// Public keys of already voted inner ring nodes.
Voters []interop.PublicKey
// Height of block with the last vote.
Height int
}
const voteKey = "ballots"
const blockDiff = 20 // change base on performance evaluation
func InitVote(ctx storage.Context) {
SetSerialized(ctx, voteKey, []Ballot{})
}
// Vote adds ballot for the decision with specific 'id' and returns amount
// on unique voters for that decision.
func Vote(ctx storage.Context, id, from []byte) int {
var (
newCandidates []Ballot
candidates = getBallots(ctx)
found = -1
blockHeight = ledger.CurrentIndex()
)
for i := 0; i < len(candidates); i++ {
cnd := candidates[i]
if blockHeight-cnd.Height > blockDiff {
continue
}
if BytesEqual(cnd.ID, id) {
voters := cnd.Voters
for j := range voters {
if BytesEqual(voters[j], from) {
return len(voters)
}
}
voters = append(voters, from)
cnd = Ballot{ID: id, Voters: voters, Height: blockHeight}
found = len(voters)
}
newCandidates = append(newCandidates, cnd)
}
if found < 0 {
voters := []interop.PublicKey{from}
newCandidates = append(newCandidates, Ballot{
ID: id,
Voters: voters,
Height: blockHeight})
found = 1
}
SetSerialized(ctx, voteKey, newCandidates)
return found
}
// RemoveVotes clears ballots of the decision that has been accepted by
// inner ring nodes.
func RemoveVotes(ctx storage.Context, id []byte) {
var (
newCandidates []Ballot
candidates = getBallots(ctx)
)
for i := 0; i < len(candidates); i++ {
cnd := candidates[i]
if !BytesEqual(cnd.ID, id) {
newCandidates = append(newCandidates, cnd)
}
}
SetSerialized(ctx, voteKey, newCandidates)
}
// getBallots returns deserialized slice of vote ballots.
func getBallots(ctx storage.Context) []Ballot {
data := storage.Get(ctx, voteKey)
if data != nil {
return std.Deserialize(data.([]byte)).([]Ballot)
}
return []Ballot{}
}
// BytesEqual compares two slice of bytes by wrapping them into strings,
// which is necessary with new util.Equal interop behaviour, see neo-go#1176.
func BytesEqual(a []byte, b []byte) bool {
return util.Equals(string(a), string(b))
}
// InvokeID returns hashed value of prefix and args concatenation. Used to
// identify different ballots.
func InvokeID(args []interface{}, prefix []byte) []byte {
for i := range args {
arg := args[i].([]byte)
prefix = append(prefix, arg...)
}
return crypto.Sha256(prefix)
}
/*
Check if invocation made from known container or audit contracts.
This is necessary because calls from these contracts require to do transfer
without signature collection (1 invoke transfer).
IR1, IR2, IR3, IR4 -(4 invokes)-> [ Container Contract ] -(1 invoke)-> [ Balance Contract ]
We can do 1 invoke transfer if:
- invoke happened from inner ring node,
- it is indirect invocation from other smart-contract.
However there is a possible attack, when malicious inner ring node creates
malicious smart-contract in morph chain to do indirect call.
MaliciousIR -(1 invoke)-> [ Malicious Contract ] -(1 invoke) -> [ Balance Contract ]
To prevent that, we have to allow 1 invoke transfer from authorised well known
smart-contracts, that will be set up at `Init` method.
*/
func FromKnownContract(ctx storage.Context, caller interop.Hash160, key string) bool {
addr := storage.Get(ctx, key).(interop.Hash160)
return BytesEqual(caller, addr)
}

View file

@ -3,31 +3,31 @@ package common
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
var ( var (
// ErrAlphabetWitnessFailed appears when method must be // ErrAlphabetWitnessFailed appears when the method must be
// called by Alphabet but was not. // called by the Alphabet but was not.
ErrAlphabetWitnessFailed = "alphabet witness check failed" ErrAlphabetWitnessFailed = "alphabet witness check failed"
// ErrOwnerWitnessFailed appears when method must be called // ErrOwnerWitnessFailed appears when the method must be called
// by owner of some assets but was not. // by an owner of some assets but was not.
ErrOwnerWitnessFailed = "owner witness check failed" ErrOwnerWitnessFailed = "owner witness check failed"
// ErrWitnessFailed appears when method must be called // ErrWitnessFailed appears when the method must be called
// using certain public key but was not. // using certain public key but was not.
ErrWitnessFailed = "witness check failed" ErrWitnessFailed = "witness check failed"
) )
// CheckAlphabetWitness checks witness of the passed caller. // CheckAlphabetWitness checks witness of the passed caller.
// Panics with ErrAlphabetWitnessFailed message on fail. // It panics with ErrAlphabetWitnessFailed message on fail.
func CheckAlphabetWitness(caller []byte) { func CheckAlphabetWitness() {
checkWitnessWithPanic(caller, ErrAlphabetWitnessFailed) checkWitnessWithPanic(AlphabetAddress(), ErrAlphabetWitnessFailed)
} }
// CheckOwnerWitness checks witness of the passed caller. // CheckOwnerWitness checks witness of the passed caller.
// Panics with ErrOwnerWitnessFailed message on fail. // It panics with ErrOwnerWitnessFailed message on fail.
func CheckOwnerWitness(caller []byte) { func CheckOwnerWitness(caller []byte) {
checkWitnessWithPanic(caller, ErrOwnerWitnessFailed) checkWitnessWithPanic(caller, ErrOwnerWitnessFailed)
} }
// CheckWitness checks witness of the passed caller. // CheckWitness checks witness of the passed caller.
// Panics with ErrWitnessFailed message on fail. // It panics with ErrWitnessFailed message on fail.
func CheckWitness(caller []byte) { func CheckWitness(caller []byte) {
checkWitnessWithPanic(caller, ErrWitnessFailed) checkWitnessWithPanic(caller, ErrWitnessFailed)
} }

21
commonclient/invoker.go Normal file
View file

@ -0,0 +1,21 @@
package commonclient
import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Invoker is a subset of methods provided by struct invoker.Invoker. The subset contains only those
// methods that are used by ActorWrapper and clients of the contracts.
type Invoker interface {
Run([]byte) (*result.Invoke, error)
Call(contract util.Uint160, method string, params ...any) (*result.Invoke, error)
TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error)
TerminateSession(sessionID uuid.UUID) error
}
// Ensure the interface is compatible with the invoker.Invoker struct.
var _ Invoker = (*invoker.Invoker)(nil)

49
commonclient/iterator.go Normal file
View file

@ -0,0 +1,49 @@
package commonclient
import (
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// ReadIteratorItems calls method that returns iterator and traverses the iterator until all items are read into array.
func ReadIteratorItems(inv Invoker, batchSize int, contract util.Uint160, method string, params ...any) ([]stackitem.Item, error) {
if batchSize <= 0 {
panic("batch size must be positive")
}
script, err := smartcontract.CreateCallAndPrefetchIteratorScript(contract, method, batchSize, params...)
if err != nil {
return nil, fmt.Errorf("couldn't create unwrap script: %w", err)
}
arr, sessionID, iter, err := unwrap.ArrayAndSessionIterator(inv.Run(script))
if err != nil {
return nil, fmt.Errorf("unwrap session iterator: %w", err)
}
if (sessionID == uuid.UUID{}) {
return arr, nil
}
defer func() {
_ = inv.TerminateSession(sessionID)
}()
var shouldStop bool
res := arr
for !shouldStop {
items, err := inv.TraverseIterator(sessionID, &iter, batchSize)
if err != nil {
return nil, err
}
res = append(res, items...)
shouldStop = len(items) < batchSize
}
return res, nil
}

View file

@ -0,0 +1,63 @@
package commonclient
import (
"errors"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io"
"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"
)
// Transaction allows to invoke several contract method at once.
type Transaction struct {
writer *io.BufBinWriter
buffer *io.BufBinWriter
contract util.Uint160
}
var ErrTransactionTooLarge = errors.New("transaction/script size limit exceeded")
// NewTransaction creates new transaction to accumulate contract invocations.
func NewTransaction(contractHash util.Uint160) *Transaction {
return &Transaction{
writer: io.NewBufBinWriter(),
buffer: io.NewBufBinWriter(),
contract: contractHash,
}
}
// WrapCall accept methods and arguments to invoke.
// Should be used with method on clients like Client.MethodNameCall.
func (t Transaction) WrapCall(method string, args []any) error {
t.buffer.Reset()
emit.AppCall(t.buffer.BinWriter, t.contract, method, callflag.All, args...)
if t.writer.Len()+t.buffer.Len() > transaction.MaxScriptLength {
return ErrTransactionTooLarge
}
t.writer.WriteBytes(t.buffer.Bytes())
return t.writer.Err
}
// WrapCallErr accept methods, arguments and error to handle and invoke.
// Should be used with method on clients like *CallErr.
func (t Transaction) WrapCallErr(method string, args []any, err error) error {
if err != nil {
return err
}
return t.WrapCall(method, args)
}
// Bytes returns the resulting buffer and makes future writes return an error.
func (t Transaction) Bytes() ([]byte, error) {
if t.writer.Len() > transaction.MaxScriptLength {
return nil, ErrTransactionTooLarge
}
return t.writer.Bytes(), nil
}

84
commonclient/waiter.go Normal file
View file

@ -0,0 +1,84 @@
package commonclient
import (
"context"
"fmt"
"strings"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
)
const alreadyExistsError = "already exists"
type WaiterOptions struct {
// IgnoreAlreadyExistsError controls behavior for "already exists" error:
// - If set to true, it indicates that "already exists" error is not a problem, we should
// wait for transaction as usual (this is the behavior of neo-go [waiter.PollingBased]).
// - If set to false, it indicates that "already exists" should be reported as an error.
IgnoreAlreadyExistsError bool
// VerifyExecResults controls whether waiter should ensure that transaction successfully
// enters blockchain block.
VerifyExecResults bool
}
// Waiter is a decorator on top of the standard [waiter.Waiter].
// It provides additional behavior (controlled by [WaiterOptions]) on top of the standard
// functionality of awaiting transactions.
type Waiter struct {
waiter waiter.Waiter
options WaiterOptions
}
var _ waiter.Waiter = (*Waiter)(nil)
// NewWaiter decorates the specified waiter in a new [Waiter] instance.
func NewWaiter(waiter waiter.Waiter, options WaiterOptions) *Waiter {
return &Waiter{
waiter: waiter,
options: options,
}
}
// Wait allows to wait until transaction will be accepted to the chain.
func (w *Waiter) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
if !w.options.IgnoreAlreadyExistsError && errIsAlreadyExists(err) {
return nil, err
}
result, err := w.waiter.Wait(h, vub, err)
return w.examineExecResult(result, err)
}
// WaitAny waits until at least one of the specified transactions will be accepted
// to the chain.
func (w *Waiter) WaitAny(ctx context.Context, vub uint32, hashes ...util.Uint256) (*state.AppExecResult, error) {
result, err := w.waiter.WaitAny(ctx, vub, hashes...)
return w.examineExecResult(result, err)
}
func (w *Waiter) examineExecResult(result *state.AppExecResult, err error) (*state.AppExecResult, error) {
if !w.options.VerifyExecResults || err != nil {
return result, err
}
if result.Execution.VMState != vmstate.Fault {
// Transaction didn't fail, so we just return result "as is"
return result, nil
}
// Transaction failed, we extract VM exception from it and report as an error
if result.FaultException != "" {
return result, fmt.Errorf("%s", result.FaultException)
}
return result, fmt.Errorf("transaction failed, stack=%v", result.Stack)
}
func errIsAlreadyExists(err error) bool {
if err == nil {
return false
}
return strings.Contains(strings.ToLower(err.Error()), alreadyExistsError)
}

135
commonclient/waiter_test.go Normal file
View file

@ -0,0 +1,135 @@
package commonclient
import (
"context"
"fmt"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
type mockWaiter struct {
mock.Mock
}
func (w *mockWaiter) successfulResult(txHash util.Uint256) *state.AppExecResult {
return &state.AppExecResult{
Container: txHash,
Execution: state.Execution{
Trigger: trigger.Application,
VMState: vmstate.Halt,
GasConsumed: 100500,
Stack: nil,
Events: nil,
FaultException: "",
},
}
}
func (w *mockWaiter) failedResult(txHash util.Uint256, exception string) *state.AppExecResult {
return &state.AppExecResult{
Container: txHash,
Execution: state.Execution{
Trigger: trigger.Application,
VMState: vmstate.Fault,
GasConsumed: 100500,
Stack: nil,
Events: nil,
FaultException: exception,
},
}
}
func (m *mockWaiter) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
args := m.Called(h, vub, err)
result := args.Get(0)
if result == nil {
return nil, args.Error(1)
}
return result.(*state.AppExecResult), args.Error(1)
}
func (m *mockWaiter) WaitAny(ctx context.Context, vub uint32, hashes ...util.Uint256) (*state.AppExecResult, error) {
args := m.Called(ctx, vub, hashes)
result := args.Get(0)
if result == nil {
return nil, args.Error(1)
}
return result.(*state.AppExecResult), args.Error(1)
}
func TestWaiter(t *testing.T) {
txHash := util.Uint256{}
vub := uint32(100)
t.Run("ignore already exists error", func(t *testing.T) {
sendErr := fmt.Errorf("transaction already exists")
mw := &mockWaiter{}
mw.On("Wait", txHash, vub, mock.Anything).Return(mw.successfulResult(txHash), nil)
waiter := NewWaiter(mw, WaiterOptions{IgnoreAlreadyExistsError: true})
_, err := waiter.Wait(txHash, vub, sendErr)
require.NoError(t, err)
})
t.Run("report already exists error", func(t *testing.T) {
sendErr := fmt.Errorf("transaction already exists")
mw := &mockWaiter{}
mw.On("Wait", txHash, vub, mock.Anything).Return(mw.successfulResult(txHash), nil)
waiter := NewWaiter(mw, WaiterOptions{IgnoreAlreadyExistsError: false})
_, err := waiter.Wait(txHash, vub, sendErr)
require.Error(t, err)
})
t.Run("report wait error when transaction error is ignored", func(t *testing.T) {
waitErr := fmt.Errorf("mock error")
mw := &mockWaiter{}
mw.On("Wait", txHash, vub, nil).Return(nil, waitErr)
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: false})
_, err := waiter.Wait(txHash, vub, nil)
require.ErrorIs(t, err, waitErr)
})
t.Run("report wait error when transaction error is verified", func(t *testing.T) {
waitErr := fmt.Errorf("mock error")
mw := &mockWaiter{}
mw.On("Wait", txHash, vub, nil).Return(nil, waitErr)
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: true})
_, err := waiter.Wait(txHash, vub, nil)
require.ErrorIs(t, err, waitErr)
})
t.Run("ignore error from transaction", func(t *testing.T) {
txError := "mock error"
mw := &mockWaiter{}
mw.On("Wait", txHash, vub, nil).Return(mw.failedResult(txHash, txError), nil)
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: false})
_, err := waiter.Wait(txHash, vub, nil)
require.NoError(t, err)
})
t.Run("examine error from transaction", func(t *testing.T) {
txError := "mock error"
mw := &mockWaiter{}
mw.On("Wait", txHash, vub, nil).Return(mw.failedResult(txHash, txError), nil)
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: true})
_, err := waiter.Wait(txHash, vub, nil)
require.ErrorContains(t, err, txError)
})
}

View file

@ -1,37 +1,41 @@
name: "NeoFS Container" name: "Container"
safemethods: ["get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "version"] safemethods:
- "count"
- "containersOf"
- "deletionInfo"
- "eACL"
- "get"
- "getContainerSize"
- "iterateContainerSizes"
- "list"
- "listContainerSizes"
- "owner"
- "version"
permissions: permissions:
- methods: ["update", "addKey", "transferX", - methods:
"addRoot", "register", "addRecord", "deleteRecords"] - "addRecord"
- "deleteRecords"
- "register"
- "transferX"
- "update"
events: events:
- name: containerPut - name: PutSuccess
parameters: parameters:
- name: container - name: containerID
type: ByteArray type: Hash256
- name: signature
type: Signature
- name: publicKey - name: publicKey
type: PublicKey type: PublicKey
- name: token - name: DeleteSuccess
type: ByteArray
- name: containerDelete
parameters: parameters:
- name: containerID - name: containerID
type: ByteArray type: ByteArray
- name: signature - name: SetEACLSuccess
type: Signature
- name: token
type: ByteArray
- name: setEACL
parameters: parameters:
- name: eACL - name: containerID
type: ByteArray type: ByteArray
- name: signature
type: Signature
- name: publicKey - name: publicKey
type: PublicKey type: PublicKey
- name: token
type: ByteArray
- name: StartEstimation - name: StartEstimation
parameters: parameters:
- name: epoch - name: epoch

View file

@ -1,15 +1,16 @@
package container package container
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/convert"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
) )
type ( type (
@ -43,13 +44,12 @@ type (
) )
const ( const (
neofsIDContractKey = "identityScriptHash" frostfsIDContractKey = "identityScriptHash"
balanceContractKey = "balanceScriptHash" balanceContractKey = "balanceScriptHash"
netmapContractKey = "netmapScriptHash" netmapContractKey = "netmapScriptHash"
nnsContractKey = "nnsScriptHash" nnsContractKey = "nnsScriptHash"
nnsRootKey = "nnsRoot" nnsRootKey = "nnsRoot"
nnsHasAliasKey = "nnsHasAlias" nnsHasAliasKey = "nnsHasAlias"
notaryDisabledKey = "notary"
// RegistrationFeeKey is a key in netmap config which contains fee for container registration. // RegistrationFeeKey is a key in netmap config which contains fee for container registration.
RegistrationFeeKey = "ContainerFee" RegistrationFeeKey = "ContainerFee"
@ -59,44 +59,52 @@ const (
// V2 format // V2 format
containerIDSize = 32 // SHA256 size containerIDSize = 32 // SHA256 size
estimateKeyPrefix = "cnr" singleEstimatePrefix = "est"
estimatePostfixSize = 10 estimateKeyPrefix = "cnr"
cleanupDelta = 3 containerKeyPrefix = 'x'
ownerKeyPrefix = 'o'
graveKeyPrefix = 'g'
estimatePostfixSize = 10
// CleanupDelta contains the number of the last epochs for which container estimations are present.
CleanupDelta = 3
// TotalCleanupDelta contains the number of the epochs after which estimation
// will be removed by epoch tick cleanup if any of the nodes hasn't updated
// container size and/or container has been removed. It must be greater than CleanupDelta.
TotalCleanupDelta = CleanupDelta + 1
// NotFoundError is returned if container is missing. // NotFoundError is returned if container is missing.
NotFoundError = "container does not exist" NotFoundError = "container does not exist"
// default SOA record field values // default SOA record field values
defaultRefresh = 3600 // 1 hour defaultRefresh = 3600 // 1 hour
defaultRetry = 600 // 10 min defaultRetry = 600 // 10 min
defaultExpire = 604800 // 1 week defaultExpire = 3600 * 24 * 365 * 10 // 10 years
defaultTTL = 3600 // 1 hour defaultTTL = 3600 // 1 hour
) )
var ( var eACLPrefix = []byte("eACL")
eACLPrefix = []byte("eACL")
)
// OnNEP11Payment is needed for registration with contract as owner to work. // OnNEP11Payment is needed for registration with contract as the owner to work.
func OnNEP11Payment(a interop.Hash160, b int, c []byte, d interface{}) { func OnNEP11Payment(a interop.Hash160, b int, c []byte, d any) {
} }
func _deploy(data interface{}, isUpdate bool) { func _deploy(data any, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
args := data.(struct {
notaryDisabled bool
addrNetmap interop.Hash160
addrBalance interop.Hash160
addrID interop.Hash160
addrNNS interop.Hash160
nnsRoot string
})
if isUpdate { if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return return
} }
args := data.(struct {
addrNetmap interop.Hash160
addrBalance interop.Hash160
addrID interop.Hash160
addrNNS interop.Hash160
nnsRoot string
})
if len(args.addrNetmap) != interop.Hash160Len || if len(args.addrNetmap) != interop.Hash160Len ||
len(args.addrBalance) != interop.Hash160Len || len(args.addrBalance) != interop.Hash160Len ||
len(args.addrID) != interop.Hash160Len { len(args.addrID) != interop.Hash160Len {
@ -105,17 +113,10 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, netmapContractKey, args.addrNetmap) storage.Put(ctx, netmapContractKey, args.addrNetmap)
storage.Put(ctx, balanceContractKey, args.addrBalance) storage.Put(ctx, balanceContractKey, args.addrBalance)
storage.Put(ctx, neofsIDContractKey, args.addrID) storage.Put(ctx, frostfsIDContractKey, args.addrID)
storage.Put(ctx, nnsContractKey, args.addrNNS) storage.Put(ctx, nnsContractKey, args.addrNNS)
storage.Put(ctx, nnsRootKey, args.nnsRoot) storage.Put(ctx, nnsRootKey, args.nnsRoot)
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
common.InitVote(ctx)
runtime.Log("container contract notary disabled")
}
// add NNS root for container alias domains // add NNS root for container alias domains
registerNiceNameTLD(args.addrNNS, args.nnsRoot) registerNiceNameTLD(args.addrNNS, args.nnsRoot)
@ -130,32 +131,31 @@ func registerNiceNameTLD(addrNNS interop.Hash160, nnsRoot string) {
} }
res := contract.Call(addrNNS, "register", contract.All, res := contract.Call(addrNNS, "register", contract.All,
nnsRoot, runtime.GetExecutingScriptHash(), "ops@nspcc.ru", nnsRoot, runtime.GetExecutingScriptHash(), "ops@frostfs.info",
defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool) defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool)
if !res { if !res {
panic("can't register NNS TLD") panic("can't register NNS TLD")
} }
} }
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. It can be invoked
// only by committee. // by committee only.
func Update(script []byte, manifest []byte, data interface{}) { func Update(script []byte, manifest []byte, data any) {
if !common.HasUpdateAccess() { if !common.HasUpdateAccess() {
panic("only committee can update contract") panic("only committee can update contract")
} }
contract.Call(interop.Hash160(management.Hash), "update", management.UpdateWithData(script, manifest, common.AppendVersion(data))
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("container contract updated") runtime.Log("container contract updated")
} }
// Put method creates new container if it was invoked by Alphabet nodes // Put method creates a new container if it has been invoked by Alphabet nodes
// of the Inner Ring. Otherwise it produces containerPut notification. // of the Inner Ring.
// //
// Container should be stable marshaled Container structure from API. // Container should be a stable marshaled Container structure from API.
// Signature is a RFC6979 signature of Container. // Signature is a RFC6979 signature of the Container.
// PublicKey contains public key of the signer. // PublicKey contains the public key of the signer.
// Token is optional and should be stable marshaled SessionToken structure from // Token is optional and should be a stable marshaled SessionToken structure from
// API. // API.
func Put(container []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) { func Put(container []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
PutNamed(container, signature, publicKey, token, "", "") PutNamed(container, signature, publicKey, token, "", "")
@ -165,13 +165,12 @@ func Put(container []byte, signature interop.Signature, publicKey interop.Public
// Note that zone must exist. // Note that zone must exist.
func PutNamed(container []byte, signature interop.Signature, func PutNamed(container []byte, signature interop.Signature,
publicKey interop.PublicKey, token []byte, publicKey interop.PublicKey, token []byte,
name, zone string) { name, zone string,
) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
ownerID := ownerFromBinaryContainer(container) ownerID := ownerFromBinaryContainer(container)
containerID := crypto.Sha256(container) containerID := crypto.Sha256(container)
neofsIDContractAddr := storage.Get(ctx, neofsIDContractKey).(interop.Hash160)
cnr := Container{ cnr := Container{
value: container, value: container,
sig: signature, sig: signature,
@ -208,33 +207,14 @@ func PutNamed(container []byte, signature interop.Signature,
panic("insufficient balance to create container") panic("insufficient balance to create container")
} }
if notaryDisabled { common.CheckAlphabetWitness()
nodeKey := common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
runtime.Notify("containerPut", container, signature, publicKey, token)
return
}
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{container, signature, publicKey}, []byte("put"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
// todo: check if new container with unique container id // todo: check if new container with unique container id
details := common.ContainerFeeTransferDetails(containerID) details := common.ContainerFeeTransferDetails(containerID)
for i := 0; i < len(alphabet); i++ { for i := 0; i < len(alphabet); i++ {
node := alphabet[i] node := alphabet[i]
to := contract.CreateStandardAccount(node.PublicKey) to := contract.CreateStandardAccount(node)
contract.Call(balanceContractAddr, "transferX", contract.Call(balanceContractAddr, "transferX",
contract.All, contract.All,
@ -250,7 +230,7 @@ func PutNamed(container []byte, signature interop.Signature,
if name != "" { if name != "" {
if needRegister { if needRegister {
res := contract.Call(nnsContractAddr, "register", contract.All, res := contract.Call(nnsContractAddr, "register", contract.All,
domain, runtime.GetExecutingScriptHash(), "ops@nspcc.ru", domain, runtime.GetExecutingScriptHash(), "ops@frostfs.info",
defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool) defaultRefresh, defaultRetry, defaultExpire, defaultTTL).(bool)
if !res { if !res {
panic("can't register the domain " + domain) panic("can't register the domain " + domain)
@ -263,14 +243,11 @@ func PutNamed(container []byte, signature interop.Signature,
storage.Put(ctx, key, domain) storage.Put(ctx, key, domain)
} }
if len(token) == 0 { // if container created directly without session
contract.Call(neofsIDContractAddr, "addKey", contract.All, ownerID, [][]byte{publicKey})
}
runtime.Log("added new container") runtime.Log("added new container")
runtime.Notify("PutSuccess", containerID, publicKey)
} }
// checkNiceNameAvailable checks if nice name is available for the container. // checkNiceNameAvailable checks if the nice name is available for the container.
// It panics if the name is taken. Returned value specifies if new domain registration is needed. // It panics if the name is taken. Returned value specifies if new domain registration is needed.
func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool { func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool {
isAvail := contract.Call(nnsContractAddr, "isAvailable", isAvail := contract.Call(nnsContractAddr, "isAvailable",
@ -294,68 +271,76 @@ func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool
return false return false
} }
// Delete method removes container from contract storage if it was // Delete method removes a container from the contract storage if it has been
// invoked by Alphabet nodes of the Inner Ring. Otherwise it produces // invoked by Alphabet nodes of the Inner Ring.
// containerDelete notification.
// //
// Signature is a RFC6979 signature of container ID. // Signature is a RFC6979 signature of the container ID.
// Token is optional and should be stable marshaled SessionToken structure from // Token is optional and should be a stable marshaled SessionToken structure from
// API. // API.
// //
// If a container doesn't exist it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func Delete(containerID []byte, signature interop.Signature, token []byte) { func Delete(containerID []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
ownerID := getOwnerByID(ctx, containerID) ownerID := getOwnerByID(ctx, containerID)
if ownerID == nil { if ownerID == nil {
return return
} }
if notaryDisabled { common.CheckAlphabetWitness()
alphabet := common.AlphabetNodes()
nodeKey := common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
runtime.Notify("containerDelete", containerID, signature, token)
return
}
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{containerID, signature}, []byte("delete"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
key := append([]byte(nnsHasAliasKey), containerID...) key := append([]byte(nnsHasAliasKey), containerID...)
domain := storage.Get(ctx, key).(string) domain := storage.Get(ctx, key).(string)
if len(domain) != 0 { if len(domain) != 0 {
storage.Delete(ctx, key) storage.Delete(ctx, key)
// We should do `getRecord` first because NNS record could be deleted // We should do `getRecord` first because NNS record could be deleted
// by other means (expiration, manual) thus leading to failing `deleteRecord` // by other means (expiration, manual), thus leading to failing `deleteRecord`
// and inability to delete container. We should also check that we own the record in case. // and inability to delete a container. We should also check if we own the record in case.
nnsContractAddr := storage.Get(ctx, nnsContractKey).(interop.Hash160) nnsContractAddr := storage.Get(ctx, nnsContractKey).(interop.Hash160)
res := contract.Call(nnsContractAddr, "getRecords", contract.ReadStates|contract.AllowCall, domain, 16 /* TXT */) res := contract.Call(nnsContractAddr, "getRecords", contract.ReadStates|contract.AllowCall, domain, 16 /* TXT */)
if res != nil && std.Base58Encode(containerID) == string(res.([]interface{})[0].(string)) { if res != nil && std.Base58Encode(containerID) == string(res.([]any)[0].(string)) {
contract.Call(nnsContractAddr, "deleteRecords", contract.All, domain, 16 /* TXT */) contract.Call(nnsContractAddr, "deleteRecords", contract.All, domain, 16 /* TXT */)
} }
} }
removeContainer(ctx, containerID, ownerID) removeContainer(ctx, containerID, ownerID)
runtime.Log("remove container") runtime.Log("remove container")
runtime.Notify("DeleteSuccess", containerID)
} }
// Get method returns structure that contains stable marshaled Container structure, type DelInfo struct {
// signature, public key of the container creator and stable marshaled SessionToken Owner []byte
Epoch int
}
type delInfo struct {
Owner []byte
Epoch []byte
}
// DeletionInfo method returns container deletion info.
// If the container had never existed, NotFoundError is throwed.
// It can be used to check whether non-existing container was indeed deleted
// or does not yet exist at some height.
func DeletionInfo(containerID []byte) DelInfo {
ctx := storage.GetReadOnlyContext()
graveKey := append([]byte{graveKeyPrefix}, containerID...)
data := storage.Get(ctx, graveKey).([]byte)
if data == nil {
panic(NotFoundError)
}
d := std.Deserialize(data).(delInfo)
return DelInfo{
Owner: d.Owner,
Epoch: common.FromFixedWidth64(d.Epoch),
}
}
// Get method returns a structure that contains a stable marshaled Container structure,
// the signature, the public key of the container creator and a stable marshaled SessionToken
// structure if it was provided. // structure if it was provided.
// //
// If a container doesn't exist it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func Get(containerID []byte) Container { func Get(containerID []byte) Container {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
cnt := getContainer(ctx, containerID) cnt := getContainer(ctx, containerID)
@ -365,9 +350,9 @@ func Get(containerID []byte) Container {
return cnt return cnt
} }
// Owner method returns 25 byte Owner ID of the container. // Owner method returns a 25 byte Owner ID of the container.
// //
// If a container doesn't exist it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func Owner(containerID []byte) []byte { func Owner(containerID []byte) []byte {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
owner := getOwnerByID(ctx, containerID) owner := getOwnerByID(ctx, containerID)
@ -377,7 +362,29 @@ func Owner(containerID []byte) []byte {
return owner return owner
} }
// List method returns list of all container IDs owned by specified owner. // Count method returns the number of registered containers.
func Count() int {
count := 0
ctx := storage.GetReadOnlyContext()
it := storage.Find(ctx, []byte{containerKeyPrefix}, storage.KeysOnly)
for iterator.Next(it) {
count++
}
return count
}
// ContainersOf iterates over all container IDs owned by the specified owner.
// If owner is nil, it iterates over all containers.
func ContainersOf(owner []byte) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
key := []byte{ownerKeyPrefix}
if len(owner) != 0 {
key = append(key, owner...)
}
return storage.Find(ctx, key, storage.ValuesOnly)
}
// List method returns a list of all container IDs owned by the specified owner.
func List(owner []byte) [][]byte { func List(owner []byte) [][]byte {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
@ -387,7 +394,7 @@ func List(owner []byte) [][]byte {
var list [][]byte var list [][]byte
it := storage.Find(ctx, owner, storage.ValuesOnly) it := storage.Find(ctx, append([]byte{ownerKeyPrefix}, owner...), storage.ValuesOnly)
for iterator.Next(it) { for iterator.Next(it) {
id := iterator.Value(it).([]byte) id := iterator.Value(it).([]byte)
list = append(list, id) list = append(list, id)
@ -396,20 +403,18 @@ func List(owner []byte) [][]byte {
return list return list
} }
// SetEACL method sets new extended ACL table related to the contract // SetEACL method sets a new extended ACL table related to the contract
// if it was invoked by Alphabet nodes of the Inner Ring. Otherwise it produces // if it was invoked by Alphabet nodes of the Inner Ring.
// setEACL notification.
// //
// EACL should be stable marshaled EACLTable structure from API. // EACL should be a stable marshaled EACLTable structure from API.
// Signature is a RFC6979 signature of Container. // Signature is a RFC6979 signature of the Container.
// PublicKey contains public key of the signer. // PublicKey contains the public key of the signer.
// Token is optional and should be stable marshaled SessionToken structure from // Token is optional and should be a stable marshaled SessionToken structure from
// API. // API.
// //
// If a container doesn't exist it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) { func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
// V2 format // V2 format
// get container ID // get container ID
@ -422,27 +427,7 @@ func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicK
panic(NotFoundError) panic(NotFoundError)
} }
if notaryDisabled { common.CheckAlphabetWitness()
alphabet := common.AlphabetNodes()
nodeKey := common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
runtime.Notify("setEACL", eACL, signature, publicKey, token)
return
}
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{eACL}, []byte("setEACL"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
rule := ExtendedACL{ rule := ExtendedACL{
value: eACL, value: eACL,
@ -456,13 +441,14 @@ func SetEACL(eACL []byte, signature interop.Signature, publicKey interop.PublicK
common.SetSerialized(ctx, key, rule) common.SetSerialized(ctx, key, rule)
runtime.Log("success") runtime.Log("success")
runtime.Notify("SetEACLSuccess", containerID, publicKey)
} }
// EACL method returns structure that contains stable marshaled EACLTable structure, // EACL method returns a structure that contains a stable marshaled EACLTable structure,
// signature, public key of the extended ACL setter and stable marshaled SessionToken // the signature, the public key of the extended ACL setter and a stable marshaled SessionToken
// structure if it was provided. // structure if it was provided.
// //
// If a container doesn't exist it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func EACL(containerID []byte) ExtendedACL { func EACL(containerID []byte) ExtendedACL {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
@ -475,10 +461,10 @@ func EACL(containerID []byte) ExtendedACL {
} }
// PutContainerSize method saves container size estimation in contract // PutContainerSize method saves container size estimation in contract
// memory. Can be invoked only by Storage nodes from the network map. Method // memory. It can be invoked only by Storage nodes from the network map. This method
// checks witness based on the provided public key of the Storage node. // checks witness based on the provided public key of the Storage node.
// //
// If a container doesn't exist it panics with NotFoundError. // If the container doesn't exist, it panics with NotFoundError.
func PutContainerSize(epoch int, cid []byte, usedSize int, pubKey interop.PublicKey) { func PutContainerSize(epoch int, cid []byte, usedSize int, pubKey interop.PublicKey) {
ctx := storage.GetContext() ctx := storage.GetContext()
@ -500,17 +486,18 @@ func PutContainerSize(epoch int, cid []byte, usedSize int, pubKey interop.Public
} }
storage.Put(ctx, key, std.Serialize(s)) storage.Put(ctx, key, std.Serialize(s))
updateEstimations(ctx, epoch, cid, pubKey, false)
runtime.Log("saved container size estimation") runtime.Log("saved container size estimation")
} }
// GetContainerSize method returns container ID and slice of container // GetContainerSize method returns the container ID and a slice of container
// estimations. Container estimation includes public key of the Storage Node // estimations. Container estimation includes the public key of the Storage Node
// that registered estimation and value of estimation. // that registered estimation and value of estimation.
// //
// Use ID obtained from ListContainerSizes method. Estimations are removed // Use the ID obtained from ListContainerSizes method. Estimations are removed
// from contract storage every epoch, see NewEpoch method, therefore method // from contract storage every epoch, see NewEpoch method; therefore, this method
// can return different results in different epochs. // can return different results during different epochs.
func GetContainerSize(id []byte) containerSizes { func GetContainerSize(id []byte) containerSizes {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
@ -523,12 +510,12 @@ func GetContainerSize(id []byte) containerSizes {
return getContainerSizeEstimation(ctx, id, cid) return getContainerSizeEstimation(ctx, id, cid)
} }
// ListContainerSizes method returns IDs of container size estimations // ListContainerSizes method returns the IDs of container size estimations
// that has been registered for specified epoch. // that have been registered for the specified epoch.
func ListContainerSizes(epoch int) [][]byte { func ListContainerSizes(epoch int) [][]byte {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
var buf interface{} = epoch var buf any = epoch
key := []byte(estimateKeyPrefix) key := []byte(estimateKeyPrefix)
key = append(key, buf.([]byte)...) key = append(key, buf.([]byte)...)
@ -555,137 +542,88 @@ func ListContainerSizes(epoch int) [][]byte {
return result return result
} }
// IterateContainerSizes method returns iterator over container size estimations
// that have been registered for the specified epoch.
func IterateContainerSizes(epoch int) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
var buf any = epoch
key := []byte(estimateKeyPrefix)
key = append(key, buf.([]byte)...)
return storage.Find(ctx, key, storage.DeserializeValues)
}
// NewEpoch method removes all container size estimations from epoch older than // NewEpoch method removes all container size estimations from epoch older than
// epochNum + 3. Can be invoked only by NewEpoch method of the Netmap contract. // epochNum + 3. It can be invoked only by NewEpoch method of the Netmap contract.
func NewEpoch(epochNum int) { func NewEpoch(epochNum int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
if notaryDisabled { common.CheckAlphabetWitness()
indirectCall := common.FromKnownContract(
ctx,
runtime.GetCallingScriptHash(),
netmapContractKey,
)
if !indirectCall {
panic("method must be invoked by inner ring")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
candidates := keysToDelete(ctx, epochNum) cleanupContainers(ctx, epochNum)
for _, candidate := range candidates {
storage.Delete(ctx, candidate)
}
} }
// StartContainerEstimation method produces StartEstimation notification. // StartContainerEstimation method produces StartEstimation notification.
// Can be invoked only by Alphabet nodes of the Inner Ring. // It can be invoked only by Alphabet nodes of the Inner Ring.
func StartContainerEstimation(epoch int) { func StartContainerEstimation(epoch int) {
ctx := storage.GetContext() common.CheckAlphabetWitness()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("method must be invoked by inner ring")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{epoch}, []byte("startEstimation"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
runtime.Notify("StartEstimation", epoch) runtime.Notify("StartEstimation", epoch)
runtime.Log("notification has been produced") runtime.Log("notification has been produced")
} }
// StopContainerEstimation method produces StopEstimation notification. // StopContainerEstimation method produces StopEstimation notification.
// Can be invoked only by Alphabet nodes of the Inner Ring. // It can be invoked only by Alphabet nodes of the Inner Ring.
func StopContainerEstimation(epoch int) { func StopContainerEstimation(epoch int) {
ctx := storage.GetContext() common.CheckAlphabetWitness()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("method must be invoked by inner ring")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{epoch}, []byte("stopEstimation"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
runtime.Notify("StopEstimation", epoch) runtime.Notify("StopEstimation", epoch)
runtime.Log("notification has been produced") runtime.Log("notification has been produced")
} }
// Version returns version of the contract. // Version returns the version of the contract.
func Version() int { func Version() int {
return common.Version return common.Version
} }
func addContainer(ctx storage.Context, id, owner []byte, container Container) { func addContainer(ctx storage.Context, id, owner []byte, container Container) {
containerListKey := append(owner, id...) containerListKey := append([]byte{ownerKeyPrefix}, owner...)
containerListKey = append(containerListKey, id...)
storage.Put(ctx, containerListKey, id) storage.Put(ctx, containerListKey, id)
common.SetSerialized(ctx, id, container) idKey := append([]byte{containerKeyPrefix}, id...)
common.SetSerialized(ctx, idKey, container)
graveKey := append([]byte{graveKeyPrefix}, id...)
storage.Delete(ctx, graveKey)
} }
func removeContainer(ctx storage.Context, id []byte, owner []byte) { func removeContainer(ctx storage.Context, id []byte, owner []byte) {
containerListKey := append(owner, id...) containerListKey := append([]byte{ownerKeyPrefix}, owner...)
containerListKey = append(containerListKey, id...)
storage.Delete(ctx, containerListKey) storage.Delete(ctx, containerListKey)
storage.Delete(ctx, id) storage.Delete(ctx, append([]byte{containerKeyPrefix}, id...))
graveKey := append([]byte{graveKeyPrefix}, id...)
netmapContractAddr := storage.Get(ctx, netmapContractKey).(interop.Hash160)
epoch := contract.Call(netmapContractAddr, "epoch", contract.ReadOnly).(int)
common.SetSerialized(ctx, graveKey, delInfo{
Owner: owner,
Epoch: common.ToFixedWidth64(epoch),
})
} }
func getAllContainers(ctx storage.Context) [][]byte { func getAllContainers(ctx storage.Context) [][]byte {
var list [][]byte var list [][]byte
it := storage.Find(ctx, []byte{}, storage.KeysOnly) it := storage.Find(ctx, []byte{containerKeyPrefix}, storage.KeysOnly|storage.RemovePrefix)
for iterator.Next(it) { for iterator.Next(it) {
key := iterator.Value(it).([]byte) // it MUST BE `storage.KeysOnly` key := iterator.Value(it).([]byte) // it MUST BE `storage.KeysOnly`
// V2 format // V2 format
if len(key) == containerIDSize { list = append(list, key)
list = append(list, key)
}
} }
return list return list
@ -702,7 +640,7 @@ func getEACL(ctx storage.Context, cid []byte) ExtendedACL {
} }
func getContainer(ctx storage.Context, cid []byte) Container { func getContainer(ctx storage.Context, cid []byte) Container {
data := storage.Get(ctx, cid) data := storage.Get(ctx, append([]byte{containerKeyPrefix}, cid...))
if data != nil { if data != nil {
return std.Deserialize(data.([]byte)).(Container) return std.Deserialize(data.([]byte)).(Container)
} }
@ -727,7 +665,7 @@ func ownerFromBinaryContainer(container []byte) []byte {
} }
func estimationKey(epoch int, cid []byte, key interop.PublicKey) []byte { func estimationKey(epoch int, cid []byte, key interop.PublicKey) []byte {
var buf interface{} = epoch var buf any = epoch
hash := crypto.Ripemd160(key) hash := crypto.Ripemd160(key)
@ -754,7 +692,7 @@ func getContainerSizeEstimation(ctx storage.Context, key, cid []byte) containerS
} }
// isStorageNode looks into _previous_ epoch network map, because storage node // isStorageNode looks into _previous_ epoch network map, because storage node
// announce container size estimation of previous epoch. // announces container size estimation of the previous epoch.
func isStorageNode(ctx storage.Context, key interop.PublicKey) bool { func isStorageNode(ctx storage.Context, key interop.PublicKey) bool {
netmapContractAddr := storage.Get(ctx, netmapContractKey).(interop.Hash160) netmapContractAddr := storage.Get(ctx, netmapContractKey).(interop.Hash160)
snapshot := contract.Call(netmapContractAddr, "snapshot", contract.ReadOnly, 1).([]storageNode) snapshot := contract.Call(netmapContractAddr, "snapshot", contract.ReadOnly, 1).([]storageNode)
@ -772,21 +710,43 @@ func isStorageNode(ctx storage.Context, key interop.PublicKey) bool {
return false return false
} }
func keysToDelete(ctx storage.Context, epoch int) [][]byte { func updateEstimations(ctx storage.Context, epoch int, cid []byte, pub interop.PublicKey, isUpdate bool) {
results := [][]byte{} h := crypto.Ripemd160(pub)
estKey := append([]byte(singleEstimatePrefix), cid...)
estKey = append(estKey, h...)
var newEpochs []int
rawList := storage.Get(ctx, estKey).([]byte)
if rawList != nil {
epochs := std.Deserialize(rawList).([]int)
for _, oldEpoch := range epochs {
if !isUpdate && epoch-oldEpoch > CleanupDelta {
key := append([]byte(estimateKeyPrefix), convert.ToBytes(oldEpoch)...)
key = append(key, cid...)
key = append(key, h[:estimatePostfixSize]...)
storage.Delete(ctx, key)
} else {
newEpochs = append(newEpochs, oldEpoch)
}
}
}
newEpochs = append(newEpochs, epoch)
common.SetSerialized(ctx, estKey, newEpochs)
}
func cleanupContainers(ctx storage.Context, epoch int) {
it := storage.Find(ctx, []byte(estimateKeyPrefix), storage.KeysOnly) it := storage.Find(ctx, []byte(estimateKeyPrefix), storage.KeysOnly)
for iterator.Next(it) { for iterator.Next(it) {
k := iterator.Value(it).([]byte) k := iterator.Value(it).([]byte)
// V2 format // V2 format
nbytes := k[len(estimateKeyPrefix) : len(k)-containerIDSize-estimatePostfixSize] nbytes := k[len(estimateKeyPrefix) : len(k)-containerIDSize-estimatePostfixSize]
var n interface{} = nbytes var n any = nbytes
if epoch-n.(int) > cleanupDelta { if epoch-n.(int) > TotalCleanupDelta {
results = append(results, k) storage.Delete(ctx, k)
} }
} }
return results
} }

View file

@ -1,68 +1,42 @@
/* /*
Container contract is a contract deployed in NeoFS side chain. Container contract is a contract deployed in FrostFS sidechain.
Container contract stores and manages containers, extended ACLs and container Container contract stores and manages containers, extended ACLs and container
size estimations. Contract does not perform sanity or signature checks of the size estimations. Contract does not perform sanity or signature checks of
containers or extended ACLs, it is done by Alphabet nodes of the Inner Ring. containers or extended ACLs, it is done by Alphabet nodes of the Inner Ring.
Alphabet nodes approve it by invoking the same Put or SetEACL methods with Alphabet nodes approve it by invoking the same Put or SetEACL methods with
the same arguments. the same arguments.
Contract notifications # Contract notifications
containerPut notification. This notification is produced when user wants to
create new container. Alphabet nodes of the Inner Ring catch notification and
validate container data, signature and token if it is present.
containerPut:
- name: container
type: ByteArray
- name: signature
type: Signature
- name: publicKey
type: PublicKey
- name: token
type: ByteArray
containerDelete notification. This notification is produced when container owner
wants to delete container. Alphabet nodes of the Inner Ring catch notification
and validate container ownership, signature and token if it is present.
containerDelete:
- name: containerID
type: ByteArray
- name: signature
type: Signature
- name: token
type: ByteArray
setEACL notification. This notification is produced when container owner wants
to update extended ACL of the container. Alphabet nodes of the Inner Ring catch
notification and validate container ownership, signature and token if it is
present.
setEACL:
- name: eACL
type: ByteArray
- name: signature
type: Signature
- name: publicKey
type: PublicKey
- name: token
type: ByteArray
StartEstimation notification. This notification is produced when Storage nodes StartEstimation notification. This notification is produced when Storage nodes
should exchange estimation values of container sizes among other Storage nodes. should exchange estimation values of container sizes among other Storage nodes.
StartEstimation: StartEstimation:
- name: epoch - name: epoch
type: Integer type: Integer
StopEstimation notification. This notification is produced when Storage nodes StopEstimation notification. This notification is produced when Storage nodes
should calculate average container size based on received estimations and store should calculate average container size based on received estimations and store
it in Container contract. it in Container contract.
StopEstimation: StopEstimation:
- name: epoch - name: epoch
type: Integer type: Integer
# Contract storage scheme
| Key | Value | Description |
|-----------------------------------------------------------------------------------------------------|
| `netmapScriptHash` | Hash160 | netmap contract hash |
| `balanceScriptHash` | Hash160 | balance contract hash |
| `identityScriptHash` | Hash160 | frostfsID contract hash |
| `nnsContractKey` | Hash160 | nns contract hash |
| `nnsRoot` | string | default value for domain zone |
| `cnr` + epoch + containerID + publicKeyHash[:10] | ByteArray | estimated container size |
| `est` + containerID + publicKeyHash | ByteArray | serialized epochs array |
| `o` + ownerID + containerID | ByteArray | container ID |
| `x` + containerID | ByteArray | serialized container struct |
| `nnsHasAlias` + containerID | string | domain name |
*/ */
package container package container

5
debian/changelog vendored Normal file
View file

@ -0,0 +1,5 @@
frostfs-contract (0.0.0) stable; urgency=medium
* Initial release
-- TrueCloudLab <tech@frostfs.info> Wed, 24 Aug 2022 18:29:49 +0300

33
debian/control vendored Normal file
View file

@ -0,0 +1,33 @@
Source: frostfs-contract
Section: misc
Priority: optional
Maintainer: FrostFS <tech@frostfs.info>
Build-Depends: debhelper-compat (= 13), git, devscripts, neo-go
Standards-Version: 4.5.1
Homepage: https://fs.neo.org/
Vcs-Git: https://git.frostfs.info/TrueCloudLab/frostfs-contract.git
Vcs-Browser: https://git.frostfs.info/TrueCloudLab/frostfs-contract
Package: frostfs-contract
Architecture: all
Depends: ${misc:Depends}
Description: FrostFS-Contract contains all FrostFS related contracts.
Contracts are written for neo-go compiler.
These contracts are deployed both in the mainchain and the sidechain.
.
Mainchain contracts:
.
- frostfs
- processing
.
Sidechain contracts:
.
- alphabet
- audit
- balance
- container
- frostfsid
- netmap
- nns
- proxy
- reputation

23
debian/copyright vendored Normal file
View file

@ -0,0 +1,23 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: frostfs-contract
Upstream-Contact: tech@frostfs.info
Source: https://git.frostfs.info/TrueCloudLab/frostfs-contract
Files: *
Copyright: 2022 TrueCloudLab (@TrueCloudLab)
Copyright: 2018-2022 NeoSPCC (@nspcc-dev)
License: GPL-3
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program or at /usr/share/common-licenses/GPL-3.
If not, see <http://www.gnu.org/licenses/>.

1
debian/neofs-contract.docs vendored Normal file
View file

@ -0,0 +1 @@
README*

39
debian/postinst.ex vendored Normal file
View file

@ -0,0 +1,39 @@
#!/bin/sh
# postinst script for frostfs-contract
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

37
debian/postrm.ex vendored Normal file
View file

@ -0,0 +1,37 @@
#!/bin/sh
# postrm script for frostfs-contract
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <overwriter>
# <overwriter-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

35
debian/preinst.ex vendored Normal file
View file

@ -0,0 +1,35 @@
#!/bin/sh
# preinst script for frostfs-contract
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
install|upgrade)
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

38
debian/prerm.ex vendored Normal file
View file

@ -0,0 +1,38 @@
#!/bin/sh
# prerm script for frostfs-contract
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|upgrade|deconfigure)
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

20
debian/rules vendored Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/make -f
SERVICE = frostfs-contract
export NEOGO ?= $(shell command -v neo-go)
%:
dh $@
override_dh_auto_build:
make all
override_dh_auto_install:
install -D -m 0750 -d debian/$(SERVICE)/var/lib/frostfs/contract
find . -maxdepth 2 \( -name '*.nef' -o -name 'config.json' \) -exec cp --parents \{\} debian/$(SERVICE)/var/lib/frostfs/contract \;
override_dh_installchangelogs:
dh_installchangelogs -k CHANGELOG.md

1
debian/source/format vendored Normal file
View file

@ -0,0 +1 @@
3.0 (quilt)

View file

@ -0,0 +1,160 @@
# Globally unique domain zone
**Make sure you understand the [basic concepts](../nns/README.md) of `NNS`.**
`Globally Unique Domains Zone` (`GUDZ`) is an extension of `NNS` that ensures unique names across multiple domain zones. When this option is enabled, all newly created domains will automatically receive a corresponding alias in the designated global zone. Deleting a domain will also remove its alias from the global zone.
It's important to note that this feature is not retroactive: domains created before this option is enabled will not receive a global alias. Likewise, if the option is later disabled, domains that already have a `GUDZ` alias will retain their records. To fully disable `GUDZ`, all domains must be recreated with the option turned off.
To enable `GUDZ`, add a `cnametgt=$(global domain)` `TXT` record that specifies the global zone.
**Example:**
Domains:
- `poland`
- `sweden`
- `animals.org`
It is necessary to associate the domain zones `.poland` and `.sweden` into the global zone `.animals`.
![](img/GUDZ.png)
Create domains:
```
frostfs-adm morph nns register --name="poland" --email="email@email.email"
frostfs-adm morph nns register --name="sweden" --email="email@email.email"
frostfs-adm morph nns register --name="org" --email="email@email.email"
frostfs-adm morph nns register --name="animals.org" --email="email@email.email"
```
Add the `cnametgt` records:
```
frostfs-adm morph nns add-record --name="poland" --data="cnametgt=animals.org" --type="txt"
frostfs-adm morph nns add-record --name="sweden" --data="cnametgt=animals.org" --type="txt"
```
Create a domain with mapping to the global zone:
```
frostfs-adm morph nns register --name="bober.poland" --email="email@email.email"
```
Add any `TXT` record
```
frostfs-adm morph nns add-record --name="bober.poland" --data="CID" --type="txt"
```
Verify that the created domain has alias in the global zone
```
frostfs-adm morph nns tokens -v
balance.frostfs
animals.org
group.frostfs
container
org
container.frostfs
proxy.frostfs
policy.frostfs
alphabet0.frostfs
sweden
frostfsid.frostfs
bober.animals.org (CNAME: bober.poland)
netmap.frostfs
frostfs
poland
bober.poland
```
Create of a conflicting domain
```
frostfs-adm morph nns register --name="bober.sweden" --email="email@email.email"
unable to register domain: script failed (FAULT state) due to an error: at instruction 1263 (THROW): unhandled exception: "global domain is already taken: bober.animals.org. Domain: bober.poland
```
**Disable GUDZ**
Delete `cnametgt` records
```
frostfs-adm morph nns delete-records --type=txt --name=poland
```
Create `hamster.poland` and `hamster.sweden`
```
frostfs-adm morph nns register --name="hamster.poland" --email="email@email.email"
frostfs-adm morph nns register --name="hamster.sweden" --email="email@email.email"
```
`hamster.poland` and `hamster.sweden` does not have alias
```
frostfs-adm morph nns tokens -v
balance.frostfs
animals.org
group.frostfs
container
org
container.frostfs
proxy.frostfs
policy.frostfs
alphabet0.frostfs
sweden
frostfsid.frostfs
bober.animals.org (CNAME: bober.poland)
netmap.frostfs
frostfs
poland
bober.poland
hamster.poland
```
Delete global alias of `bober.poland`
```
frostfs-adm morph nns delete-records --name="bober.poland" --type="txt"
```
```
frostfs-adm morph nns tokens -v
balance.frostfs
animals.org
group.frostfs
container
org
container.frostfs
proxy.frostfs
policy.frostfs
alphabet0.frostfs
sweden
frostfsid.frostfs
netmap.frostfs
frostfs
poland
bober.poland
hamster.poland
```
Delete `bober.poland`
```
frostfs-adm morph nns delete --name="bober.poland"
```
```
frostfs-adm morph nns tokens -v
balance.frostfs
animals.org
group.frostfs
container
org
container.frostfs
proxy.frostfs
policy.frostfs
alphabet0.frostfs
sweden
frostfsid.frostfs
netmap.frostfs
frostfs
poland
hamster.poland
```

139
docs/img/GUDZ.drawio Normal file
View file

@ -0,0 +1,139 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:129.0) Gecko/20100101 Firefox/129.0" version="24.7.14">
<diagram name="Page-1" id="N1NjK5oQ_tQiBXsDL3WT">
<mxGraphModel dx="989" dy="917" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-1" value=".ns" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="130" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-2" value=".org" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="440" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-3" value=".sweden" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="250" y="280" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-4" value=".poland" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="10" y="280" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-5" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="l_qLPBR5nCWAv8Wbn5Wl-1" target="l_qLPBR5nCWAv8Wbn5Wl-4">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="260" y="480" as="sourcePoint" />
<mxPoint x="310" y="430" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-6" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="l_qLPBR5nCWAv8Wbn5Wl-1" target="l_qLPBR5nCWAv8Wbn5Wl-3">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="200" y="230" as="sourcePoint" />
<mxPoint x="80" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-7" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="10" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-8" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="l_qLPBR5nCWAv8Wbn5Wl-4" target="l_qLPBR5nCWAv8Wbn5Wl-7">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="190" y="450" as="sourcePoint" />
<mxPoint x="70" y="390" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-9" value="bober" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;strokeColor=#FF6666;" vertex="1" parent="1">
<mxGeometry x="250" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-10" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;fillColor=#f8cecc;strokeColor=#FF6666;" edge="1" parent="1" target="l_qLPBR5nCWAv8Wbn5Wl-9">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="310" y="340" as="sourcePoint" />
<mxPoint x="310" y="390" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-11" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="440" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-12" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" target="l_qLPBR5nCWAv8Wbn5Wl-11">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="500" y="340" as="sourcePoint" />
<mxPoint x="500" y="390" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-13" value="&lt;font style=&quot;font-size: 5px;&quot;&gt;TXT cnametgt=animals.org&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="30" y="310" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-14" value=".&lt;span lang=&quot;en&quot; class=&quot;Y2IQFc&quot;&gt;animals&lt;/span&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="440" y="280" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-15" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="499.76" y="220" as="sourcePoint" />
<mxPoint x="499.76" y="280" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-16" value="&lt;font style=&quot;font-size: 5px;&quot;&gt;TXT cnametgt=&lt;/font&gt;&lt;font style=&quot;font-size: 5px;&quot;&gt;animals.org&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="270" y="310" width="80" height="30" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-17" value="" style="shape=umlDestroy;whiteSpace=wrap;html=1;strokeWidth=3;targetShapes=umlLifeline;strokeColor=#FF6666;" vertex="1" parent="1">
<mxGeometry x="292.5" y="350" width="35" height="40" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-18" value="&lt;span style=&quot;white-space: pre-wrap;&quot; data-src-align=&quot;0:10&quot; class=&quot;EzKURWReUAB5oZgtQNkl&quot;&gt;[ global&lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot; data-src-align=&quot;11:8&quot; class=&quot;EzKURWReUAB5oZgtQNkl&quot;&gt;domain&lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot; data-src-align=&quot;20:4&quot; class=&quot;EzKURWReUAB5oZgtQNkl&quot;&gt;zone ]&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="425" y="100" width="150" height="30" as="geometry" />
</mxCell>
<mxCell id="l_qLPBR5nCWAv8Wbn5Wl-19" value="&lt;font style=&quot;font-size: 5px;&quot;&gt;CNAME bober&lt;/font&gt;&lt;font style=&quot;font-size: 5px;&quot;&gt;.&lt;/font&gt;&lt;font style=&quot;font-size: 5px;&quot;&gt;poland&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="465" y="430" width="70" height="30" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-1" value=".ns" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="130" y="-240" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-2" value=".org" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="440" y="-240" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-3" value=".sweden" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="250" y="-120" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-4" value=".poland" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="10" y="-120" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-5" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z9nLDmrU-mAa8Hye4rHX-1" target="Z9nLDmrU-mAa8Hye4rHX-4">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="260" y="80" as="sourcePoint" />
<mxPoint x="310" y="30" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-6" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z9nLDmrU-mAa8Hye4rHX-1" target="Z9nLDmrU-mAa8Hye4rHX-3">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="200" y="-170" as="sourcePoint" />
<mxPoint x="80" y="-110" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-7" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="10" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-8" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Z9nLDmrU-mAa8Hye4rHX-4" target="Z9nLDmrU-mAa8Hye4rHX-7">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="190" y="50" as="sourcePoint" />
<mxPoint x="70" y="-10" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-9" value=".&lt;span lang=&quot;en&quot; class=&quot;Y2IQFc&quot;&gt;animals&lt;/span&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="440" y="-120" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-10" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="499.76" y="-180" as="sourcePoint" />
<mxPoint x="499.76" y="-120" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-11" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="309.71000000000004" y="-60" as="sourcePoint" />
<mxPoint x="309.71000000000004" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-12" value="bober" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Z9nLDmrU-mAa8Hye4rHX-13" value="&lt;span style=&quot;white-space: pre-wrap;&quot; data-src-align=&quot;0:10&quot; class=&quot;EzKURWReUAB5oZgtQNkl&quot;&gt;[ without global&lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot; data-src-align=&quot;11:8&quot; class=&quot;EzKURWReUAB5oZgtQNkl&quot;&gt;domain&lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;white-space: pre-wrap;&quot; data-src-align=&quot;20:4&quot; class=&quot;EzKURWReUAB5oZgtQNkl&quot;&gt;zone ]&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="390" y="-300" width="180" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

BIN
docs/img/GUDZ.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View file

@ -1,5 +1,9 @@
name: "NeoFS" name: "FrostFS"
safemethods: ["alphabetList", "alphabetAddress", "innerRingCandidates", "config", "listConfig", "version"] safemethods:
- "config"
- "innerRingCandidates"
- "listConfig"
- "version"
permissions: permissions:
- methods: ["update", "transfer"] - methods: ["update", "transfer"]
events: events:

92
frostfs/doc.go Normal file
View file

@ -0,0 +1,92 @@
/*
FrostFS contract is a contract deployed in FrostFS mainchain.
FrostFS contract is an entry point to FrostFS users. This contract stores all FrostFS
related GAS, registers new Inner Ring candidates and produces notifications
to control the sidechain.
While mainchain committee controls the list of Alphabet nodes in native
RoleManagement contract, FrostFS can't change more than 1\3 keys at a time.
FrostFS contract contains the actual list of Alphabet nodes in the sidechain.
Network configuration is also stored in FrostFS contract. All changes in
configuration are mirrored in the sidechain with notifications.
# Contract notifications
Deposit notification. This notification is produced when user transfers native
GAS to the FrostFS contract address. The same amount of FROSTFS token will be
minted in Balance contract in the sidechain.
Deposit:
- name: from
type: Hash160
- name: amount
type: Integer
- name: receiver
type: Hash160
- name: txHash
type: Hash256
Withdraw notification. This notification is produced when a user wants to
withdraw GAS from the internal FrostFS balance and has paid fee for that.
Withdraw:
- name: user
type: Hash160
- name: amount
type: Integer
- name: txHash
type: Hash256
Cheque notification. This notification is produced when FrostFS contract
has successfully transferred assets back to the user after withdraw.
Cheque:
- name: id
type: ByteArray
- name: user
type: Hash160
- name: amount
type: Integer
- name: lockAccount
type: ByteArray
Bind notification. This notification is produced when a user wants to bind
public keys with the user account (OwnerID). Keys argument is an array of ByteArray.
Bind:
- name: user
type: ByteArray
- name: keys
type: Array
Unbind notification. This notification is produced when a user wants to unbind
public keys with the user account (OwnerID). Keys argument is an array of ByteArray.
Unbind:
- name: user
type: ByteArray
- name: keys
type: Array
SetConfig notification. This notification is produced when Alphabet nodes update
FrostFS network configuration value.
SetConfig
- name: id
type: ByteArray
- name: key
type: ByteArray
- name: value
type: ByteArray
# Contract storage scheme
| Key | Value | Description |
|-----------------------------------------------------------------------------|
| `processingScriptHash` | Hash160 | processing contract hash |
| `candidates` + candidateKey | ByteArray | it flags inner ring candidate |
| `config` + postfix | ByteArray | serialized config data |
*/
package frostfs

370
frostfs/frostfs_contract.go Normal file
View file

@ -0,0 +1,370 @@
package frostfs
import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"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/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
type (
record struct {
key []byte
val []byte
}
)
const (
// CandidateFeeConfigKey contains fee for a candidate registration.
CandidateFeeConfigKey = "InnerRingCandidateFee"
withdrawFeeConfigKey = "WithdrawFee"
candidatesKey = "candidates"
processingContractKey = "processingScriptHash"
maxBalanceAmount = 9000 // Max integer of Fixed12 in JSON bound (2**53-1)
maxBalanceAmountGAS = int64(maxBalanceAmount) * 1_0000_0000
// hardcoded value to ignore deposit notification in onReceive
ignoreDepositNotification = "\x57\x0b"
)
var configPrefix = []byte("config")
// _deploy sets up initial alphabet node keys.
func _deploy(data any, isUpdate bool) {
ctx := storage.GetContext()
if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return
}
args := data.(struct {
addrProc interop.Hash160
keys []interop.PublicKey
config [][]byte
})
if len(args.keys) == 0 {
panic("at least one alphabet key must be provided")
}
if len(args.addrProc) != interop.Hash160Len {
panic("incorrect length of contract script hash")
}
for i := 0; i < len(args.keys); i++ {
pub := args.keys[i]
if len(pub) != interop.PublicKeyCompressedLen {
panic("incorrect public key length")
}
}
storage.Put(ctx, processingContractKey, args.addrProc)
ln := len(args.config)
if ln%2 != 0 {
panic("bad configuration")
}
for i := 0; i < ln/2; i++ {
key := args.config[i*2]
val := args.config[i*2+1]
setConfig(ctx, key, val)
}
runtime.Log("frostfs: contract initialized")
}
// Update method updates contract source code and manifest. It can be invoked
// only by sidechain committee.
func Update(script []byte, manifest []byte, data any) {
blockHeight := ledger.CurrentIndex()
alphabetKeys := roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1))
alphabetCommittee := common.Multiaddress(alphabetKeys, true)
if !runtime.CheckWitness(alphabetCommittee) {
panic(common.ErrAlphabetWitnessFailed)
}
management.UpdateWithData(script, manifest, common.AppendVersion(data))
runtime.Log("frostfs contract updated")
}
// InnerRingCandidates returns an array of structures that contain an Inner Ring
// candidate node key.
func InnerRingCandidates() []common.IRNode {
ctx := storage.GetReadOnlyContext()
nodes := []common.IRNode{}
it := storage.Find(ctx, candidatesKey, storage.KeysOnly|storage.RemovePrefix)
for iterator.Next(it) {
pub := iterator.Value(it).([]byte)
nodes = append(nodes, common.IRNode{PublicKey: pub})
}
return nodes
}
// InnerRingCandidateRemove removes a key from a list of Inner Ring candidates.
// It can be invoked by Alphabet nodes or the candidate itself.
//
// This method does not return fee back to the candidate.
func InnerRingCandidateRemove(key interop.PublicKey) {
ctx := storage.GetContext()
keyOwner := runtime.CheckWitness(key)
if !keyOwner {
if !runtime.CheckWitness(common.AlphabetAddress()) {
panic("this method must be invoked by candidate or alphabet")
}
}
prefix := []byte(candidatesKey)
stKey := append(prefix, key...)
if storage.Get(ctx, stKey) != nil {
storage.Delete(ctx, stKey)
runtime.Log("candidate has been removed")
}
}
// InnerRingCandidateAdd adds a key to a list of Inner Ring candidates.
// It can be invoked only by the candidate itself.
//
// This method transfers fee from a candidate to the contract account.
// Fee value is specified in FrostFS network config with the key InnerRingCandidateFee.
func InnerRingCandidateAdd(key interop.PublicKey) {
ctx := storage.GetContext()
common.CheckWitness(key)
stKey := append([]byte(candidatesKey), key...)
if storage.Get(ctx, stKey) != nil {
panic("candidate already in the list")
}
from := contract.CreateStandardAccount(key)
to := runtime.GetExecutingScriptHash()
fee := getConfig(ctx, CandidateFeeConfigKey).(int)
transferred := gas.Transfer(from, to, fee, []byte(ignoreDepositNotification))
if !transferred {
panic("failed to transfer funds, aborting")
}
storage.Put(ctx, stKey, []byte{1})
runtime.Log("candidate has been added")
}
// OnNEP17Payment is a callback for NEP-17 compatible native GAS contract.
// It takes no more than 9000.0 GAS. Native GAS has precision 8, and
// FrostFS balance contract has precision 12. Values bigger than 9000.0 can
// break JSON limits for integers when precision is converted.
func OnNEP17Payment(from interop.Hash160, amount int, data any) {
rcv := data.(interop.Hash160)
if common.BytesEqual(rcv, []byte(ignoreDepositNotification)) {
return
}
if amount <= 0 {
common.AbortWithMessage("amount must be positive")
} else if maxBalanceAmountGAS < int64(amount) {
common.AbortWithMessage("out of max amount limit")
}
caller := runtime.GetCallingScriptHash()
if !common.BytesEqual(caller, interop.Hash160(gas.Hash)) {
common.AbortWithMessage("only GAS can be accepted for deposit")
}
switch len(rcv) {
case 20:
case 0:
rcv = from
default:
common.AbortWithMessage("invalid data argument, expected Hash160")
}
runtime.Log("funds have been transferred")
tx := runtime.GetScriptContainer()
runtime.Notify("Deposit", from, amount, rcv, tx.Hash)
}
// Withdraw initializes gas asset withdraw from FrostFS. It can be invoked only
// by the specified user.
//
// This method produces Withdraw notification to lock assets in the sidechain and
// transfers withdraw fee from a user account to each Alphabet node. If notary
// is enabled in the mainchain, fee is transferred to Processing contract.
// Fee value is specified in FrostFS network config with the key WithdrawFee.
func Withdraw(user interop.Hash160, amount int) {
if !runtime.CheckWitness(user) {
panic("you should be the owner of the wallet")
}
if amount < 0 {
panic("non positive amount number")
}
if amount > maxBalanceAmount {
panic("out of max amount limit")
}
ctx := storage.GetContext()
// transfer fee to proxy contract to pay cheque invocation
fee := getConfig(ctx, withdrawFeeConfigKey).(int)
processingAddr := storage.Get(ctx, processingContractKey).(interop.Hash160)
transferred := gas.Transfer(user, processingAddr, fee, []byte{})
if !transferred {
panic("failed to transfer withdraw fee, aborting")
}
// notify alphabet nodes
amount = amount * 100000000
tx := runtime.GetScriptContainer()
runtime.Notify("Withdraw", user, amount, tx.Hash)
}
// Cheque transfers GAS back to the user from the contract account, if assets were
// successfully locked in FrostFS balance contract. It can be invoked only by
// Alphabet nodes.
//
// This method produces Cheque notification to burn assets in sidechain.
func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) {
common.CheckAlphabetWitness()
from := runtime.GetExecutingScriptHash()
transferred := gas.Transfer(from, user, amount, nil)
if !transferred {
panic("failed to transfer funds, aborting")
}
runtime.Log("funds have been transferred")
runtime.Notify("Cheque", id, user, amount, lockAcc)
}
// Bind method produces notification to bind the specified public keys in FrostFSID
// contract in the sidechain. It can be invoked only by specified user.
//
// This method produces Bind notification. This method panics if keys are not
// 33 byte long. User argument must be a valid 20 byte script hash.
func Bind(user []byte, keys []interop.PublicKey) {
if !runtime.CheckWitness(user) {
panic("you should be the owner of the wallet")
}
for i := 0; i < len(keys); i++ {
pubKey := keys[i]
if len(pubKey) != interop.PublicKeyCompressedLen {
panic("incorrect public key size")
}
}
runtime.Notify("Bind", user, keys)
}
// Unbind method produces notification to unbind the specified public keys in FrostFSID
// contract in the sidechain. It can be invoked only by the specified user.
//
// This method produces Unbind notification. This method panics if keys are not
// 33 byte long. User argument must be a valid 20 byte script hash.
func Unbind(user []byte, keys []interop.PublicKey) {
if !runtime.CheckWitness(user) {
panic("you should be the owner of the wallet")
}
for i := 0; i < len(keys); i++ {
pubKey := keys[i]
if len(pubKey) != interop.PublicKeyCompressedLen {
panic("incorrect public key size")
}
}
runtime.Notify("Unbind", user, keys)
}
// Config returns configuration value of FrostFS configuration. If the key does
// not exists, returns nil.
func Config(key []byte) any {
ctx := storage.GetReadOnlyContext()
return getConfig(ctx, key)
}
// SetConfig key-value pair as a FrostFS runtime configuration value. It can be invoked
// only by Alphabet nodes.
func SetConfig(id, key, val []byte) {
ctx := storage.GetContext()
common.CheckAlphabetWitness()
setConfig(ctx, key, val)
runtime.Notify("SetConfig", id, key, val)
runtime.Log("configuration has been updated")
}
// ListConfig returns an array of structures that contain a key and a value of all
// FrostFS configuration records. Key and value are both byte arrays.
func ListConfig() []record {
ctx := storage.GetReadOnlyContext()
var config []record
it := storage.Find(ctx, configPrefix, storage.None)
for iterator.Next(it) {
pair := iterator.Value(it).(struct {
key []byte
val []byte
})
r := record{key: pair.key[len(configPrefix):], val: pair.val}
config = append(config, r)
}
return config
}
// Version returns version of the contract.
func Version() int {
return common.Version
}
// getConfig returns the installed frostfs configuration value or nil if it is not set.
func getConfig(ctx storage.Context, key any) interface{} {
postfix := key.([]byte)
storageKey := append(configPrefix, postfix...)
return storage.Get(ctx, storageKey)
}
// setConfig sets a frostfs configuration value in the contract storage.
func setConfig(ctx storage.Context, key, val any) {
postfix := key.([]byte)
storageKey := append(configPrefix, postfix...)
storage.Put(ctx, storageKey, val)
}
// multiaddress returns a multisignature address from the list of IRNode structures
// with m = 2/3n+1.
func multiaddress(keys []interop.PublicKey) []byte {
threshold := len(keys)*2/3 + 1
return contract.CreateMultisigAccount(threshold, keys)
}

656
frostfsid/client/client.go Normal file
View file

@ -0,0 +1,656 @@
package client
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
"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/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"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/stackitem"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet"
)
type (
Client struct {
act *actor.Actor
waiter waiter.Waiter
contract util.Uint160
}
Options struct {
ProxyContract util.Uint160
Waiter commonclient.WaiterOptions
}
)
type (
Subject struct {
PrimaryKey *keys.PublicKey
AdditionalKeys keys.PublicKeys
Namespace string
Name string
KV map[string]string
}
SubjectExtended struct {
PrimaryKey *keys.PublicKey
AdditionalKeys keys.PublicKeys
Namespace string
Name string
KV map[string]string
Groups []*Group
}
Namespace struct {
Name string
}
NamespaceExtended struct {
Name string
GroupsCount int64
SubjectsCount int64
}
Group struct {
ID int64
Name string
Namespace string
KV map[string]string
}
GroupExtended struct {
ID int64
Name string
Namespace string
KV map[string]string
SubjectsCount int64
}
)
const (
IAMPathKey = "iam-path"
IAMARNKey = "iam-arn"
IAMCreatedTimeKey = "ctime"
IAMModifiedTimeKey = "mtime"
)
const iteratorBatchSize = 100
const (
getAdminMethod = "getAdmin"
setAdminMethod = "setAdmin"
clearAdminMethod = "clearAdmin"
versionMethod = "version"
createSubjectMethod = "createSubject"
getSubjectMethod = "getSubject"
getSubjectExtendedMethod = "getSubjectExtended"
getSubjectKVMethod = "getSubjectKV"
listSubjectsMethod = "listSubjects"
addSubjectKeyMethod = "addSubjectKey"
removeSubjectKeyMethod = "removeSubjectKey"
getSubjectByKeyMethod = "getSubjectByKey"
getSubjectByNameMethod = "getSubjectByName"
getSubjectKeyByNameMethod = "getSubjectKeyByName"
setSubjectKVMethod = "setSubjectKV"
setSubjectNameMethod = "setSubjectName"
deleteSubjectKVMethod = "deleteSubjectKV"
deleteSubjectMethod = "deleteSubject"
createNamespaceMethod = "createNamespace"
getNamespaceMethod = "getNamespace"
getNamespaceExtendedMethod = "getNamespaceExtended"
listNamespacesMethod = "listNamespaces"
listNamespaceSubjectsMethod = "listNamespaceSubjects"
createGroupMethod = "createGroup"
getGroupMethod = "getGroup"
getGroupExtendedMethod = "getGroupExtended"
getGroupIDByNameMethod = "getGroupIDByName"
getGroupByNameMethod = "getGroupByName"
setGroupNameMethod = "setGroupName"
setGroupKVMethod = "setGroupKV"
deleteGroupKVMethod = "deleteGroupKV"
listGroupsMethod = "listGroups"
addSubjectToGroupMethod = "addSubjectToGroup"
removeSubjectFromGroupMethod = "removeSubjectFromGroup"
listGroupSubjectsMethod = "listGroupSubjects"
deleteGroupMethod = "deleteGroup"
)
// New creates a new Client. Options can be empty.
func New(ra actor.RPCActor, acc *wallet.Account, contract util.Uint160, opt Options) (*Client, error) {
signers := []actor.SignerAccount{{
Signer: transaction.Signer{
Account: acc.Contract.ScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc,
}}
if !opt.ProxyContract.Equals(util.Uint160{}) {
signers = append([]actor.SignerAccount{{
Signer: transaction.Signer{
Account: opt.ProxyContract,
Scopes: transaction.CustomContracts,
AllowedContracts: []util.Uint160{contract},
},
Account: notary.FakeContractAccount(opt.ProxyContract),
}}, signers...)
}
act, err := actor.New(ra, signers)
if err != nil {
return nil, fmt.Errorf("init actor: %w", err)
}
wtr := commonclient.NewWaiter(act, opt.Waiter)
return &Client{
act: act,
waiter: wtr,
contract: contract,
}, nil
}
// NewSimple creates a new Client using existing actor.Actor and default waiter options.
func NewSimple(act *actor.Actor, contract util.Uint160) *Client {
wtr := commonclient.NewWaiter(act, commonclient.WaiterOptions{})
return &Client{
act: act,
waiter: wtr,
contract: contract,
}
}
// StartTx inits transaction.
func (c Client) StartTx() *commonclient.Transaction {
return commonclient.NewTransaction(c.contract)
}
// SendTx sends provided transaction to blockchain.
func (c Client) SendTx(txn *commonclient.Transaction) (tx util.Uint256, vub uint32, err error) {
txBytes, err := txn.Bytes()
if err != nil {
return util.Uint256{}, 0, err
}
return c.act.SendRun(txBytes)
}
// Version returns version of contract.
func (c Client) Version() (int64, error) {
return unwrap.Int64(c.act.Call(c.contract, versionMethod))
}
// SetAdmin sets address that can perform write operations on contract.
// Must be invoked by committee.
func (c Client) SetAdmin(owner util.Uint160) (tx util.Uint256, vub uint32, err error) {
method, args := c.SetAdminCall(owner)
return c.act.SendCall(c.contract, method, args...)
}
// SetAdminCall provides args for SetAdmin to use in commonclient.Transaction.
func (c Client) SetAdminCall(owner util.Uint160) (method string, args []any) {
return setAdminMethod, []any{owner}
}
// ClearAdmin removes address that can perform write operations on contract.
// Must be invoked by committee.
func (c Client) ClearAdmin() (tx util.Uint256, vub uint32, err error) {
method, args := c.ClearAdminCall()
return c.act.SendCall(c.contract, method, args...)
}
// ClearAdminCall provides args for ClearAdmin to use in commonclient.Transaction.
func (c Client) ClearAdminCall() (method string, args []any) {
return clearAdminMethod, nil
}
// GetAdmin returns address that can perform write operations on contract.
// Second return values is true iff admin is set.
func (c Client) GetAdmin() (util.Uint160, bool, error) {
item, err := unwrap.Item(c.act.Call(c.contract, getAdminMethod))
if err != nil {
return util.Uint160{}, false, err
}
if item.Value() == nil {
return util.Uint160{}, false, nil
}
bs, err := item.TryBytes()
if err != nil {
return util.Uint160{}, true, err
}
u, err := util.Uint160DecodeBytesBE(bs)
return u, true, err
}
// CreateSubject creates new subject using public key and namespace.
// Must be invoked by contract owner.
func (c Client) CreateSubject(ns string, key *keys.PublicKey) (tx util.Uint256, vub uint32, err error) {
method, args := c.CreateSubjectCall(ns, key)
return c.act.SendCall(c.contract, method, args...)
}
// CreateSubjectCall provides args for CreateSubject to use in commonclient.Transaction.
func (c Client) CreateSubjectCall(ns string, key *keys.PublicKey) (method string, args []any) {
return createSubjectMethod, []any{ns, key.Bytes()}
}
// GetSubject gets subject by address.
func (c Client) GetSubject(addr util.Uint160) (*Subject, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getSubjectMethod, addr))
if err != nil {
return nil, err
}
return ParseSubject(items)
}
// GetSubjectExtended gets extended subject by address.
func (c Client) GetSubjectExtended(addr util.Uint160) (*SubjectExtended, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getSubjectExtendedMethod, addr))
if err != nil {
return nil, err
}
return ParseSubjectExtended(items)
}
// GetSubjectKV invokes `getSubjectKV` method of contract.
func (c Client) GetSubjectKV(addr util.Uint160, name string) (string, error) {
return unwrap.UTF8String(c.act.Call(c.contract, getSubjectKVMethod, addr, name))
}
// ListSubjects gets all subjects.
func (c Client) ListSubjects() ([]util.Uint160, error) {
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listSubjectsMethod))
}
// ListFullSubjects gets list of subjects.
func (c Client) ListFullSubjects(hashes []util.Uint160) ([]*Subject, error) {
w := io.NewBufBinWriter()
for _, hash := range hashes {
emit.AppCall(w.BinWriter, c.contract, getSubjectMethod, callflag.All, hash)
}
invoker, err := c.act.Run(w.Bytes())
if err != nil {
return nil, err
}
if invoker.State != vmstate.Halt.String() {
return nil, fmt.Errorf("invocation failed: %s", invoker.FaultException)
}
subjects := make([]*Subject, 0, len(invoker.Stack))
for _, item := range invoker.Stack {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid subject")
}
subject, err := ParseSubject(arr)
if err != nil {
return nil, err
}
subjects = append(subjects, subject)
}
return subjects, nil
}
// AddSubjectKey adds extra public key to subject.
// Must be invoked by contract owner.
func (c Client) AddSubjectKey(addr util.Uint160, key *keys.PublicKey) (tx util.Uint256, vub uint32, err error) {
method, args := c.AddSubjectKeyCall(addr, key)
return c.act.SendCall(c.contract, method, args...)
}
// AddSubjectKeyCall provides args for AddSubjectKey to use in commonclient.Transaction.
func (c Client) AddSubjectKeyCall(addr util.Uint160, key *keys.PublicKey) (method string, args []any) {
return addSubjectKeyMethod, []any{addr, key.Bytes()}
}
// RemoveSubjectKey removes extra public key from subject.
// Must be invoked by contract owner.
func (c Client) RemoveSubjectKey(addr util.Uint160, key *keys.PublicKey) (tx util.Uint256, vub uint32, err error) {
method, args := c.RemoveSubjectKeyCall(addr, key)
return c.act.SendCall(c.contract, method, args...)
}
// RemoveSubjectKeyCall provides args for RemoveSubjectKey to use in commonclient.Transaction.
func (c Client) RemoveSubjectKeyCall(addr util.Uint160, key *keys.PublicKey) (method string, args []any) {
return removeSubjectKeyMethod, []any{addr, key.Bytes()}
}
// SetSubjectKV updates subject kv map.
// Must be invoked by contract owner.
// You can use some predefined key constants: IAMPathKey, IAMARNKey, IAMCreatedTimeKey, IAMModifiedTimeKey.
func (c Client) SetSubjectKV(addr util.Uint160, key, val string) (tx util.Uint256, vub uint32, err error) {
method, args := c.SetSubjectKVCall(addr, key, val)
return c.act.SendCall(c.contract, method, args...)
}
// SetSubjectKVCall provides args for SetSubjectKV to use in commonclient.Transaction.
func (c Client) SetSubjectKVCall(addr util.Uint160, key, val string) (method string, args []any) {
return setSubjectKVMethod, []any{addr, key, val}
}
// SetSubjectName updates subject name.
// Must be invoked by contract owner.
func (c Client) SetSubjectName(addr util.Uint160, name string) (tx util.Uint256, vub uint32, err error) {
method, args := c.SetSubjectNameCall(addr, name)
return c.act.SendCall(c.contract, method, args...)
}
// SetSubjectNameCall provides args for SetSubjectName to use in commonclient.Transaction.
func (c Client) SetSubjectNameCall(addr util.Uint160, name string) (method string, args []any) {
return setSubjectNameMethod, []any{addr, name}
}
// DeleteSubjectKV removes subject kv map.
// Must be invoked by contract owner.
func (c Client) DeleteSubjectKV(addr util.Uint160, key string) (tx util.Uint256, vub uint32, err error) {
method, args := c.DeleteSubjectKVCall(addr, key)
return c.act.SendCall(c.contract, method, args...)
}
// DeleteSubjectKVCall provides args for DeleteSubjectKV to use in commonclient.Transaction.
func (c Client) DeleteSubjectKVCall(addr util.Uint160, key string) (method string, args []any) {
return deleteSubjectKVMethod, []any{addr, key}
}
// GetSubjectByKey gets subject by its primary or additional keys.
func (c Client) GetSubjectByKey(key *keys.PublicKey) (*Subject, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getSubjectByKeyMethod, key.Bytes()))
if err != nil {
return nil, err
}
return ParseSubject(items)
}
// GetSubjectByName gets subject by its name (namespace scope).
func (c Client) GetSubjectByName(namespace, subjectName string) (*Subject, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getSubjectByNameMethod, namespace, subjectName))
if err != nil {
return nil, err
}
return ParseSubject(items)
}
// GetSubjectKeyByName gets subject public key by its name (namespace scope).
func (c Client) GetSubjectKeyByName(namespace, subjectName string) (*keys.PublicKey, error) {
return unwrap.PublicKey(c.act.Call(c.contract, getSubjectKeyByNameMethod, namespace, subjectName))
}
// DeleteSubject delete subject and removes it from related namespaces and groups.
// Must be invoked by contract owner.
func (c Client) DeleteSubject(addr util.Uint160) (tx util.Uint256, vub uint32, err error) {
method, args := c.DeleteSubjectCall(addr)
return c.act.SendCall(c.contract, method, args...)
}
// DeleteSubjectCall provides args for DeleteSubject to use in commonclient.Transaction.
func (c Client) DeleteSubjectCall(addr util.Uint160) (method string, args []any) {
return deleteSubjectMethod, []any{addr}
}
// CreateNamespace create new namespace.
// Must be invoked by contract owner.
func (c Client) CreateNamespace(namespace string) (tx util.Uint256, vub uint32, err error) {
method, args := c.CreateNamespaceCall(namespace)
return c.act.SendCall(c.contract, method, args...)
}
// CreateNamespaceCall provides args for CreateNamespace to use in commonclient.Transaction.
func (c Client) CreateNamespaceCall(namespace string) (method string, args []any) {
return createNamespaceMethod, []any{namespace}
}
// GetNamespace gets namespace.
func (c Client) GetNamespace(namespace string) (*Namespace, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getNamespaceMethod, namespace))
if err != nil {
return nil, err
}
return ParseNamespace(items)
}
// GetNamespaceExtended gets extended namespace.
func (c Client) GetNamespaceExtended(namespace string) (*NamespaceExtended, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getNamespaceExtendedMethod, namespace))
if err != nil {
return nil, err
}
return ParseNamespaceExtended(items)
}
// ListNamespaces gets all namespaces.
func (c Client) ListNamespaces() ([]*Namespace, error) {
items, err := commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listNamespacesMethod)
if err != nil {
return nil, err
}
return ParseNamespaces(items)
}
// ListNamespaceSubjects gets all subjects from namespace.
func (c Client) ListNamespaceSubjects(namespace string) ([]util.Uint160, error) {
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listNamespaceSubjectsMethod, namespace))
}
// CreateGroup creates a new group in specific namespace.
// Must be invoked by contract owner.
func (c Client) CreateGroup(namespace, group string) (tx util.Uint256, vub uint32, err error) {
method, args := c.CreateGroupCall(namespace, group)
return c.act.SendCall(c.contract, method, args...)
}
// CreateGroupCall provides args for CreateGroup to use in commonclient.Transaction.
func (c Client) CreateGroupCall(namespace, group string) (method string, args []any) {
return createGroupMethod, []any{namespace, group}
}
// GetGroup gets group.
func (c Client) GetGroup(namespace string, groupID int64) (*Group, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getGroupMethod, namespace, groupID))
if err != nil {
return nil, err
}
return ParseGroup(items)
}
// GetGroupExtended gets extended group.
func (c Client) GetGroupExtended(namespace string, groupID int64) (*GroupExtended, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getGroupExtendedMethod, namespace, groupID))
if err != nil {
return nil, err
}
return ParseGroupExtended(items)
}
// SetGroupName updates subject name.
// Must be invoked by contract owner.
func (c Client) SetGroupName(namespace string, groupID int64, name string) (tx util.Uint256, vub uint32, err error) {
method, args := c.SetGroupNameCall(namespace, groupID, name)
return c.act.SendCall(c.contract, method, args...)
}
// SetGroupNameCall provides args for SetGroupName to use in commonclient.Transaction.
func (c Client) SetGroupNameCall(namespace string, groupID int64, name string) (method string, args []any) {
return setGroupNameMethod, []any{namespace, groupID, name}
}
// SetGroupKV updates group kv map.
// Must be invoked by contract owner.
// You can use some predefined key constants: IAMPathKey, IAMARNKey, IAMCreatedTimeKey, IAMModifiedTimeKey.
func (c Client) SetGroupKV(namespace string, groupID int64, key, val string) (tx util.Uint256, vub uint32, err error) {
method, args := c.SetGroupKVCall(namespace, groupID, key, val)
return c.act.SendCall(c.contract, method, args...)
}
// SetGroupKVCall provides args for SetGroupKV to use in commonclient.Transaction.
func (c Client) SetGroupKVCall(namespace string, groupID int64, key, val string) (method string, args []any) {
return setGroupKVMethod, []any{namespace, groupID, key, val}
}
// DeleteGroupKV removes group kv map.
// Must be invoked by contract owner.
func (c Client) DeleteGroupKV(namespace string, groupID int64, key string) (tx util.Uint256, vub uint32, err error) {
method, args := c.DeleteGroupKVCall(namespace, groupID, key)
return c.act.SendCall(c.contract, method, args...)
}
// DeleteGroupKVCall provides args for DeleteGroupKV to use in commonclient.Transaction.
func (c Client) DeleteGroupKVCall(namespace string, groupID int64, key string) (method string, args []any) {
return deleteGroupKVMethod, []any{namespace, groupID, key}
}
// GetGroupIDByName gets group id its name (namespace scope).
func (c Client) GetGroupIDByName(namespace, groupName string) (int64, error) {
return unwrap.Int64(c.act.Call(c.contract, getGroupIDByNameMethod, namespace, groupName))
}
// GetGroupByName gets group by its name (namespace scope).
func (c Client) GetGroupByName(namespace, groupName string) (*Group, error) {
items, err := unwrap.Array(c.act.Call(c.contract, getGroupByNameMethod, namespace, groupName))
if err != nil {
return nil, err
}
return ParseGroup(items)
}
// ListGroups gets all groups in specific namespace.
func (c Client) ListGroups(namespace string) ([]*Group, error) {
items, err := commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listGroupsMethod, namespace)
if err != nil {
return nil, err
}
return ParseGroups(items)
}
// AddSubjectToGroup adds a new subject to group.
// Must be invoked by contract owner.
func (c Client) AddSubjectToGroup(addr util.Uint160, groupID int64) (tx util.Uint256, vub uint32, err error) {
method, args := c.AddSubjectToGroupCall(addr, groupID)
return c.act.SendCall(c.contract, method, args...)
}
// AddSubjectToGroupCall provides args for AddSubjectToGroup to use in commonclient.Transaction.
func (c Client) AddSubjectToGroupCall(addr util.Uint160, groupID int64) (method string, args []any) {
return addSubjectToGroupMethod, []any{addr, groupID}
}
// RemoveSubjectFromGroup removes subject from group.
// Must be invoked by contract owner.
func (c Client) RemoveSubjectFromGroup(addr util.Uint160, groupID int64) (tx util.Uint256, vub uint32, err error) {
method, args := c.RemoveSubjectFromGroupCall(addr, groupID)
return c.act.SendCall(c.contract, method, args...)
}
// RemoveSubjectFromGroupCall provides args for RemoveSubjectFromGroup to use in commonclient.Transaction.
func (c Client) RemoveSubjectFromGroupCall(addr util.Uint160, groupID int64) (method string, args []any) {
return removeSubjectFromGroupMethod, []any{addr, groupID}
}
// ListGroupSubjects gets all subjects in specific group.
func (c Client) ListGroupSubjects(namespace string, groupID int64) ([]util.Uint160, error) {
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listGroupSubjectsMethod, namespace, groupID))
}
// DeleteGroup deletes group.
// Must be invoked by contract owner.
func (c Client) DeleteGroup(namespace string, groupID int64) (tx util.Uint256, vub uint32, err error) {
method, args := c.DeleteGroupCall(namespace, groupID)
return c.act.SendCall(c.contract, method, args...)
}
// DeleteGroupCall provides args for DeleteGroup to use in commonclient.Transaction.
func (c Client) DeleteGroupCall(namespace string, groupID int64) (method string, args []any) {
return deleteGroupMethod, []any{namespace, groupID}
}
// ListNonEmptyNamespaces gets namespaces that contain at least one subject.
func (c Client) ListNonEmptyNamespaces() ([]string, error) {
namespaces, err := c.ListNamespaces()
if err != nil {
return nil, err
}
var res []string
for _, namespace := range namespaces {
nsExt, err := c.GetNamespaceExtended(namespace.Name)
if err != nil {
return nil, err
}
if nsExt.SubjectsCount > 0 {
res = append(res, nsExt.Name)
}
}
return res, nil
}
// Wait waits until the specified transaction is accepted to the chain.
func (c Client) Wait(tx util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
return c.Waiter().Wait(tx, vub, err)
}
// Waiter returns underlying waiter.Waiter.
func (c Client) Waiter() waiter.Waiter {
return c.waiter
}
// ParseGroupID fetch groupID from stack after creating group method invocation.
func (c Client) ParseGroupID(res *state.AppExecResult, err error) (int64, error) {
if err != nil {
return 0, err
}
return unwrap.Int64(makeResFromAppExec(res))
}
// ListNonEmptyGroups gets groups that contain at least one subject.
func (c Client) ListNonEmptyGroups(namespace string) ([]string, error) {
groups, err := c.ListGroups(namespace)
if err != nil {
return nil, err
}
var res []string
for _, group := range groups {
groupExt, err := c.GetGroupExtended(namespace, group.ID)
if err != nil {
return nil, err
}
if groupExt.SubjectsCount > 0 {
res = append(res, groupExt.Name)
}
}
return res, nil
}

312
frostfsid/client/utils.go Normal file
View file

@ -0,0 +1,312 @@
package client
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
)
func UnwrapArrayOfUint160(items []stackitem.Item, err error) ([]util.Uint160, error) {
if err != nil {
return nil, err
}
return unwrap.ArrayOfUint160(makeValidRes(stackitem.NewArray(items)))
}
func ParseSubject(structArr []stackitem.Item) (*Subject, error) {
if len(structArr) < 5 {
return nil, errors.New("invalid response subject struct")
}
var (
err error
subj Subject
)
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
if err != nil {
return nil, err
}
if !structArr[1].Equals(stackitem.Null{}) {
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
if err != nil {
return nil, err
}
}
if !structArr[2].Equals(stackitem.Null{}) {
subj.Namespace, err = unwrap.UTF8String(makeValidRes(structArr[2]))
if err != nil {
return nil, err
}
}
if !structArr[2].Equals(stackitem.Null{}) {
subj.Name, err = unwrap.UTF8String(makeValidRes(structArr[3]))
if err != nil {
return nil, err
}
}
subj.KV, err = ParseMap(structArr[4])
if err != nil {
return nil, err
}
return &subj, nil
}
func ParseSubjectExtended(structArr []stackitem.Item) (*SubjectExtended, error) {
if len(structArr) < 6 {
return nil, errors.New("invalid response subject extended struct")
}
var (
err error
subj SubjectExtended
)
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
if err != nil {
return nil, err
}
if !structArr[1].Equals(stackitem.Null{}) {
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
if err != nil {
return nil, err
}
}
nsBytes, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
subj.Namespace = string(nsBytes)
nameBytes, err := structArr[3].TryBytes()
if err != nil {
return nil, err
}
subj.Name = string(nameBytes)
subj.KV, err = ParseMap(structArr[4])
if err != nil {
return nil, err
}
if !structArr[5].Equals(stackitem.Null{}) {
groupItems, ok := structArr[5].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid groups field")
}
subj.Groups, err = ParseGroups(groupItems)
if err != nil {
return nil, err
}
}
return &subj, nil
}
func ParseMap(item stackitem.Item) (map[string]string, error) {
if item.Equals(stackitem.Null{}) {
return nil, nil
}
metaMap, err := unwrap.Map(makeValidRes(item))
if err != nil {
return nil, err
}
meta, ok := metaMap.Value().([]stackitem.MapElement)
if !ok {
return nil, errors.New("invalid map type")
}
res := make(map[string]string, len(meta))
for _, element := range meta {
key, err := element.Key.TryBytes()
if err != nil {
return nil, err
}
val, err := element.Value.TryBytes()
if err != nil {
return nil, err
}
res[string(key)] = string(val)
}
return res, nil
}
func ParseNamespace(structArr []stackitem.Item) (*Namespace, error) {
if len(structArr) < 1 {
return nil, errors.New("invalid response namespace struct")
}
name, err := structArr[0].TryBytes()
if err != nil {
return nil, err
}
return &Namespace{Name: string(name)}, nil
}
func ParseNamespaceExtended(structArr []stackitem.Item) (*NamespaceExtended, error) {
if len(structArr) < 3 {
return nil, errors.New("invalid response namespace extended struct")
}
name, err := structArr[0].TryBytes()
if err != nil {
return nil, err
}
groupCount, err := structArr[1].TryInteger()
if err != nil {
return nil, err
}
subjectCount, err := structArr[2].TryInteger()
if err != nil {
return nil, err
}
return &NamespaceExtended{
Name: string(name),
GroupsCount: groupCount.Int64(),
SubjectsCount: subjectCount.Int64(),
}, nil
}
func ParseNamespaces(items []stackitem.Item) ([]*Namespace, error) {
var err error
res := make([]*Namespace, len(items))
for i := 0; i < len(items); i++ {
arr, ok := items[i].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid namespace type")
}
res[i], err = ParseNamespace(arr)
if err != nil {
return nil, err
}
}
return res, nil
}
func ParseGroup(structArr []stackitem.Item) (*Group, error) {
if len(structArr) < 4 {
return nil, errors.New("invalid response group struct")
}
groupID, err := structArr[0].TryInteger()
if err != nil {
return nil, err
}
name, err := structArr[1].TryBytes()
if err != nil {
return nil, err
}
namespace, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
kvs, err := ParseMap(structArr[3])
if err != nil {
return nil, err
}
return &Group{
ID: groupID.Int64(),
Name: string(name),
Namespace: string(namespace),
KV: kvs,
}, nil
}
func ParseGroupExtended(structArr []stackitem.Item) (*GroupExtended, error) {
if len(structArr) < 5 {
return nil, errors.New("invalid response group extended struct")
}
groupID, err := structArr[0].TryInteger()
if err != nil {
return nil, err
}
name, err := structArr[1].TryBytes()
if err != nil {
return nil, err
}
namespace, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
kvs, err := ParseMap(structArr[3])
if err != nil {
return nil, err
}
subjectCount, err := structArr[4].TryInteger()
if err != nil {
return nil, err
}
return &GroupExtended{
ID: groupID.Int64(),
Name: string(name),
Namespace: string(namespace),
KV: kvs,
SubjectsCount: subjectCount.Int64(),
}, nil
}
func ParseGroups(items []stackitem.Item) ([]*Group, error) {
var err error
res := make([]*Group, len(items))
for i := 0; i < len(items); i++ {
arr, ok := items[i].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid group type")
}
res[i], err = ParseGroup(arr)
if err != nil {
return nil, err
}
}
return res, nil
}
func makeValidRes(item stackitem.Item) (*result.Invoke, error) {
return &result.Invoke{
Stack: []stackitem.Item{item},
State: vmstate.Halt.String(),
}, nil
}
func makeResFromAppExec(res *state.AppExecResult) (*result.Invoke, error) {
return &result.Invoke{
Stack: res.Stack,
State: res.VMState.String(),
}, nil
}

134
frostfsid/config.yml Normal file
View file

@ -0,0 +1,134 @@
name: "Identity"
safemethods:
- "getAdmin"
- "getGroup"
- "getGroupExtended"
- "getGroupIDByName"
- "getGroupByName"
- "getNamespace"
- "getNamespaceExtended"
- "getSubjectKV"
- "getSubject"
- "getSubjectExtended"
- "getSubjectByKey"
- "getSubjectByName"
- "getSubjectKeyByName"
- "listGroups"
- "listGroupSubjects"
- "listNamespaces"
- "listNamespaceSubjects"
- "listSubjects"
- "version"
permissions:
- methods: ["update"]
events:
- name: CreateSubject
parameters:
- name: subjectAddress
type: Hash160
- name: AddSubjectKey
parameters:
- name: subjectAddress
type: Hash160
- name: subjectKey
type: PublicKey
- name: RemoveSubjectKey
parameters:
- name: subjectAddress
type: Hash160
- name: subjectKey
type: PublicKey
- name: SetSubjectName
parameters:
- name: subjectAddress
type: Hash160
- name: name
type: String
- name: SetSubjectKV
parameters:
- name: subjectAddress
type: Hash160
- name: key
type: String
- name: value
type: String
- name: DeleteSubjectKV
parameters:
- name: subjectAddress
type: Hash160
- name: key
type: String
- name: DeleteSubject
parameters:
- name: subjectAddress
type: Hash160
- name: CreateNamespace
parameters:
- name: namespace
type: String
- name: AddSubjectToNamespace
parameters:
- name: subjectAddress
type: Hash160
- name: namespace
type: String
- name: RemoveSubjectFromNamespace
parameters:
- name: subjectAddress
type: Hash160
- name: namespace
type: String
- name: CreateGroup
parameters:
- name: namespace
type: String
- name: group
type: String
- name: SetGroupName
parameters:
- name: namespace
type: String
- name: groupID
type: Integer
- name: name
type: String
- name: SetGroupKV
parameters:
- name: namespace
type: String
- name: groupID
type: Integer
- name: key
type: String
- name: value
type: String
- name: DeleteGroupKV
parameters:
- name: namespace
type: String
- name: groupID
type: Integer
- name: key
type: String
- name: AddSubjectToGroup
parameters:
- name: subjectAddress
type: Hash160
- name: namespace
type: String
- name: groupID
type: Integer
- name: RemoveSubjectFromGroup
parameters:
- name: subjectAddress
type: Hash160
- name: namespace
type: String
- name: groupID
type: Integer
- name: DeleteGroup
parameters:
- name: namespace
type: String
- name: groupID
type: Integer

27
frostfsid/doc.go Normal file
View file

@ -0,0 +1,27 @@
// Package frostfsid
/*
FrostFSID contract is a contract deployed in FrostFS sidechain.
# Contract notifications
FrostFSID contract does not produce notifications to process.
# Contract storage scheme
| Key | Value | Description |
|------------------------------------------------------------------------------|--------------------------------|-----------------------------------------------|
| `o` + [ owner address ] | []byte{1} | contract owners that can invoke write methods |
| `s` + [ subject address ] | Serialized Subject structure | subject into |
| `a` + [ pk address ] + [ subject address ] | []byte{1} | link extra public keys for subject |
| `n` + [ RIPEMD160 of namespace ] | Serialized Namespace structure | namespace info |
| `N` + [ RIPEMD160 of namespace ] + [ subject address ] | []byte{1} | subject that belongs to the namespace |
| `l` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of subject name ] | Subject public key | subject name to public key index |
| `g` + [ RIPEMD160 of namespace ] + [ 8 byte group id ] | Serialized Group structure | group into |
| `G` + [ RIPEMD160 of namespace ] + [ 8 byte group id ] + [ subject address ] | []byte{1} | subject that belongs to the group |
| `c` | Int | group id counter |
| `m` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of subject name ] | Serialized group id int | group name to group id index |
| `A` + [ subject address ] | bool | means that the wallet has been used |
*/
package frostfsid

File diff suppressed because it is too large Load diff

55
go.mod
View file

@ -1,9 +1,56 @@
module github.com/nspcc-dev/neofs-contract module git.frostfs.info/TrueCloudLab/frostfs-contract
go 1.14 go 1.22
require ( require (
github.com/google/uuid v1.6.0
github.com/mr-tron/base58 v1.2.0 github.com/mr-tron/base58 v1.2.0
github.com/nspcc-dev/neo-go v0.98.0 github.com/nspcc-dev/neo-go v0.106.3
github.com/stretchr/testify v1.7.0 github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.8.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/nspcc-dev/dbft v0.2.0 // indirect
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect
github.com/nspcc-dev/rfc6979 v0.2.1 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
github.com/twmb/murmur3 v1.1.8 // indirect
github.com/urfave/cli/v2 v2.27.2 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
) )

476
go.sum
View file

@ -1,432 +1,194 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0=
github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig=
github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I=
github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA=
github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs=
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nspcc-dev/dbft v0.2.0 h1:sDwsQES600OSIMncV176t2SX5OvB14lzeOAyKFOkbMI=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nspcc-dev/dbft v0.2.0/go.mod h1:oFE6paSC/yfFh9mcNU6MheMGOYXK9+sPiRk3YMoz49o=
github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk=
github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUOU=
github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA=
github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= github.com/nspcc-dev/neo-go v0.106.3 h1:HEyhgkjQY+HfBzotMJ12xx2VuOUphkngZ4kEkjvXDtE=
github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02 h1:JgRx27vfGw5WV5QbaNDy0iy2WD1XJO964wwAapaYKLg= github.com/nspcc-dev/neo-go v0.106.3/go.mod h1:3vEwJ2ld12N7HRGCaH/l/7EwopplC/+8XdIdPDNmD/M=
github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec h1:vDrbVXF2+2uP0RlkZmem3QYATcXCu9BzzGGCNsNcK7Q=
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no=
github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8=
github.com/nspcc-dev/neo-go v0.98.0 h1:yyW4sgY88/pLf0949qmgfkQXzRKC3CI/WyhqXNnwMd8= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 h1:mdxtlSU2I4oVZ/7AXTLKyz8uUPbDWikZw4DM8gvrddA=
github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12/go.mod h1:JdsEM1qgNukrWqgOBDChcYp8oY4XUzidcKaxY4hNJvQ=
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1 h1:CGA56mhLLduWRuMHcWujP5Ek+gAnXHk0WuIWkG65G1s= github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM=
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= github.com/nspcc-dev/tzhash v1.7.2 h1:iRXoa9TJqH/DQO7FFcqpq9BdruF9E7/xnFGlIghl5J4=
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/tzhash v1.7.2/go.mod h1:oHiH0qwmTsZkeVs7pvCS5cVXUaLhXxSFvnmnZ++ijm4=
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477 h1:JC+jt4ARpMV/L3OqPHBKxAmbMabU7RYl/L4KgBz3yPs=
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
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=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
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=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo= github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4 h1:UPou2i3GzKgi6igR+/0C5XyHKBngHxBp/CL5CQ0p3Zk=
golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9 h1:sEvmEcJVKBNUvgCUClbUQeHOAa9U0I2Ce1BooMvVCY4=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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-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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
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=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/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.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/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.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -1,95 +0,0 @@
/*
NeoFS contract is a contract deployed in NeoFS main chain.
NeoFS contract is an entry point to NeoFS users. This contract stores all NeoFS
related GAS, registers new Inner Ring candidates and produces notifications
to control side chain.
While main chain committee controls list of Alphabet nodes in native
RoleManagement contract, NeoFS can't change more than 1\3 keys at a time.
NeoFS contract contains actual list of Alphabet nodes in the side chain.
Network configuration also stored in NeoFS contract. All the changes in
configuration are mirrored in side chain with notifications.
Contract notifications
Deposit notification. This notification is produced when user transfers native
GAS to the NeoFS contract address. The same amount of NEOFS token will be
minted in Balance contract in the side chain.
Deposit:
- name: from
type: Hash160
- name: amount
type: Integer
- name: receiver
type: Hash160
- name: txHash
type: Hash256
Withdraw notification. This notification is produced when user wants to
withdraw GAS from internal NeoFS balance and has payed fee for that.
Withdraw:
- name: user
type: Hash160
- name: amount
type: Integer
- name: txHash
type: Hash256
Cheque notification. This notification is produced when NeoFS contract
successfully transferred assets back to the user after withdraw.
Cheque:
- name: id
type: ByteArray
- name: user
type: Hash160
- name: amount
type: Integer
- name: lockAccount
type: ByteArray
Bind notification. This notification is produced when user wants to bind
public keys with user account (OwnerID). Keys argument is array of ByteArray.
Bind:
- name: user
type: ByteArray
- name: keys
type: Array
Unbind notification. This notification is produced when user wants to unbind
public keys with user account (OwnerID). Keys argument is an array of ByteArray.
Unbind:
- name: user
type: ByteArray
- name: keys
type: Array
AlphabetUpdate notification. This notification is produced when Alphabet nodes
updated it's list in the contract. Alphabet argument is an array of ByteArray. It
contains public keys of new alphabet nodes.
AlphabetUpdate:
- name: id
type: ByteArray
- name: alphabet
type: Array
SetConfig notification. This notification is produced when Alphabet nodes update
NeoFS network configuration value.
SetConfig
- name: id
type: ByteArray
- name: key
type: ByteArray
- name: value
type: ByteArray
*/
package neofs

View file

@ -1,579 +0,0 @@
package neofs
import (
"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/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
)
type (
record struct {
key []byte
val []byte
}
)
const (
// CandidateFeeConfigKey contains fee for a candidate registration.
CandidateFeeConfigKey = "InnerRingCandidateFee"
withdrawFeeConfigKey = "WithdrawFee"
alphabetKey = "alphabet"
candidatesKey = "candidates"
notaryDisabledKey = "notary"
processingContractKey = "processingScriptHash"
maxBalanceAmount = 9000 // Max integer of Fixed12 in JSON bound (2**53-1)
maxBalanceAmountGAS = maxBalanceAmount * 1_0000_0000
// hardcoded value to ignore deposit notification in onReceive
ignoreDepositNotification = "\x57\x0b"
)
var (
configPrefix = []byte("config")
)
// _deploy sets up initial alphabet node keys.
func _deploy(data interface{}, isUpdate bool) {
if isUpdate {
ctx := storage.GetContext()
nodes := getNodes(ctx, candidatesKey)
storage.Delete(ctx, candidatesKey)
for i := range nodes {
key := append([]byte(candidatesKey), nodes[i].PublicKey...)
storage.Put(ctx, key, []byte{1})
}
return
}
args := data.(struct {
notaryDisabled bool
addrProc interop.Hash160
keys []interop.PublicKey
config [][]byte
})
ctx := storage.GetContext()
var irList []common.IRNode
if len(args.keys) == 0 {
panic("at least one alphabet key must be provided")
}
if len(args.addrProc) != interop.Hash160Len {
panic("incorrect length of contract script hash")
}
for i := 0; i < len(args.keys); i++ {
pub := args.keys[i]
if len(pub) != interop.PublicKeyCompressedLen {
panic("incorrect public key length")
}
irList = append(irList, common.IRNode{PublicKey: pub})
}
// initialize all storage slices
common.SetSerialized(ctx, alphabetKey, irList)
storage.Put(ctx, processingContractKey, args.addrProc)
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
common.InitVote(ctx)
runtime.Log("neofs contract notary disabled")
}
ln := len(args.config)
if ln%2 != 0 {
panic("bad configuration")
}
for i := 0; i < ln/2; i++ {
key := args.config[i*2]
val := args.config[i*2+1]
setConfig(ctx, key, val)
}
runtime.Log("neofs: contract initialized")
}
// Update method updates contract source code and manifest. Can be invoked
// only by side chain committee.
func Update(script []byte, manifest []byte, data interface{}) {
blockHeight := ledger.CurrentIndex()
alphabetKeys := roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight))
alphabetCommittee := common.Multiaddress(alphabetKeys, true)
common.CheckAlphabetWitness(alphabetCommittee)
contract.Call(interop.Hash160(management.Hash), "update",
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("neofs contract updated")
}
// AlphabetList returns array of alphabet node keys. Use in side chain notary
// disabled environment.
func AlphabetList() []common.IRNode {
ctx := storage.GetReadOnlyContext()
return getNodes(ctx, alphabetKey)
}
// AlphabetAddress returns 2\3n+1 multi signature address of alphabet nodes.
// Used in side chain notary disabled environment.
func AlphabetAddress() interop.Hash160 {
ctx := storage.GetReadOnlyContext()
return multiaddress(getNodes(ctx, alphabetKey))
}
// InnerRingCandidates returns array of structures that contain Inner Ring
// candidate node key.
func InnerRingCandidates() []common.IRNode {
ctx := storage.GetReadOnlyContext()
nodes := []common.IRNode{}
it := storage.Find(ctx, candidatesKey, storage.KeysOnly|storage.RemovePrefix)
for iterator.Next(it) {
pub := iterator.Value(it).([]byte)
nodes = append(nodes, common.IRNode{PublicKey: pub})
}
return nodes
}
// InnerRingCandidateRemove removes key from the list of Inner Ring candidates.
// Can be invoked by Alphabet nodes or candidate itself.
//
// Method does not return fee back to the candidate.
func InnerRingCandidateRemove(key interop.PublicKey) {
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
keyOwner := runtime.CheckWitness(key)
if !keyOwner {
if notaryDisabled {
alphabet = getNodes(ctx, alphabetKey)
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked by candidate or alphabet")
}
} else {
multiaddr := AlphabetAddress()
if !runtime.CheckWitness(multiaddr) {
panic("this method must be invoked by candidate or alphabet")
}
}
}
if notaryDisabled && !keyOwner {
threshold := len(alphabet)*2/3 + 1
id := append(key, []byte("delete")...)
hashID := crypto.Sha256(id)
n := common.Vote(ctx, hashID, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, hashID)
}
prefix := []byte(candidatesKey)
stKey := append(prefix, key...)
if storage.Get(ctx, stKey) != nil {
storage.Delete(ctx, stKey)
runtime.Log("candidate has been removed")
}
}
// InnerRingCandidateAdd adds key to the list of Inner Ring candidates.
// Can be invoked only by candidate itself.
//
// This method transfers fee from candidate to contract account.
// Fee value specified in NeoFS network config with the key InnerRingCandidateFee.
func InnerRingCandidateAdd(key interop.PublicKey) {
ctx := storage.GetContext()
common.CheckWitness(key)
stKey := append([]byte(candidatesKey), key...)
if storage.Get(ctx, stKey) != nil {
panic("candidate already in the list")
}
from := contract.CreateStandardAccount(key)
to := runtime.GetExecutingScriptHash()
fee := getConfig(ctx, CandidateFeeConfigKey).(int)
transferred := gas.Transfer(from, to, fee, []byte(ignoreDepositNotification))
if !transferred {
panic("failed to transfer funds, aborting")
}
storage.Put(ctx, stKey, []byte{1})
runtime.Log("candidate has been added")
}
// OnNEP17Payment is a callback for NEP-17 compatible native GAS contract.
// It takes no more than 9000.0 GAS. Native GAS has precision 8 and
// NeoFS balance contract has precision 12. Values bigger than 9000.0 can
// break JSON limits for integers when precision is converted.
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
rcv := data.(interop.Hash160)
if common.BytesEqual(rcv, []byte(ignoreDepositNotification)) {
return
}
if amount <= 0 {
common.AbortWithMessage("amount must be positive")
} else if maxBalanceAmountGAS < amount {
common.AbortWithMessage("out of max amount limit")
}
caller := runtime.GetCallingScriptHash()
if !common.BytesEqual(caller, interop.Hash160(gas.Hash)) {
common.AbortWithMessage("only GAS can be accepted for deposit")
}
switch len(rcv) {
case 20:
case 0:
rcv = from
default:
common.AbortWithMessage("invalid data argument, expected Hash160")
}
runtime.Log("funds have been transferred")
tx := runtime.GetScriptContainer()
runtime.Notify("Deposit", from, amount, rcv, tx.Hash)
}
// Withdraw initialize gas asset withdraw from NeoFS. Can be invoked only
// by the specified user.
//
// This method produces Withdraw notification to lock assets in side chain and
// transfers withdraw fee from user account to each Alphabet node. If notary
// is enabled in main chain, fee is transferred to Processing contract.
// Fee value specified in NeoFS network config with the key WithdrawFee.
func Withdraw(user interop.Hash160, amount int) {
if !runtime.CheckWitness(user) {
panic("you should be the owner of the wallet")
}
if amount < 0 {
panic("non positive amount number")
}
if amount > maxBalanceAmount {
panic("out of max amount limit")
}
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
// transfer fee to proxy contract to pay cheque invocation
fee := getConfig(ctx, withdrawFeeConfigKey).(int)
if notaryDisabled {
alphabet := getNodes(ctx, alphabetKey)
for _, node := range alphabet {
processingAddr := contract.CreateStandardAccount(node.PublicKey)
transferred := gas.Transfer(user, processingAddr, fee, []byte{})
if !transferred {
panic("failed to transfer withdraw fee, aborting")
}
}
} else {
processingAddr := storage.Get(ctx, processingContractKey).(interop.Hash160)
transferred := gas.Transfer(user, processingAddr, fee, []byte{})
if !transferred {
panic("failed to transfer withdraw fee, aborting")
}
}
// notify alphabet nodes
amount = amount * 100000000
tx := runtime.GetScriptContainer()
runtime.Notify("Withdraw", user, amount, tx.Hash)
}
// Cheque transfers GAS back to the user from contract account, if assets were
// successfully locked in NeoFS balance contract. Can be invoked only by
// Alphabet nodes.
//
// This method produces Cheque notification to burn assets in side chain.
func Cheque(id []byte, user interop.Hash160, amount int, lockAcc []byte) {
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = getNodes(ctx, alphabetKey)
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked by alphabet")
}
} else {
multiaddr := AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
from := runtime.GetExecutingScriptHash()
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
transferred := gas.Transfer(from, user, amount, nil)
if !transferred {
panic("failed to transfer funds, aborting")
}
runtime.Log("funds have been transferred")
runtime.Notify("Cheque", id, user, amount, lockAcc)
}
// Bind method produces notification to bind specified public keys in NeoFSID
// contract in side chain. Can be invoked only by specified user.
//
// This method produces Bind notification. Method panics if keys are not
// 33 byte long. User argument must be valid 20 byte script hash.
func Bind(user []byte, keys []interop.PublicKey) {
if !runtime.CheckWitness(user) {
panic("you should be the owner of the wallet")
}
for i := 0; i < len(keys); i++ {
pubKey := keys[i]
if len(pubKey) != interop.PublicKeyCompressedLen {
panic("incorrect public key size")
}
}
runtime.Notify("Bind", user, keys)
}
// Unbind method produces notification to unbind specified public keys in NeoFSID
// contract in side chain. Can be invoked only by specified user.
//
// This method produces Unbind notification. Method panics if keys are not
// 33 byte long. User argument must be valid 20 byte script hash.
func Unbind(user []byte, keys []interop.PublicKey) {
if !runtime.CheckWitness(user) {
panic("you should be the owner of the wallet")
}
for i := 0; i < len(keys); i++ {
pubKey := keys[i]
if len(pubKey) != interop.PublicKeyCompressedLen {
panic("incorrect public key size")
}
}
runtime.Notify("Unbind", user, keys)
}
// AlphabetUpdate updates list of alphabet nodes with provided list of
// public keys. Can be invoked only by alphabet nodes.
//
// This method used in notary disabled side chain environment. In this case
// actual alphabet list should be stored in the NeoFS contract.
func AlphabetUpdate(id []byte, args []interop.PublicKey) {
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
if len(args) == 0 {
panic("bad arguments")
}
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = getNodes(ctx, alphabetKey)
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked by alphabet")
}
} else {
multiaddr := AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
newAlphabet := []common.IRNode{}
for i := 0; i < len(args); i++ {
pubKey := args[i]
if len(pubKey) != interop.PublicKeyCompressedLen {
panic("invalid public key in alphabet list")
}
newAlphabet = append(newAlphabet, common.IRNode{
PublicKey: pubKey,
})
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
common.SetSerialized(ctx, alphabetKey, newAlphabet)
runtime.Notify("AlphabetUpdate", id, newAlphabet)
runtime.Log("alphabet list has been updated")
}
// Config returns configuration value of NeoFS configuration. If key does
// not exists, returns nil.
func Config(key []byte) interface{} {
ctx := storage.GetReadOnlyContext()
return getConfig(ctx, key)
}
// SetConfig key-value pair as a NeoFS runtime configuration value. Can be invoked
// only by Alphabet nodes.
func SetConfig(id, key, val []byte) {
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = getNodes(ctx, alphabetKey)
nodeKey = common.InnerRingInvoker(alphabet)
if len(key) == 0 {
panic("this method must be invoked by alphabet")
}
} else {
multiaddr := AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
setConfig(ctx, key, val)
runtime.Notify("SetConfig", id, key, val)
runtime.Log("configuration has been updated")
}
// ListConfig returns array of structures that contain key and value of all
// NeoFS configuration records. Key and value are both byte arrays.
func ListConfig() []record {
ctx := storage.GetReadOnlyContext()
var config []record
it := storage.Find(ctx, configPrefix, storage.None)
for iterator.Next(it) {
pair := iterator.Value(it).(struct {
key []byte
val []byte
})
r := record{key: pair.key[len(configPrefix):], val: pair.val}
config = append(config, r)
}
return config
}
// Version returns version of the contract.
func Version() int {
return common.Version
}
// getNodes returns deserialized slice of nodes from storage.
func getNodes(ctx storage.Context, key string) []common.IRNode {
data := storage.Get(ctx, key)
if data != nil {
return std.Deserialize(data.([]byte)).([]common.IRNode)
}
return []common.IRNode{}
}
// getConfig returns installed neofs configuration value or nil if it is not set.
func getConfig(ctx storage.Context, key interface{}) interface{} {
postfix := key.([]byte)
storageKey := append(configPrefix, postfix...)
return storage.Get(ctx, storageKey)
}
// setConfig sets neofs configuration value in the contract storage.
func setConfig(ctx storage.Context, key, val interface{}) {
postfix := key.([]byte)
storageKey := append(configPrefix, postfix...)
storage.Put(ctx, storageKey, val)
}
// multiaddress returns multi signature address from list of IRNode structures
// with m = 2/3n+1.
func multiaddress(n []common.IRNode) []byte {
threshold := len(n)*2/3 + 1
keys := []interop.PublicKey{}
for _, node := range n {
key := node.PublicKey
keys = append(keys, key)
}
return contract.CreateMultisigAccount(threshold, keys)
}

View file

@ -1,4 +0,0 @@
name: "NeoFS ID"
safemethods: ["key", "version"]
permissions:
- methods: ["update"]

View file

@ -1,20 +0,0 @@
/*
NeoFSID contract is a contract deployed in NeoFS side chain.
NeoFSID contract used to store connection between OwnerID and it's public keys.
OwnerID is a 25-byte N3 wallet address that can be produced from public key.
It is one-way conversion. In simple cases NeoFS verifies ownership by checking
signature and relation between public key and OwnerID.
In more complex cases, user can use public keys unrelated to OwnerID to maintain
secure access to the data. NeoFSID contract stores relation between OwnerID and
arbitrary public keys. Data owner can bind or unbind public key with it's account
by invoking Bind or Unbind methods of NeoFS contract in main chain. After that,
Alphabet nodes produce multi signed AddKey and RemoveKey invocations of NeoFSID
contract.
Contract notifications
NeoFSID contract does not produce notifications to process.
*/
package neofsid

View file

@ -1,245 +0,0 @@
package neofsid
import (
"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/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
)
type (
UserInfo struct {
Keys [][]byte
}
)
const (
ownerSize = 1 + interop.Hash160Len + 4
)
const (
netmapContractKey = "netmapScriptHash"
containerContractKey = "containerScriptHash"
notaryDisabledKey = "notary"
ownerKeysPrefix = 'o'
)
func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext()
if isUpdate {
it := storage.Find(ctx, []byte{}, storage.None)
for iterator.Next(it) {
kv := iterator.Value(it).([][]byte)
// V2 format
if len(kv[0]) == ownerSize {
info := std.Deserialize(kv[1]).(UserInfo)
key := append([]byte{ownerKeysPrefix}, kv[0]...)
for i := range info.Keys {
storage.Put(ctx, append(key, info.Keys[i]...), []byte{1})
}
storage.Delete(ctx, kv[0])
}
}
return
}
args := data.(struct {
notaryDisabled bool
addrNetmap interop.Hash160
addrContainer interop.Hash160
})
if len(args.addrNetmap) != interop.Hash160Len || len(args.addrContainer) != interop.Hash160Len {
panic("incorrect length of contract script hash")
}
storage.Put(ctx, netmapContractKey, args.addrNetmap)
storage.Put(ctx, containerContractKey, args.addrContainer)
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
common.InitVote(ctx)
runtime.Log("neofsid contract notary disabled")
}
runtime.Log("neofsid contract initialized")
}
// Update method updates contract source code and manifest. Can be invoked
// only by committee.
func Update(script []byte, manifest []byte, data interface{}) {
if !common.HasUpdateAccess() {
panic("only committee can update contract")
}
contract.Call(interop.Hash160(management.Hash), "update",
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("neofsid contract updated")
}
// AddKey binds list of provided public keys to OwnerID. Can be invoked only by
// Alphabet nodes.
//
// This method panics if OwnerID is not ownerSize byte or public key is not 33 byte long.
// If key is already bound, ignores it.
func AddKey(owner []byte, keys []interop.PublicKey) {
// V2 format
if len(owner) != ownerSize {
panic("incorrect owner")
}
for i := range keys {
if len(keys[i]) != interop.PublicKeyCompressedLen {
panic("incorrect public key")
}
}
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
indirectCall bool
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("invocation from non inner ring node")
}
indirectCall = common.FromKnownContract(
ctx,
runtime.GetCallingScriptHash(),
containerContractKey)
if indirectCall {
threshold := len(alphabet)*2/3 + 1
id := invokeIDKeys(owner, keys, []byte("add"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
for i := range keys {
stKey := append(ownerKey, keys[i]...)
storage.Put(ctx, stKey, []byte{1})
}
runtime.Log("key bound to the owner")
}
// RemoveKey unbinds provided public keys from OwnerID. Can be invoked only by
// Alphabet nodes.
//
// This method panics if OwnerID is not ownerSize byte or public key is not 33 byte long.
// If key is already unbound, ignores it.
func RemoveKey(owner []byte, keys []interop.PublicKey) {
// V2 format
if len(owner) != ownerSize {
panic("incorrect owner")
}
for i := range keys {
if len(keys[i]) != interop.PublicKeyCompressedLen {
panic("incorrect public key")
}
}
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("invocation from non inner ring node")
}
threshold := len(alphabet)*2/3 + 1
id := invokeIDKeys(owner, keys, []byte("remove"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
} else {
multiaddr := common.AlphabetAddress()
if !runtime.CheckWitness(multiaddr) {
panic("invocation from non inner ring node")
}
}
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
for i := range keys {
stKey := append(ownerKey, keys[i]...)
storage.Delete(ctx, stKey)
}
}
// Key method returns list of 33-byte public keys bound with OwnerID.
//
// This method panics if owner is not ownerSize byte long.
func Key(owner []byte) [][]byte {
// V2 format
if len(owner) != ownerSize {
panic("incorrect owner")
}
ctx := storage.GetReadOnlyContext()
ownerKey := append([]byte{ownerKeysPrefix}, owner...)
info := getUserInfo(ctx, ownerKey)
return info.Keys
}
// Version returns version of the contract.
func Version() int {
return common.Version
}
func getUserInfo(ctx storage.Context, key interface{}) UserInfo {
it := storage.Find(ctx, key, storage.KeysOnly|storage.RemovePrefix)
pubs := [][]byte{}
for iterator.Next(it) {
pub := iterator.Value(it).([]byte)
pubs = append(pubs, pub)
}
return UserInfo{Keys: pubs}
}
func invokeIDKeys(owner []byte, keys []interop.PublicKey, prefix []byte) []byte {
prefix = append(prefix, owner...)
for i := range keys {
prefix = append(prefix, keys[i]...)
}
return crypto.Sha256(prefix)
}

View file

@ -1,5 +1,13 @@
name: "NeoFS Netmap" name: "Netmap"
safemethods: ["innerRingList", "epoch", "netmap", "netmapCandidates", "snapshot", "snapshotByEpoch", "config", "listConfig", "version"] safemethods:
- "config"
- "epoch"
- "listConfig"
- "netmap"
- "netmapCandidates"
- "snapshot"
- "snapshotByEpoch"
- "version"
permissions: permissions:
- methods: ["update", "newEpoch"] - methods: ["update", "newEpoch"]
events: events:
@ -7,12 +15,22 @@ events:
parameters: parameters:
- name: nodeInfo - name: nodeInfo
type: ByteArray type: ByteArray
- name: AddPeerSuccess
parameters:
- name: publicKey
type: PublicKey
- name: UpdateState - name: UpdateState
parameters: parameters:
- name: state - name: state
type: Integer type: Integer
- name: publicKey - name: publicKey
type: PublicKey type: PublicKey
- name: UpdateStateSuccess
parameters:
- name: publicKey
type: PublicKey
- name: state
type: Integer
- name: NewEpoch - name: NewEpoch
parameters: parameters:
- name: epoch - name: epoch

View file

@ -1,34 +1,45 @@
/* /*
Netmap contract is a contract deployed in NeoFS side chain. Netmap contract is a contract deployed in FrostFS sidechain.
Netmap contract stores and manages NeoFS network map, Storage node candidates Netmap contract stores and manages FrostFS network map, Storage node candidates
and epoch number counter. In notary disabled environment, contract also stores and epoch number counter.
list of Inner Ring node keys.
Contract notifications # Contract notifications
AddPeer notification. This notification is produced when Storage node sends AddPeer notification. This notification is produced when a Storage node sends
bootstrap request by invoking AddPeer method. a bootstrap request by invoking AddPeer method.
AddPeer AddPeer
- name: nodeInfo - name: nodeInfo
type: ByteArray type: ByteArray
UpdateState notification. This notification is produced when Storage node wants UpdateState notification. This notification is produced when a Storage node wants
to change it's state (go offline) by invoking UpdateState method. Supported to change its state (go offline) by invoking UpdateState method. Supported
states: (2) -- offline. states: (2) -- offline.
UpdateState UpdateState
- name: state - name: state
type: Integer type: Integer
- name: publicKey - name: publicKey
type: PublicKey type: PublicKey
NewEpoch notification. This notification is produced when new epoch is applied NewEpoch notification. This notification is produced when a new epoch is applied
in the network by invoking NewEpoch method. in the network by invoking NewEpoch method.
NewEpoch NewEpoch
- name: epoch - name: epoch
type: Integer type: Integer
# Contract storage scheme
| Key | Value | Description |
|-----------------------------|------------|-----------------------------------|
| `snapshotCount` | int | snapshot count |
| `snapshotEpoch` | int | snapshot epoch |
| `snapshotBlock` | int | snapshot block |
| `snapshot_` + snapshotNum | ByteArray | serialized '[]Node' array |
| `snapshotCurrent` | int | current snapshot |
| `balanceScriptHash` | Hash160 | balance contract hash |
| `containerScriptHash` | Hash160 | container contract hash |
*/ */
package netmap package netmap

View file

@ -1,44 +1,58 @@
package netmap package netmap
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
) )
type ( // NodeState is an enumeration for node states.
storageNode struct { type NodeState int
info []byte
}
netmapNode struct { // Various Node states
node storageNode const (
state nodeState _ NodeState = iota
}
nodeState int // NodeStateOnline stands for nodes that are in full network and
// operational availability.
NodeStateOnline
record struct { // NodeStateOffline stands for nodes that are in network unavailability.
key []byte NodeStateOffline
val []byte
} // NodeStateMaintenance stands for nodes under maintenance with partial
// network availability.
NodeStateMaintenance
) )
// Node groups data related to FrostFS storage nodes registered in the FrostFS
// network. The information is stored in the current contract.
type Node struct {
// Information about the node encoded according to the FrostFS binary
// protocol.
BLOB []byte
// Current node state.
State NodeState
}
const ( const (
notaryDisabledKey = "notary" innerRingKey = "innerring"
innerRingKey = "innerring"
snapshot0Key = "snapshotCurrent" // DefaultSnapshotCount contains the number of previous snapshots stored by this contract.
snapshot1Key = "snapshotPrevious" // Must be less than 255.
snapshotEpoch = "snapshotEpoch" DefaultSnapshotCount = 10
snapshotBlockKey = "snapshotBlock" snapshotCountKey = "snapshotCount"
snapshotKeyPrefix = "snapshot_"
snapshotCurrentIDKey = "snapshotCurrent"
snapshotEpoch = "snapshotEpoch"
snapshotBlockKey = "snapshotBlock"
containerContractKey = "containerScriptHash" containerContractKey = "containerScriptHash"
balanceContractKey = "balanceScriptHash" balanceContractKey = "balanceScriptHash"
@ -46,28 +60,21 @@ const (
cleanupEpochMethod = "newEpoch" cleanupEpochMethod = "newEpoch"
) )
const (
// V2 format
_ nodeState = iota
onlineState
offlineState
)
var ( var (
configPrefix = []byte("config") configPrefix = []byte("config")
candidatePrefix = []byte("candidate") candidatePrefix = []byte("candidate")
) )
// _deploy function sets up initial list of inner ring public keys. // _deploy function sets up initial list of inner ring public keys.
func _deploy(data interface{}, isUpdate bool) { func _deploy(data any, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
var args = data.(struct { args := data.(struct {
notaryDisabled bool addrBalance interop.Hash160
addrBalance interop.Hash160 addrContainer interop.Hash160
addrContainer interop.Hash160 keys []interop.PublicKey
keys []interop.PublicKey config [][]byte
config [][]byte version int
}) })
ln := len(args.config) ln := len(args.config)
@ -83,6 +90,7 @@ func _deploy(data interface{}, isUpdate bool) {
} }
if isUpdate { if isUpdate {
common.CheckVersion(args.version)
return return
} }
@ -91,273 +99,153 @@ func _deploy(data interface{}, isUpdate bool) {
} }
// epoch number is a little endian int, it doesn't need to be serialized // epoch number is a little endian int, it doesn't need to be serialized
storage.Put(ctx, snapshotCountKey, DefaultSnapshotCount)
storage.Put(ctx, snapshotEpoch, 0) storage.Put(ctx, snapshotEpoch, 0)
storage.Put(ctx, snapshotBlockKey, 0) storage.Put(ctx, snapshotBlockKey, 0)
common.SetSerialized(ctx, snapshot0Key, []netmapNode{}) prefix := []byte(snapshotKeyPrefix)
common.SetSerialized(ctx, snapshot1Key, []netmapNode{}) for i := 0; i < DefaultSnapshotCount; i++ {
common.SetSerialized(ctx, append(prefix, byte(i)), []Node{})
}
storage.Put(ctx, snapshotCurrentIDKey, 0)
storage.Put(ctx, balanceContractKey, args.addrBalance) storage.Put(ctx, balanceContractKey, args.addrBalance)
storage.Put(ctx, containerContractKey, args.addrContainer) storage.Put(ctx, containerContractKey, args.addrContainer)
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
var irList []common.IRNode
for i := 0; i < len(args.keys); i++ {
key := args.keys[i]
irList = append(irList, common.IRNode{PublicKey: key})
}
common.SetSerialized(ctx, innerRingKey, irList)
common.InitVote(ctx)
runtime.Log("netmap contract notary disabled")
}
runtime.Log("netmap contract initialized") runtime.Log("netmap contract initialized")
} }
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. It can be invoked
// only by committee. // only by committee.
func Update(script []byte, manifest []byte, data interface{}) { func Update(script []byte, manifest []byte, data any) {
if !common.HasUpdateAccess() { if !common.HasUpdateAccess() {
panic("only committee can update contract") panic("only committee can update contract")
} }
contract.Call(interop.Hash160(management.Hash), "update", management.UpdateWithData(script, manifest, common.AppendVersion(data))
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("netmap contract updated") runtime.Log("netmap contract updated")
} }
// InnerRingList method returns slice of structures that contains public key of // AddPeerIR accepts Alphabet calls in the notary-enabled contract setting and
// Inner Ring node. Should be used only in notary disabled environment. // behaves similar to AddPeer in the notary-disabled one.
// //
// If notary enabled, then look to NeoFSAlphabet role in native RoleManagement // AddPeerIR MUST NOT be called in notary-disabled contract setting.
// contract of the side chain. // AddPeerIR MUST be called by the Alphabet member only.
func InnerRingList() []common.IRNode { func AddPeerIR(nodeInfo []byte) {
ctx := storage.GetReadOnlyContext() ctx := storage.GetContext()
return getIRNodes(ctx)
common.CheckAlphabetWitness()
publicKey := nodeInfo[2:35] // V2 format: offset:2, len:33
addToNetmap(ctx, publicKey, Node{
BLOB: nodeInfo,
State: NodeStateOnline,
})
} }
// UpdateInnerRing method updates list of Inner Ring node keys. Should be used // AddPeer accepts information about the network map candidate in the FrostFS
// only in notary disabled environment. Can be invoked only by Alphabet nodes. // binary protocol format and does nothing. Keep method because storage node
// // creates a notary transaction with this method, which produces a notary
// If notary enabled, then update NeoFSAlphabet role in native RoleManagement // notification (implicit here).
// contract of the side chain. Use notary service to collect multi signature. func AddPeer(nodeInfo []byte) {
func UpdateInnerRing(keys []interop.PublicKey) { // V2 format - offset:2, len:33
ctx := storage.GetContext() common.CheckWitness(nodeInfo[2:35])
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked by alphabet nodes")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
var irList []common.IRNode
for i := 0; i < len(keys); i++ {
key := keys[i]
irList = append(irList, common.IRNode{PublicKey: key})
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
id := keysID(keys, []byte("updateIR"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
runtime.Log("inner ring list updated")
common.SetSerialized(ctx, innerRingKey, irList)
}
// Register method tries to add new candidate to the network map by
// emitting AddPeer notification. Should be invoked by the registree.
func Register(nodeInfo []byte) {
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
if notaryDisabled {
panic("Register should only be called in notary-enabled environment")
}
common.CheckAlphabetWitness(common.AlphabetAddress())
addToNetmap(ctx, storageNode{info: nodeInfo})
return return
} }
// AddPeer method adds new candidate to the next network map if it was invoked // updates state of the network map candidate by its public key in the contract
// by Alphabet node. If it was invoked by node candidate, it produces AddPeer // storage, and throws UpdateStateSuccess notification after this.
// notification. Otherwise method throws panic.
// //
// If the candidate already exists, it's info is updated. // State MUST be from the NodeState enum.
// NodeInfo argument contains stable marshaled version of netmap.NodeInfo func updateCandidateState(ctx storage.Context, publicKey interop.PublicKey, state NodeState) {
// structure. switch state {
func AddPeer(nodeInfo []byte) { case NodeStateOffline:
ctx := storage.GetContext() removeFromNetmap(ctx, publicKey)
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool) runtime.Log("remove storage node from the network map")
case NodeStateOnline, NodeStateMaintenance:
var ( // for invocation collection without notary updateNetmapState(ctx, publicKey, state)
alphabet []common.IRNode runtime.Log("update state of the network map candidate")
nodeKey []byte default:
) panic("unsupported state")
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
} }
// If notary is enabled or caller is not an alphabet node, runtime.Notify("UpdateStateSuccess", publicKey, state)
// just emit the notification for alphabet.
if !notaryDisabled || len(nodeKey) == 0 {
// V2 format
publicKey := nodeInfo[2:35] // offset:2, len:33
common.CheckWitness(publicKey)
runtime.Notify("AddPeer", nodeInfo)
return
}
candidate := storageNode{
info: nodeInfo,
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
rawCandidate := std.Serialize(candidate)
id := crypto.Sha256(rawCandidate)
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
addToNetmap(ctx, candidate)
} }
// UpdateState method updates state of node from the network map candidate list // UpdateState accepts new state to be assigned to network map candidate
// if it was invoked by Alphabet node. If it was invoked by public key owner, // identified by the given public key, identifies the signer.
// then it produces UpdateState notification. Otherwise method throws panic. // Applicable only for notary-enabled environment.
// //
// State argument defines node state. The only supported state now is (2) -- // Signers:
// offline state. Node is removed from network map candidate list.
// //
// Method panics when invoked with unsupported states. // (a) candidate himself only, if provided public key corresponds to the signer
func UpdateState(state int, publicKey interop.PublicKey) { // (b) Alphabet member only
// (ab) both candidate and Alphabet member
// (c) others
//
// UpdateState case-by-case behavior:
//
// (a) panics
// (b) panics
// (ab) updates candidate's state in the contract storage (*), and throws
// UpdateStateSuccess with the provided key and new state
// (c) panics
//
// (*) Candidate is removed from the candidate set if state is NodeStateOffline.
// Any other state is written into candidate's descriptor in the contract storage.
// If requested candidate is missing, panic occurs. Throws UpdateStateSuccess
// notification on success.
//
// State MUST be from the NodeState enum. Public key MUST be
// interop.PublicKeyCompressedLen bytes.
func UpdateState(state NodeState, publicKey interop.PublicKey) {
if len(publicKey) != interop.PublicKeyCompressedLen { if len(publicKey) != interop.PublicKeyCompressedLen {
panic("incorrect public key") panic("incorrect public key")
} }
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckWitness(publicKey)
alphabet []common.IRNode common.CheckAlphabetWitness()
nodeKey []byte
)
if notaryDisabled { updateCandidateState(ctx, publicKey, state)
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
common.CheckWitness(publicKey)
runtime.Notify("UpdateState", state, publicKey)
return
}
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{state, publicKey}, []byte("update"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
} else {
multiaddr := common.AlphabetAddress()
common.CheckWitness(publicKey)
common.CheckAlphabetWitness(multiaddr)
}
switch nodeState(state) {
case offlineState:
removeFromNetmap(ctx, publicKey)
runtime.Log("remove storage node from the network map")
default:
panic("unsupported state")
}
} }
// NewEpoch method changes epoch number up to provided epochNum argument. Can // UpdateStateIR accepts Alphabet calls in the notary-enabled contract setting
// be invoked only by Alphabet nodes. If provided epoch number is less or equal // and behaves similar to UpdateState, but does not require candidate's
// current epoch number, method throws panic. // signature presence.
// //
// When epoch number updated, contract sets storage node candidates as current // UpdateStateIR MUST NOT be called in notary-disabled contract setting.
// network map. Also contract invokes NewEpoch method on Balance and Container // UpdateStateIR MUST be called by the Alphabet member only.
func UpdateStateIR(state NodeState, publicKey interop.PublicKey) {
ctx := storage.GetContext()
common.CheckAlphabetWitness()
updateCandidateState(ctx, publicKey, state)
}
// NewEpoch method changes the epoch number up to the provided epochNum argument. It can
// be invoked only by Alphabet nodes. If provided epoch number is less than the
// current epoch number or equals it, the method throws panic.
//
// When epoch number is updated, the contract sets storage node candidates as the current
// network map. The contract also invokes NewEpoch method on Balance and Container
// contracts. // contracts.
// //
// Produces NewEpoch notification. // It produces NewEpoch notification.
func NewEpoch(epochNum int) { func NewEpoch(epochNum int) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("this method must be invoked by inner ring nodes")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
id := common.InvokeID([]interface{}{epochNum}, []byte("epoch"))
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
currentEpoch := storage.Get(ctx, snapshotEpoch).(int) currentEpoch := storage.Get(ctx, snapshotEpoch).(int)
if epochNum <= currentEpoch { if epochNum <= currentEpoch {
panic("invalid epoch") // ignore invocations with invalid epoch panic("invalid epoch") // ignore invocations with invalid epoch
} }
data0snapshot := getSnapshot(ctx, snapshot0Key) dataOnlineState := filterNetmap(ctx)
dataOnlineState := filterNetmap(ctx, onlineState)
runtime.Log("process new epoch") runtime.Log("process new epoch")
@ -365,11 +253,12 @@ func NewEpoch(epochNum int) {
storage.Put(ctx, snapshotEpoch, epochNum) storage.Put(ctx, snapshotEpoch, epochNum)
storage.Put(ctx, snapshotBlockKey, ledger.CurrentIndex()) storage.Put(ctx, snapshotBlockKey, ledger.CurrentIndex())
// put actual snapshot into previous snapshot id := storage.Get(ctx, snapshotCurrentIDKey).(int)
common.SetSerialized(ctx, snapshot1Key, data0snapshot) id = (id + 1) % getSnapshotCount(ctx)
storage.Put(ctx, snapshotCurrentIDKey, id)
// put netmap into actual snapshot // put netmap into actual snapshot
common.SetSerialized(ctx, snapshot0Key, dataOnlineState) common.SetSerialized(ctx, snapshotKeyPrefix+string([]byte{byte(id)}), dataOnlineState)
// make clean up routines in other contracts // make clean up routines in other contracts
cleanup(ctx, epochNum) cleanup(ctx, epochNum)
@ -377,116 +266,190 @@ func NewEpoch(epochNum int) {
runtime.Notify("NewEpoch", epochNum) runtime.Notify("NewEpoch", epochNum)
} }
// Epoch method returns current epoch number. // Epoch method returns the current epoch number.
func Epoch() int { func Epoch() int {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return storage.Get(ctx, snapshotEpoch).(int) return storage.Get(ctx, snapshotEpoch).(int)
} }
// LastEpochBlock method returns block number when current epoch was applied. // LastEpochBlock method returns the block number when the current epoch was applied.
func LastEpochBlock() int { func LastEpochBlock() int {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return storage.Get(ctx, snapshotBlockKey).(int) return storage.Get(ctx, snapshotBlockKey).(int)
} }
// Netmap method returns list of structures that contain byte array of stable // Netmap returns set of information about the storage nodes representing a network
// marshalled netmap.NodeInfo structure. These structure contain Storage nodes // map in the current epoch.
// of current epoch. //
func Netmap() []storageNode { // Current state of each node is represented in the State field. It MAY differ
// with the state encoded into BLOB field, in this case binary encoded state
// MUST NOT be processed.
func Netmap() []Node {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return getSnapshot(ctx, snapshot0Key) id := storage.Get(ctx, snapshotCurrentIDKey).(int)
return getSnapshot(ctx, snapshotKeyPrefix+string([]byte{byte(id)}))
} }
// Snapshot method returns list of structures that contain node state // NetmapCandidates returns set of information about the storage nodes
// and byte array of stable marshalled netmap.NodeInfo structure. // representing candidates for the network map in the coming epoch.
// These structure contain Storage node candidates for next epoch. //
func NetmapCandidates() []netmapNode { // Current state of each node is represented in the State field. It MAY differ
// with the state encoded into BLOB field, in this case binary encoded state
// MUST NOT be processed.
func NetmapCandidates() []Node {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return getNetmapNodes(ctx) return getNetmapNodes(ctx)
} }
// Snapshot method returns list of structures that contain node state // Snapshot returns set of information about the storage nodes representing a network
// (online: 1) and byte array of stable marshalled netmap.NodeInfo structure. // map in (current-diff)-th epoch.
// These structure contain Storage nodes of specified epoch.
// //
// Netmap contract contains only two recent network map snapshot: current and // Diff MUST NOT be negative. Diff MUST be less than maximum number of network
// previous epoch. For diff bigger than 1 or less than 0 method throws panic. // map snapshots stored in the contract. The limit is a contract setting,
func Snapshot(diff int) []storageNode { // DefaultSnapshotCount by default. See UpdateSnapshotCount for details.
var key string //
// Current state of each node is represented in the State field. It MAY differ
switch diff { // with the state encoded into BLOB field, in this case binary encoded state
case 0: // MUST NOT be processed.
key = snapshot0Key func Snapshot(diff int) []Node {
case 1: ctx := storage.GetReadOnlyContext()
key = snapshot1Key count := getSnapshotCount(ctx)
default: if diff < 0 || count <= diff {
panic("incorrect diff") panic("incorrect diff")
} }
ctx := storage.GetReadOnlyContext() id := storage.Get(ctx, snapshotCurrentIDKey).(int)
needID := (id - diff + count) % count
key := snapshotKeyPrefix + string([]byte{byte(needID)})
return getSnapshot(ctx, key) return getSnapshot(ctx, key)
} }
// SnapshotByEpoch method returns list of structures that contain node state func getSnapshotCount(ctx storage.Context) int {
// (online: 1) and byte array of stable marshalled netmap.NodeInfo structure. return storage.Get(ctx, snapshotCountKey).(int)
// These structure contain Storage nodes of specified epoch. }
// UpdateSnapshotCount updates the number of the stored snapshots.
// If a new number is less than the old one, old snapshots are removed.
// Otherwise, history is extended with empty snapshots, so
// `Snapshot` method can return invalid results for `diff = new-old` epochs
// until `diff` epochs have passed.
// //
// Netmap contract contains only two recent network map snapshot: current and // Count MUST NOT be negative.
// previous epoch. For all others epoch method throws panic. func UpdateSnapshotCount(count int) {
func SnapshotByEpoch(epoch int) []storageNode { common.CheckAlphabetWitness()
if count < 0 {
panic("count must be positive")
}
ctx := storage.GetContext()
curr := getSnapshotCount(ctx)
if curr == count {
panic("count has not changed")
}
storage.Put(ctx, snapshotCountKey, count)
id := storage.Get(ctx, snapshotCurrentIDKey).(int)
var delStart, delFinish int
if curr < count {
// Increase history size.
//
// Old state (N = count, K = curr, E = current index, C = current epoch)
// KEY INDEX: 0 | 1 | ... | E | E+1 | ... | K-1 | ... | N-1
// EPOCH : C-E | C-E+1 | ... | C | C-K+1 | ... | C-E-1 |
//
// New state:
// KEY INDEX: 0 | 1 | ... | E | E+1 | ... | K-1 | ... | N-1
// EPOCH : C-E | C-E+1 | ... | C | nil | ... | . | ... | C-E-1
//
// So we need to move tail snapshots N-K keys forward,
// i.e. from E+1 .. K to N-K+E+1 .. N
diff := count - curr
lower := diff + id + 1
for k := count - 1; k >= lower; k-- {
moveSnapshot(ctx, k-diff, k)
}
delStart, delFinish = id+1, id+1+diff
if curr < delFinish {
delFinish = curr
}
} else {
// Decrease history size.
//
// Old state (N = curr, K = count)
// KEY INDEX: 0 | 1 | ... K1 ... | E | E+1 | ... K2-1 ... | N-1
// EPOCH : C-E | C-E+1 | ... .. ... | C | C-N+1 | ... ... ... | C-E-1
var step, start int
if id < count {
// K2 case, move snapshots from E+1+N-K .. N-1 range to E+1 .. K-1
// New state:
// KEY INDEX: 0 | 1 | ... | E | E+1 | ... | K-1
// EPOCH : C-E | C-E+1 | ... | C | C-K+1 | ... | C-E-1
step = curr - count
start = id + 1
} else {
// New state:
// KEY INDEX: 0 | 1 | ... | K-1
// EPOCH : C-K+1 | C-K+2 | ... | C
// K1 case, move snapshots from E-K+1 .. E range to 0 .. K-1
// AND replace current id with K-1
step = id - count + 1
storage.Put(ctx, snapshotCurrentIDKey, count-1)
}
for k := start; k < count; k++ {
moveSnapshot(ctx, k+step, k)
}
delStart, delFinish = count, curr
}
for k := delStart; k < delFinish; k++ {
key := snapshotKeyPrefix + string([]byte{byte(k)})
storage.Delete(ctx, key)
}
}
func moveSnapshot(ctx storage.Context, from, to int) {
keyFrom := snapshotKeyPrefix + string([]byte{byte(from)})
keyTo := snapshotKeyPrefix + string([]byte{byte(to)})
data := storage.Get(ctx, keyFrom)
storage.Put(ctx, keyTo, data)
}
// SnapshotByEpoch returns set of information about the storage nodes representing
// a network map in the given epoch.
//
// Behaves like Snapshot: it is called after difference with the current epoch is
// calculated.
func SnapshotByEpoch(epoch int) []Node {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
currentEpoch := storage.Get(ctx, snapshotEpoch).(int) currentEpoch := storage.Get(ctx, snapshotEpoch).(int)
return Snapshot(currentEpoch - epoch) return Snapshot(currentEpoch - epoch)
} }
// Config returns configuration value of NeoFS configuration. If key does // Config returns configuration value of FrostFS configuration. If key does
// not exists, returns nil. // not exists, returns nil.
func Config(key []byte) interface{} { func Config(key []byte) any {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return getConfig(ctx, key) return getConfig(ctx, key)
} }
// SetConfig key-value pair as a NeoFS runtime configuration value. Can be invoked // SetConfig key-value pair as a FrostFS runtime configuration value. It can be invoked
// only by Alphabet nodes. // only by Alphabet nodes.
func SetConfig(id, key, val []byte) { func SetConfig(id, key, val []byte) {
ctx := storage.GetContext() ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary common.CheckAlphabetWitness()
alphabet []common.IRNode
nodeKey []byte
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
if len(nodeKey) == 0 {
panic("invoked by non inner ring node")
}
} else {
multiaddr := common.AlphabetAddress()
common.CheckAlphabetWitness(multiaddr)
}
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
setConfig(ctx, key, val) setConfig(ctx, key, val)
runtime.Log("configuration has been updated") runtime.Log("configuration has been updated")
} }
// ListConfig returns array of structures that contain key and value of all type record struct {
// NeoFS configuration records. Key and value are both byte arrays. key []byte
val []byte
}
// ListConfig returns an array of structures that contain key and value of all
// FrostFS configuration records. Key and value are both byte arrays.
func ListConfig() []record { func ListConfig() []record {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
@ -506,24 +469,20 @@ func ListConfig() []record {
return config return config
} }
// Version returns version of the contract. // Version returns the version of the contract.
func Version() int { func Version() int {
return common.Version return common.Version
} }
func addToNetmap(ctx storage.Context, n storageNode) { // serializes and stores the given Node by its public key in the contract storage,
var ( // and throws AddPeerSuccess notification after this.
newNode = n.info //
newNodeKey = newNode[2:35] // Public key MUST match the one encoded in BLOB field.
storageKey = append(candidatePrefix, newNodeKey...) func addToNetmap(ctx storage.Context, publicKey []byte, node Node) {
storageKey := append(candidatePrefix, publicKey...)
node = netmapNode{
node: n,
state: onlineState,
}
)
storage.Put(ctx, storageKey, std.Serialize(node)) storage.Put(ctx, storageKey, std.Serialize(node))
runtime.Notify("AddPeerSuccess", interop.PublicKey(publicKey))
} }
func removeFromNetmap(ctx storage.Context, key interop.PublicKey) { func removeFromNetmap(ctx storage.Context, key interop.PublicKey) {
@ -531,51 +490,62 @@ func removeFromNetmap(ctx storage.Context, key interop.PublicKey) {
storage.Delete(ctx, storageKey) storage.Delete(ctx, storageKey)
} }
func filterNetmap(ctx storage.Context, st nodeState) []storageNode { func updateNetmapState(ctx storage.Context, key interop.PublicKey, state NodeState) {
storageKey := append(candidatePrefix, key...)
raw := storage.Get(ctx, storageKey).([]byte)
if raw == nil {
panic("peer is missing")
}
node := std.Deserialize(raw).(Node)
node.State = state
storage.Put(ctx, storageKey, std.Serialize(node))
}
func filterNetmap(ctx storage.Context) []Node {
var ( var (
netmap = getNetmapNodes(ctx) netmap = getNetmapNodes(ctx)
result = []storageNode{} result = []Node{}
) )
for i := 0; i < len(netmap); i++ { for i := 0; i < len(netmap); i++ {
item := netmap[i] item := netmap[i]
if item.state == st { if item.State != NodeStateOffline {
result = append(result, item.node) result = append(result, item)
} }
} }
return result return result
} }
func getNetmapNodes(ctx storage.Context) []netmapNode { func getNetmapNodes(ctx storage.Context) []Node {
result := []netmapNode{} result := []Node{}
it := storage.Find(ctx, candidatePrefix, storage.ValuesOnly|storage.DeserializeValues) it := storage.Find(ctx, candidatePrefix, storage.ValuesOnly|storage.DeserializeValues)
for iterator.Next(it) { for iterator.Next(it) {
node := iterator.Value(it).(netmapNode) node := iterator.Value(it).(Node)
result = append(result, node) result = append(result, node)
} }
return result return result
} }
func getSnapshot(ctx storage.Context, key string) []storageNode { func getSnapshot(ctx storage.Context, key string) []Node {
data := storage.Get(ctx, key) data := storage.Get(ctx, key)
if data != nil { if data != nil {
return std.Deserialize(data.([]byte)).([]storageNode) return std.Deserialize(data.([]byte)).([]Node)
} }
return []storageNode{} return []Node{}
} }
func getConfig(ctx storage.Context, key interface{}) interface{} { func getConfig(ctx storage.Context, key any) interface{} {
postfix := key.([]byte) postfix := key.([]byte)
storageKey := append(configPrefix, postfix...) storageKey := append(configPrefix, postfix...)
return storage.Get(ctx, storageKey) return storage.Get(ctx, storageKey)
} }
func setConfig(ctx storage.Context, key, val interface{}) { func setConfig(ctx storage.Context, key, val any) {
postfix := key.([]byte) postfix := key.([]byte)
storageKey := append(configPrefix, postfix...) storageKey := append(configPrefix, postfix...)
@ -589,26 +559,3 @@ func cleanup(ctx storage.Context, epoch int) {
containerContractAddr := storage.Get(ctx, containerContractKey).(interop.Hash160) containerContractAddr := storage.Get(ctx, containerContractKey).(interop.Hash160)
contract.Call(containerContractAddr, cleanupEpochMethod, contract.All, epoch) contract.Call(containerContractAddr, cleanupEpochMethod, contract.All, epoch)
} }
func getIRNodes(ctx storage.Context) []common.IRNode {
data := storage.Get(ctx, innerRingKey)
if data != nil {
return std.Deserialize(data.([]byte)).([]common.IRNode)
}
return []common.IRNode{}
}
func keysID(args []interop.PublicKey, prefix []byte) []byte {
var (
result []byte
)
result = append(result, prefix...)
for i := range args {
result = append(result, args[i]...)
}
return crypto.Sha256(result)
}

75
nns/README.md Normal file
View file

@ -0,0 +1,75 @@
# NNS
NNS - Neo Name Service is a service that allows manage a domain name as a digital asset (NFT). It has an interface similar to `DNS` but has significant differences in its internal structure.
## Entities:
- Domain
- Record
- Owner
- Committee
### Domain
Domain is string that satisfies the following requirements:
- Length from 2 to 255 characters.
- Root domain must start with a letter.
- All other fragments must start and end with a letter or digit.
Domain has owner, a registration period, and may optionally have records.
A fee established by the committee is charged upon domain registration. After registration, the owner can manage this asset (add/delete records, transfer ownership to another owner) until the end of the domain registration period.
### Record
A record is a pair of values `<type, string>`.
Supported record types:
| Type | Description |
|-------|-------------------------------------------|
| A | Represents address record type |
| AAA | Represents IPv6 address record type |
| TXT | Represents text record type |
| CNAME | Represents canonical name record type |
| SOA | Represents start of authority record type |
### Owner
An owner is a wallet that has the right to manage this NFT (domain).
### Committee
The committee makes new tokens (domains), sets, and charges a fee for issuance.
## Globally Unique Domain Zone
For more information, see [here](../docs/globally-unique-domain-zone.md).
## NNS and Frostfsid
You can register a TLD domain without a committee signature using Frostfsid. To do this, create a new wallet
```
neo-go wallet init -w newwallet/wallet.json
```
Get wallet address:
```
neo-go wallet dump-keys -w newwallet/wallet.json
[subject-address]
[subject-key]
```
Create a subject in `FrostfsID`:
```
frostfs-adm morph frostfsid create-subject --subject-key="[subject-key]"
```
Grant permissions to the wallet:
```
frostfs-adm morph nns give-privilege --subject-address="[subject-address]"
```
Register domain:
```
neo-go contract invokefunction [NNS-hash] register "subdomain.domain" hash160:[subject-address] "email@frostfs.info" 10000 1000 1000 1000 -- [subject-address]:Global
```

View file

@ -2,8 +2,35 @@ name: "NameService"
supportedstandards: ["NEP-11"] supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf",
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecords", "tokens", "properties", "roots", "getPrice", "isAvailable", "getRecords",
"getAllRecords",
"resolve", "version"] "resolve", "version"]
events: events:
- name: RegisterDomain
parameters:
- name: name
type: String
- name: AddRecord
parameters:
- name: name
type: String
- name: type
type: Integer
- name: DeleteRecord
parameters:
- name: name
type: String
- name: type
type: Integer
- name: DeleteRecords
parameters:
- name: name
type: String
- name: type
type: Integer
- name: DeleteDomain
parameters:
- name: name
type: String
- name: Transfer - name: Transfer
parameters: parameters:
- name: from - name: from

18
nns/doc.go Normal file
View file

@ -0,0 +1,18 @@
/*
# Contract storage scheme
| Key | Value | Description |
|--------------------------------------|------------|-----------------------------------|
| 0x0 | int | total supply of minted domains |
| 0x1 + accountAddr | int | account's balance |
| 0x2 + accountAddr + tokenKey | ByteArray | token ID |
| 0x10 | int | price for domain registration |
| 0x20 | int | set of roots |
| 0x21 + tokenKey | ByteArray | serialized NameState struct |
| 0x22 + tokenKey + Hash160(tokenName) | Hash160 | container contract hash |
| 0x23 + tokenKey + Hash160(tokenName) | string | global domain flag |
*/
package nns

39
nns/frostfsid.go Normal file
View file

@ -0,0 +1,39 @@
package nns
import (
"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/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
const FrostfsIDNNSName = "frostfsid.frostfs"
const (
FrostfsIDNNSTLDPermissionKey = "nns-allow-register-tld"
FrostfsIDTLDRegistrationAllowed = "allow"
)
func checkFrostfsID(ctx storage.Context, addr interop.Hash160) bool {
if len(addr) == 0 {
return false
}
frostfsIDAddress := getRecordsByType(ctx, []byte(tokenIDFromName(FrostfsIDNNSName)), FrostfsIDNNSName, TXT)
if len(frostfsIDAddress) < 2 {
return false
}
decodedBytes := std.Base58Decode([]byte(frostfsIDAddress[1]))
if len(decodedBytes) < 21 || management.GetContract(decodedBytes[1:21]) == nil {
return false
}
if res := contract.Call(decodedBytes[1:21], "getSubjectKV", contract.ReadOnly, addr, FrostfsIDNNSTLDPermissionKey).(string); res == FrostfsIDTLDRegistrationAllowed {
return true
}
return false
}

View file

@ -9,13 +9,13 @@ import (
type NameState struct { type NameState struct {
Owner interop.Hash160 Owner interop.Hash160
Name string Name string
Expiration int Expiration int64
Admin interop.Hash160 Admin interop.Hash160
} }
// ensureNotExpired panics if domain name is expired. // ensureNotExpired panics if domain name is expired.
func (n NameState) ensureNotExpired() { func (n NameState) ensureNotExpired() {
if runtime.GetTime() >= n.Expiration { if int64(runtime.GetTime()) >= n.Expiration {
panic("name has expired") panic("name has expired")
} }
} }

View file

@ -1,20 +0,0 @@
name: "NameService"
supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf",
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecord",
"resolve", "getAllRecords"]
events:
- name: Transfer
parameters:
- name: from
type: Hash160
- name: to
type: Hash160
- name: amount
type: Integer
- name: tokenId
type: ByteArray
permissions:
- hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
methods: ["update"]
- methods: ["onNEP11Payment"]

View file

@ -4,11 +4,12 @@ implementation. This token is a compatible analogue of C# Neo Name Service
token and is aimed to serve as a domain name service for Neo smart-contracts, token and is aimed to serve as a domain name service for Neo smart-contracts,
thus it's NeoNameService. This token can be minted with new domain name thus it's NeoNameService. This token can be minted with new domain name
registration, the domain name itself is your NFT. Corresponding domain root registration, the domain name itself is your NFT. Corresponding domain root
must be added by the committee before new domain name can be registered. must be added by committee before a new domain name can be registered.
*/ */
package nns package nns
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
@ -19,14 +20,13 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util" "github.com/nspcc-dev/neo-go/pkg/interop/util"
"github.com/nspcc-dev/neofs-contract/common"
) )
// Prefixes used for contract data storage. // Prefixes used for contract data storage.
const ( const (
// prefixTotalSupply contains total supply of minted domains. // prefixTotalSupply contains total supply of minted domains.
prefixTotalSupply byte = 0x00 prefixTotalSupply byte = 0x00
// prefixBalance contains map from owner to his balance. // prefixBalance contains map from the owner to their balance.
prefixBalance byte = 0x01 prefixBalance byte = 0x01
// prefixAccountToken contains map from (owner + token key) to token ID, // prefixAccountToken contains map from (owner + token key) to token ID,
// where token key = hash160(token ID) and token ID = domain name. // where token key = hash160(token ID) and token ID = domain name.
@ -41,18 +41,26 @@ const (
// prefixRecord contains map from (token key + hash160(token name) + record type) // prefixRecord contains map from (token key + hash160(token name) + record type)
// to record. // to record.
prefixRecord byte = 0x22 prefixRecord byte = 0x22
// prefixGlobalDomain contains a flag indicating that this domain was created using GlobalDomain.
// This is necessary to distinguish it from regular CNAME records.
prefixGlobalDomain byte = 0x23
// prefixCountSubDomains contains information about the number of domains in the zone.
// If it is nil, it will definitely be calculated on the first removal.
prefixCountSubDomains byte = 0x24
// prefixAutoCreated contains a flag indicating whether the TLD domain was created automatically.
prefixAutoCreated = 0x25
) )
// Values constraints. // Values constraints.
const ( const (
// maxRegisterPrice is the maximum price of register method. // maxRegisterPrice is the maximum price of register method.
maxRegisterPrice = 1_0000_0000_0000 maxRegisterPrice = int64(1_0000_0000_0000)
// maxRootLength is the maximum domain root length. // maxRootLength is the maximum domain root length.
maxRootLength = 16 maxRootLength = 16
// maxDomainNameFragmentLength is the maximum length of the domain name fragment. // maxDomainNameFragmentLength is the maximum length of the domain name fragment.
maxDomainNameFragmentLength = 62 maxDomainNameFragmentLength = 63
// minDomainNameLength is minimum domain length. // minDomainNameLength is minimum domain length.
minDomainNameLength = 3 minDomainNameLength = 2
// maxDomainNameLength is maximum domain length. // maxDomainNameLength is maximum domain length.
maxDomainNameLength = 255 maxDomainNameLength = 255
// maxTXTRecordLength is the maximum length of the TXT domain record. // maxTXTRecordLength is the maximum length of the TXT domain record.
@ -64,7 +72,15 @@ const (
// defaultRegisterPrice is the default price for new domain registration. // defaultRegisterPrice is the default price for new domain registration.
defaultRegisterPrice = 10_0000_0000 defaultRegisterPrice = 10_0000_0000
// millisecondsInYear is amount of milliseconds per year. // millisecondsInYear is amount of milliseconds per year.
millisecondsInYear = 365 * 24 * 3600 * 1000 millisecondsInYear = int64(365 * 24 * 3600 * 1000)
// errInvalidDomainName is an error message for invalid domain name format.
errInvalidDomainName = "invalid domain name format"
)
const (
// Cnametgt is a special TXT record ensuring all created subdomains point to the global domain - the value of this variable.
// It is guaranteed that two domains cannot point to the same global domain.
Cnametgt = "cnametgt"
) )
// RecordState is a type that registered entities are saved to. // RecordState is a type that registered entities are saved to.
@ -76,61 +92,21 @@ type RecordState struct {
} }
// Update updates NameService contract. // Update updates NameService contract.
func Update(nef []byte, manifest string) { func Update(nef []byte, manifest string, data any) {
checkCommittee() checkCommittee()
// Calculating keys and serializing requires calling // Calculating keys and serializing requires calling
// std and crypto contracts. This can be helpful on update // std and crypto contracts. This can be helpful on update
// thus we provide `AllowCall` to management.Update. // thus we provide `AllowCall` to management.Update.
// management.Update(nef, []byte(manifest)) // management.Update(nef, []byte(manifest))
contract.Call(interop.Hash160(management.Hash), "update", management.UpdateWithData(nef, []byte(manifest), common.AppendVersion(data))
contract.All, nef, manifest, common.AppendVersion(nil)) runtime.Log("nns contract updated")
} }
// _deploy initializes defaults (total supply and registration price) on contract deploy. // _deploy initializes defaults (total supply and registration price) on contract deploy.
func _deploy(data interface{}, isUpdate bool) { func _deploy(data any, isUpdate bool) {
if isUpdate { if isUpdate {
ctx := storage.GetContext() args := data.([]any)
committee := common.CommitteeAddress() common.CheckVersion(args[len(args)-1].(int))
it := storage.Find(ctx, []byte{prefixRoot}, storage.KeysOnly|storage.RemovePrefix)
for iterator.Next(it) {
name := iterator.Value(it).(string)
if name != "neofs" {
continue
}
ns := NameState{
Owner: committee,
Name: name,
Expiration: runtime.GetTime() + millisecondsInYear,
}
tokenKey := getTokenKey([]byte(name))
putNameStateWithKey(ctx, tokenKey, ns)
putSoaRecord(ctx, name, "ops@nspcc.ru",
3600 /* 1 hour */, 600, /* 10 min */
604800 /* 1 week */, 3600 /* 1 hour */)
}
r := GetRecords("container.neofs", TXT)
owner := shBEFromLEHex(r[0])
it = storage.Find(ctx, []byte{prefixRoot}, storage.KeysOnly|storage.RemovePrefix)
for iterator.Next(it) {
name := iterator.Value(it).(string)
if name == "neofs" {
continue
}
ns := NameState{
Owner: owner,
Name: name,
Expiration: runtime.GetTime() + millisecondsInYear,
}
tokenKey := getTokenKey([]byte(name))
putNameStateWithKey(ctx, tokenKey, ns)
putSoaRecord(ctx, name, "ops@nspcc.ru",
3600 /* 1 hour */, 600, /* 10 min */
604800 /* 1 week */, 3600 /* 1 hour */)
}
return return
} }
@ -139,39 +115,6 @@ func _deploy(data interface{}, isUpdate bool) {
storage.Put(ctx, []byte{prefixRegisterPrice}, defaultRegisterPrice) storage.Put(ctx, []byte{prefixRegisterPrice}, defaultRegisterPrice)
} }
// remove after v0.13.1 upgrade
func shBEFromLEHex(s string) interop.Hash160 {
res := make([]byte, interop.Hash160Len)
ln := len(s) / 2
for i := 0; i < ln; i++ {
a := hexToNum(s[i*2])
b := hexToNum(s[i*2+1])
var n interface{} = a*16 + b
res[ln-1-i] = n.([]byte)[0]
}
return res
}
// remove after v0.13.1 upgrade
func hexToNum(s uint8) int {
if s >= '0' && s <= '9' {
return int(s) - 48
}
if s >= 'A' && s <= 'F' {
return int(s) - 55
}
if s >= 'a' && s <= 'f' {
return int(s) - 87
}
panic("invalid hex")
}
// Symbol returns NeoNameService symbol. // Symbol returns NeoNameService symbol.
func Symbol() string { func Symbol() string {
return "NNS" return "NNS"
@ -182,35 +125,35 @@ func Decimals() int {
return 0 return 0
} }
// Version returns version of the contract. // Version returns the version of the contract.
func Version() int { func Version() int {
return common.Version return common.Version
} }
// TotalSupply returns overall number of domains minted by the NeoNameService contract. // TotalSupply returns the overall number of domains minted by NeoNameService contract.
func TotalSupply() int { func TotalSupply() int {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
return getTotalSupply(ctx) return getTotalSupply(ctx)
} }
// OwnerOf returns owner of the specified domain. // OwnerOf returns the owner of the specified domain.
func OwnerOf(tokenID []byte) interop.Hash160 { func OwnerOf(tokenID []byte) interop.Hash160 {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
ns := getNameState(ctx, tokenID) ns := getNameState(ctx, tokenID)
return ns.Owner return ns.Owner
} }
// Properties returns domain name and expiration date of the specified domain. // Properties returns a domain name and an expiration date of the specified domain.
func Properties(tokenID []byte) map[string]interface{} { func Properties(tokenID []byte) map[string]any {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
ns := getNameState(ctx, tokenID) ns := getNameState(ctx, tokenID)
return map[string]interface{}{ return map[string]any{
"name": ns.Name, "name": ns.Name,
"expiration": ns.Expiration, "expiration": ns.Expiration,
} }
} }
// BalanceOf returns overall number of domains owned by the specified owner. // BalanceOf returns the overall number of domains owned by the specified owner.
func BalanceOf(owner interop.Hash160) int { func BalanceOf(owner interop.Hash160) int {
if !isValid(owner) { if !isValid(owner) {
panic(`invalid owner`) panic(`invalid owner`)
@ -238,8 +181,8 @@ func TokensOf(owner interop.Hash160) iterator.Iterator {
return storage.Find(ctx, append([]byte{prefixAccountToken}, owner...), storage.ValuesOnly) return storage.Find(ctx, append([]byte{prefixAccountToken}, owner...), storage.ValuesOnly)
} }
// Transfer transfers domain with the specified name to new owner. // Transfer transfers the domain with the specified name to a new owner.
func Transfer(to interop.Hash160, tokenID []byte, data interface{}) bool { func Transfer(to interop.Hash160, tokenID []byte, data any) bool {
if !isValid(to) { if !isValid(to) {
panic(`invalid receiver`) panic(`invalid receiver`)
} }
@ -275,7 +218,7 @@ func Roots() iterator.Iterator {
} }
// SetPrice sets the domain registration price. // SetPrice sets the domain registration price.
func SetPrice(price int) { func SetPrice(price int64) {
checkCommittee() checkCommittee()
if price < 0 || price > maxRegisterPrice { if price < 0 || price > maxRegisterPrice {
panic("The price is out of range.") panic("The price is out of range.")
@ -290,71 +233,136 @@ func GetPrice() int {
return storage.Get(ctx, []byte{prefixRegisterPrice}).(int) return storage.Get(ctx, []byte{prefixRegisterPrice}).(int)
} }
// IsAvailable checks whether provided domain name is available. // IsAvailable checks whether the provided domain name is available.
func IsAvailable(name string) bool { func IsAvailable(name string) bool {
fragments := splitAndCheck(name, false) fragments := splitAndCheck(name)
if fragments == nil {
panic("invalid domain name format")
}
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
l := len(fragments) l := len(fragments)
if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil { if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil {
if l != 1 {
panic("TLD not found")
}
return true return true
} }
return parentExpired(ctx, 0, fragments)
checkParent(ctx, fragments)
checkAvailableGlobalDomain(ctx, name)
return storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) == nil
} }
// parentExpired returns true if any domain from fragments doesn't exist or expired. // checkAvailableGlobalDomain - triggers a panic if the global domain name is occupied.
// first denotes the deepest subdomain to check. func checkAvailableGlobalDomain(ctx storage.Context, domain string) {
func parentExpired(ctx storage.Context, first int, fragments []string) bool { globalDomain := getGlobalDomain(ctx, domain)
now := runtime.GetTime() if globalDomain == "" {
return
}
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(globalDomain))...))
if nsBytes != nil {
panic("global domain is already taken: " + globalDomain + ". Domain: " + domain)
}
}
// getGlobalDomain returns the global domain.
func getGlobalDomain(ctx storage.Context, domain string) string {
index := std.MemorySearch([]byte(domain), []byte("."))
if index == -1 {
return ""
}
name := domain[index+1:]
if name == "" {
return ""
}
return extractCnametgt(ctx, name, domain)
}
// extractCnametgt returns the value of the Cnametgt TXT record.
func extractCnametgt(ctx storage.Context, name, domain string) string {
fragments := splitAndCheck(domain)
tokenID := []byte(tokenIDFromName(name))
records := getRecordsByType(ctx, tokenID, name, TXT)
if records == nil {
return ""
}
globalDomain := ""
for _, name := range records {
fragments := std.StringSplit(name, "=")
if len(fragments) != 2 {
continue
}
if fragments[0] == Cnametgt {
globalDomain = fragments[1]
break
}
}
if globalDomain == "" {
return ""
}
return fragments[0] + "." + globalDomain
}
// checkParent returns parent domain or empty string if domain not found.
func checkParent(ctx storage.Context, fragments []string) string {
now := int64(runtime.GetTime())
last := len(fragments) - 1 last := len(fragments) - 1
name := fragments[last] name := fragments[last]
for i := last; i >= first; i-- { parent := ""
for i := last; i > 0; i-- {
if i != last { if i != last {
name = fragments[i] + "." + name name = fragments[i] + "." + name
} }
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...))
if nsBytes == nil { if nsBytes == nil {
return true continue
} }
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
if now >= ns.Expiration { if now >= ns.Expiration {
return true panic("domain expired: " + name)
} }
parent = name
} }
return false return parent
} }
// Register registers new domain with the specified owner and name if it's available. // Register registers a new domain with the specified owner and name if it's available.
func Register(name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool { func Register(name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
fragments := splitAndCheck(name, true)
if fragments == nil {
panic("invalid domain name format")
}
l := len(fragments)
tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...)
ctx := storage.GetContext() ctx := storage.GetContext()
return register(ctx, name, owner, email, refresh, retry, expire, ttl)
}
// Register registers a new domain with the specified owner and name if it's available.
func register(ctx storage.Context, name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
fragments := splitAndCheck(name)
countZone := len(fragments)
rootZone := []byte(fragments[countZone-1])
tldKey := append([]byte{prefixRoot}, rootZone...)
tldBytes := storage.Get(ctx, tldKey) tldBytes := storage.Get(ctx, tldKey)
if l == 1 { if countZone == 1 {
checkCommittee() checkCommitteeAndFrostfsID(ctx, owner)
if tldBytes != nil { if tldBytes != nil {
panic("TLD already exists") panic("TLD already exists")
} }
storage.Put(ctx, tldKey, 0) storage.Put(ctx, tldKey, 0)
} else { } else {
if tldBytes == nil { parent := checkParent(ctx, fragments)
panic("TLD not found") if parent == "" {
parent = fragments[len(fragments)-1]
storage.Put(ctx, append([]byte{prefixAutoCreated}, rootZone...), true)
register(ctx, parent, owner, email, refresh, retry, expire, ttl)
} }
if parentExpired(ctx, 1, fragments) {
panic("one of the parent domains is not registered") parentKey := getTokenKey([]byte(parent))
}
parentKey := getTokenKey([]byte(name[len(fragments[0])+1:]))
nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...)) nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...))
if nsBytes == nil {
panic("parent does not exist:" + parent)
}
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
ns.checkAdmin() ns.checkAdmin()
@ -382,7 +390,7 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
nsBytes := storage.Get(ctx, append([]byte{prefixName}, tokenKey...)) nsBytes := storage.Get(ctx, append([]byte{prefixName}, tokenKey...))
if nsBytes != nil { if nsBytes != nil {
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
if runtime.GetTime() < ns.Expiration { if int64(runtime.GetTime()) < ns.Expiration {
return false return false
} }
oldOwner = ns.Owner oldOwner = ns.Owner
@ -391,35 +399,37 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
updateTotalSupply(ctx, +1) updateTotalSupply(ctx, +1)
} }
ns := NameState{ ns := NameState{
Owner: owner, Owner: owner,
Name: name, Name: name,
Expiration: runtime.GetTime() + millisecondsInYear, // NNS expiration is in milliseconds
Expiration: int64(runtime.GetTime() + expire*1000),
} }
checkAvailableGlobalDomain(ctx, name)
updateSubdDomainCounter(ctx, rootZone, countZone)
putNameStateWithKey(ctx, tokenKey, ns) putNameStateWithKey(ctx, tokenKey, ns)
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl) putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
updateBalance(ctx, []byte(name), owner, +1) updateBalance(ctx, []byte(name), owner, +1)
postTransfer(oldOwner, owner, []byte(name), nil) postTransfer(oldOwner, owner, []byte(name), nil)
runtime.Notify("RegisterDomain", name)
return true return true
} }
// Renew increases domain expiration date. // Renew increases domain expiration date.
func Renew(name string) int { func Renew(name string) int64 {
if len(name) > maxDomainNameLength { checkDomainNameLength(name)
panic("invalid domain name format")
}
runtime.BurnGas(GetPrice()) runtime.BurnGas(GetPrice())
ctx := storage.GetContext() ctx := storage.GetContext()
ns := getNameState(ctx, []byte(name)) ns := getNameState(ctx, []byte(name))
ns.checkAdmin()
ns.Expiration += millisecondsInYear ns.Expiration += millisecondsInYear
putNameState(ctx, ns) putNameState(ctx, ns)
return ns.Expiration return ns.Expiration
} }
// UpdateSOA update soa record. // UpdateSOA updates soa record.
func UpdateSOA(name, email string, refresh, retry, expire, ttl int) { func UpdateSOA(name, email string, refresh, retry, expire, ttl int) {
if len(name) > maxDomainNameLength { checkDomainNameLength(name)
panic("invalid domain name format")
}
ctx := storage.GetContext() ctx := storage.GetContext()
ns := getNameState(ctx, []byte(name)) ns := getNameState(ctx, []byte(name))
ns.checkAdmin() ns.checkAdmin()
@ -428,9 +438,7 @@ func UpdateSOA(name, email string, refresh, retry, expire, ttl int) {
// SetAdmin updates domain admin. // SetAdmin updates domain admin.
func SetAdmin(name string, admin interop.Hash160) { func SetAdmin(name string, admin interop.Hash160) {
if len(name) > maxDomainNameLength { checkDomainNameLength(name)
panic("invalid domain name format")
}
if admin != nil && !runtime.CheckWitness(admin) { if admin != nil && !runtime.CheckWitness(admin) {
panic("not witnessed by admin") panic("not witnessed by admin")
} }
@ -441,7 +449,7 @@ func SetAdmin(name string, admin interop.Hash160) {
putNameState(ctx, ns) putNameState(ctx, ns)
} }
// SetRecord adds new record of the specified type to the provided domain. // SetRecord adds a new record of the specified type to the provided domain.
func SetRecord(name string, typ RecordType, id byte, data string) { func SetRecord(name string, typ RecordType, id byte, data string) {
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
if !checkBaseRecords(typ, data) { if !checkBaseRecords(typ, data) {
@ -459,7 +467,7 @@ func checkBaseRecords(typ RecordType, data string) bool {
case A: case A:
return checkIPv4(data) return checkIPv4(data)
case CNAME: case CNAME:
return splitAndCheck(data, true) != nil return splitAndCheck(data) != nil
case TXT: case TXT:
return len(data) <= maxTXTRecordLength return len(data) <= maxTXTRecordLength
case AAAA: case AAAA:
@ -469,7 +477,7 @@ func checkBaseRecords(typ RecordType, data string) bool {
} }
} }
// AddRecord adds new record of the specified type to the provided domain. // AddRecord adds a new record of the specified type to the provided domain.
func AddRecord(name string, typ RecordType, data string) { func AddRecord(name string, typ RecordType, data string) {
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
if !checkBaseRecords(typ, data) { if !checkBaseRecords(typ, data) {
@ -493,13 +501,26 @@ func GetRecords(name string, typ RecordType) []string {
// DeleteRecords removes domain records with the specified type. // DeleteRecords removes domain records with the specified type.
func DeleteRecords(name string, typ RecordType) { func DeleteRecords(name string, typ RecordType) {
ctx := storage.GetContext()
deleteRecords(ctx, name, typ)
}
// DeleteRecords removes domain records with the specified type.
func deleteRecords(ctx storage.Context, name string, typ RecordType) {
if typ == SOA { if typ == SOA {
panic("you cannot delete soa record") panic("you cannot delete soa record")
} }
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetContext()
ns := getNameState(ctx, tokenID) ns := getNameState(ctx, tokenID)
ns.checkAdmin() ns.checkAdmin()
globalDomainStorage := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
globalDomainRaw := storage.Get(ctx, globalDomainStorage)
globalDomain := globalDomainRaw.(string)
if globalDomainRaw != nil && globalDomain != "" {
deleteDomain(ctx, globalDomain)
}
recordsKey := getRecordsKeyByType(tokenID, name, typ) recordsKey := getRecordsKeyByType(tokenID, name, typ)
records := storage.Find(ctx, recordsKey, storage.KeysOnly) records := storage.Find(ctx, recordsKey, storage.KeysOnly)
for iterator.Next(records) { for iterator.Next(records) {
@ -507,6 +528,126 @@ func DeleteRecords(name string, typ RecordType) {
storage.Delete(ctx, r) storage.Delete(ctx, r)
} }
updateSoaSerial(ctx, tokenID) updateSoaSerial(ctx, tokenID)
runtime.Notify("DeleteRecords", name, typ)
}
// DeleteRecord delete a record of the specified type by data in the provided domain.
// Returns false if the record was not found.
func DeleteRecord(name string, typ RecordType, data string) bool {
tokenID := []byte(tokenIDFromName(name))
if !checkBaseRecords(typ, data) {
panic("invalid record data")
}
ctx := storage.GetContext()
ns := getNameState(ctx, tokenID)
ns.checkAdmin()
return deleteRecord(ctx, tokenID, name, typ, data)
}
func deleteRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType, data string) bool {
recordsKey := getRecordsKeyByType(tokenId, name, typ)
var previousKey any
it := storage.Find(ctx, recordsKey, storage.KeysOnly)
for iterator.Next(it) {
key := iterator.Value(it).([]byte)
ss := storage.Get(ctx, key).([]byte)
ns := std.Deserialize(ss).(RecordState)
if ns.Name == name && ns.Type == typ && ns.Data == data {
previousKey = key
continue
}
if previousKey != nil {
data := storage.Get(ctx, key)
storage.Put(ctx, previousKey, data)
previousKey = key
}
}
if previousKey == nil {
return false
}
storage.Delete(ctx, previousKey)
runtime.Notify("DeleteRecord", name, typ)
return true
}
// DeleteDomain deletes the domain with the given name.
func DeleteDomain(name string) {
ctx := storage.GetContext()
deleteDomain(ctx, name)
}
func countSubdomains(name string) int {
countSubDomains := 0
it := Tokens()
for iterator.Next(it) {
domain := iterator.Value(it)
if std.MemorySearch([]byte(domain.(string)), []byte(name)) > 0 {
countSubDomains = countSubDomains + 1
}
}
return countSubDomains
}
func deleteDomain(ctx storage.Context, name string) {
fragments := splitAndCheck(name)
parent := []byte(fragments[len(fragments)-1])
countSubDomainsKey := append([]byte{prefixCountSubDomains}, parent...)
autoCreatedPrefix := append([]byte{prefixAutoCreated}, parent...)
nsKey := append([]byte{prefixName}, getTokenKey([]byte(name))...)
nsRaw := storage.Get(ctx, nsKey)
if nsRaw == nil {
panic("domain not found")
}
ns := std.Deserialize(nsRaw.([]byte)).(NameState)
ns.checkAdmin()
countSubDomain := 0
countSubDomainRaw := storage.Get(ctx, countSubDomainsKey)
if countSubDomainRaw != nil {
countSubDomain = common.FromFixedWidth64(countSubDomainRaw.([]byte))
} else {
countSubDomain = countSubdomains(fragments[len(fragments)-1])
}
if countSubDomain > 1 && len(fragments) == 1 {
panic("can't delete TLD domain that has subdomains")
}
countSubDomain = countSubDomain - 1
storage.Put(ctx, countSubDomainsKey, common.ToFixedWidth64(countSubDomain))
globalNSKey := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
globalDomainRaw := storage.Get(ctx, globalNSKey)
globalDomain := globalDomainRaw.(string)
if globalDomainRaw != nil && globalDomain != "" {
deleteDomain(ctx, globalDomain)
}
deleteRecords(ctx, name, CNAME)
deleteRecords(ctx, name, TXT)
deleteRecords(ctx, name, A)
deleteRecords(ctx, name, AAAA)
storage.Delete(ctx, nsKey)
storage.Delete(ctx, append([]byte{prefixRoot}, []byte(name)...))
isAutoCreated := storage.Get(ctx, autoCreatedPrefix)
if countSubDomain == 1 && isAutoCreated != nil && isAutoCreated.(bool) {
deleteDomain(ctx, fragments[len(fragments)-1])
}
if len(fragments) == 1 {
storage.Delete(ctx, countSubDomainsKey)
storage.Delete(ctx, autoCreatedPrefix)
}
runtime.Notify("DeleteDomain", name)
} }
// Resolve resolves given name (not more then three redirects are allowed). // Resolve resolves given name (not more then three redirects are allowed).
@ -515,7 +656,7 @@ func Resolve(name string, typ RecordType) []string {
return resolve(ctx, nil, name, typ, 2) return resolve(ctx, nil, name, typ, 2)
} }
// GetAllRecords returns an Iterator with RecordState items for given name. // GetAllRecords returns an Iterator with RecordState items for the given name.
func GetAllRecords(name string) iterator.Iterator { func GetAllRecords(name string) iterator.Iterator {
tokenID := []byte(tokenIDFromName(name)) tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
@ -529,13 +670,13 @@ func updateBalance(ctx storage.Context, tokenId []byte, acc interop.Hash160, dif
balanceKey := append([]byte{prefixBalance}, acc...) balanceKey := append([]byte{prefixBalance}, acc...)
var balance int var balance int
if b := storage.Get(ctx, balanceKey); b != nil { if b := storage.Get(ctx, balanceKey); b != nil {
balance = b.(int) balance = common.FromFixedWidth64(b.([]byte))
} }
balance += diff balance += diff
if balance == 0 { if balance == 0 {
storage.Delete(ctx, balanceKey) storage.Delete(ctx, balanceKey)
} else { } else {
storage.Put(ctx, balanceKey, balance) storage.Put(ctx, balanceKey, common.ToFixedWidth64(balance))
} }
tokenKey := getTokenKey(tokenId) tokenKey := getTokenKey(tokenId)
@ -549,7 +690,7 @@ func updateBalance(ctx storage.Context, tokenId []byte, acc interop.Hash160, dif
// postTransfer sends Transfer notification to the network and calls onNEP11Payment // postTransfer sends Transfer notification to the network and calls onNEP11Payment
// method. // method.
func postTransfer(from, to interop.Hash160, tokenID []byte, data interface{}) { func postTransfer(from, to interop.Hash160, tokenID []byte, data any) {
runtime.Notify("Transfer", from, to, 1, tokenID) runtime.Notify("Transfer", from, to, 1, tokenID)
if management.GetContract(to) != nil { if management.GetContract(to) != nil {
contract.Call(to, "onNEP11Payment", contract.All, from, 1, tokenID, data) contract.Call(to, "onNEP11Payment", contract.All, from, 1, tokenID, data)
@ -559,14 +700,14 @@ func postTransfer(from, to interop.Hash160, tokenID []byte, data interface{}) {
// getTotalSupply returns total supply from storage. // getTotalSupply returns total supply from storage.
func getTotalSupply(ctx storage.Context) int { func getTotalSupply(ctx storage.Context) int {
val := storage.Get(ctx, []byte{prefixTotalSupply}) val := storage.Get(ctx, []byte{prefixTotalSupply})
return val.(int) return common.FromFixedWidth64(val.([]byte))
} }
// updateTotalSupply adds specified diff to the total supply. // updateTotalSupply adds the specified diff to the total supply.
func updateTotalSupply(ctx storage.Context, diff int) { func updateTotalSupply(ctx storage.Context, diff int) {
tsKey := []byte{prefixTotalSupply} tsKey := []byte{prefixTotalSupply}
ts := getTotalSupply(ctx) ts := getTotalSupply(ctx)
storage.Put(ctx, tsKey, ts+diff) storage.Put(ctx, tsKey, common.ToFixedWidth64(ts+diff))
} }
// getTokenKey computes hash160 from the given tokenID. // getTokenKey computes hash160 from the given tokenID.
@ -579,9 +720,7 @@ func getNameState(ctx storage.Context, tokenID []byte) NameState {
tokenKey := getTokenKey(tokenID) tokenKey := getTokenKey(tokenID)
ns := getNameStateWithKey(ctx, tokenKey) ns := getNameStateWithKey(ctx, tokenKey)
fragments := std.StringSplit(string(tokenID), ".") fragments := std.StringSplit(string(tokenID), ".")
if parentExpired(ctx, 1, fragments) { checkParent(ctx, fragments)
panic("parent domain has expired")
}
return ns return ns
} }
@ -651,6 +790,33 @@ func addRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType,
} }
} }
globalDomainKey := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
globalDomainStorage := storage.Get(ctx, globalDomainKey)
globalDomain := getGlobalDomain(ctx, name)
if globalDomainStorage == nil && typ == TXT {
if globalDomain != "" {
checkAvailableGlobalDomain(ctx, name)
nsOriginal := getNameState(ctx, []byte(tokenIDFromName(name)))
ns := NameState{
Name: globalDomain,
Owner: nsOriginal.Owner,
Expiration: nsOriginal.Expiration,
Admin: nsOriginal.Admin,
}
putNameStateWithKey(ctx, getTokenKey([]byte(globalDomain)), ns)
storage.Put(ctx, globalDomainKey, globalDomain)
var oldOwner interop.Hash160
updateBalance(ctx, []byte(name), nsOriginal.Owner, +1)
postTransfer(oldOwner, nsOriginal.Owner, []byte(name), nil)
putCnameRecord(ctx, globalDomain, name)
} else {
storage.Put(ctx, globalDomainKey, "")
}
}
if typ == CNAME && id != 0 { if typ == CNAME && id != 0 {
panic("you shouldn't have more than one CNAME record") panic("you shouldn't have more than one CNAME record")
} }
@ -659,7 +825,7 @@ func addRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType,
storeRecord(ctx, recordKey, name, typ, id, data) storeRecord(ctx, recordKey, name, typ, id, data)
} }
// storeRecord put record to storage. // storeRecord puts record to storage.
func storeRecord(ctx storage.Context, recordKey []byte, name string, typ RecordType, id byte, data string) { func storeRecord(ctx storage.Context, recordKey []byte, name string, typ RecordType, id byte, data string) {
rs := RecordState{ rs := RecordState{
Name: name, Name: name,
@ -669,6 +835,7 @@ func storeRecord(ctx storage.Context, recordKey []byte, name string, typ RecordT
} }
recBytes := std.Serialize(rs) recBytes := std.Serialize(rs)
storage.Put(ctx, recordKey, recBytes) storage.Put(ctx, recordKey, recBytes)
runtime.Notify("AddRecord", name, typ)
} }
// putSoaRecord stores soa domain record. // putSoaRecord stores soa domain record.
@ -689,6 +856,24 @@ func putSoaRecord(ctx storage.Context, name, email string, refresh, retry, expir
} }
recBytes := std.Serialize(rs) recBytes := std.Serialize(rs)
storage.Put(ctx, recordKey, recBytes) storage.Put(ctx, recordKey, recBytes)
runtime.Notify("AddRecord", name, SOA)
}
// putCnameRecord stores CNAME domain record.
func putCnameRecord(ctx storage.Context, name, data string) {
var id byte
tokenId := []byte(tokenIDFromName(name))
recordKey := getIdRecordKey(tokenId, name, CNAME, id)
rs := RecordState{
Name: name,
Type: CNAME,
ID: id,
Data: data,
}
recBytes := std.Serialize(rs)
storage.Put(ctx, recordKey, recBytes)
runtime.Notify("AddRecord", name, CNAME)
} }
// updateSoaSerial stores soa domain record. // updateSoaSerial stores soa domain record.
@ -698,7 +883,7 @@ func updateSoaSerial(ctx storage.Context, tokenId []byte) {
recBytes := storage.Get(ctx, recordKey) recBytes := storage.Get(ctx, recordKey)
if recBytes == nil { if recBytes == nil {
panic("not found soa record") return
} }
rec := std.Deserialize(recBytes.([]byte)).(RecordState) rec := std.Deserialize(recBytes.([]byte)).(RecordState)
@ -716,19 +901,19 @@ func updateSoaSerial(ctx storage.Context, tokenId []byte) {
storage.Put(ctx, recordKey, recBytes) storage.Put(ctx, recordKey, recBytes)
} }
// getRecordsKey returns prefix used to store domain records of different types. // getRecordsKey returns the prefix used to store domain records of different types.
func getRecordsKey(tokenId []byte, name string) []byte { func getRecordsKey(tokenId []byte, name string) []byte {
recordKey := append([]byte{prefixRecord}, getTokenKey(tokenId)...) recordKey := append([]byte{prefixRecord}, getTokenKey(tokenId)...)
return append(recordKey, getTokenKey([]byte(name))...) return append(recordKey, getTokenKey([]byte(name))...)
} }
// getRecordsKeyByType returns key used to store domain records. // getRecordsKeyByType returns the key used to store domain records.
func getRecordsKeyByType(tokenId []byte, name string, typ RecordType) []byte { func getRecordsKeyByType(tokenId []byte, name string, typ RecordType) []byte {
recordKey := getRecordsKey(tokenId, name) recordKey := getRecordsKey(tokenId, name)
return append(recordKey, byte(typ)) return append(recordKey, byte(typ))
} }
// getIdRecordKey returns key used to store domain records. // getIdRecordKey returns the key used to store domain records.
func getIdRecordKey(tokenId []byte, name string, typ RecordType, id byte) []byte { func getIdRecordKey(tokenId []byte, name string, typ RecordType, id byte) []byte {
recordKey := getRecordsKey(tokenId, name) recordKey := getRecordsKey(tokenId, name)
return append(recordKey, byte(typ), id) return append(recordKey, byte(typ), id)
@ -739,6 +924,14 @@ func isValid(address interop.Hash160) bool {
return address != nil && len(address) == interop.Hash160Len return address != nil && len(address) == interop.Hash160Len
} }
// checkCommitteeAndFrostfsID panics if the script container is not signed by the committee.
// or if the owner does not have permission in FrostfsID.
func checkCommitteeAndFrostfsID(ctx storage.Context, owner interop.Hash160) {
if !checkFrostfsID(ctx, owner) {
checkCommittee()
}
}
// checkCommittee panics if the script container is not signed by the committee. // checkCommittee panics if the script container is not signed by the committee.
func checkCommittee() { func checkCommittee() {
committee := neo.GetCommittee() committee := neo.GetCommittee()
@ -754,7 +947,7 @@ func checkCommittee() {
// checkFragment validates root or a part of domain name. // checkFragment validates root or a part of domain name.
// 1. Root domain must start with a letter. // 1. Root domain must start with a letter.
// 2. All other fragments must start and end in a letter or a digit. // 2. All other fragments must start and end with a letter or a digit.
func checkFragment(v string, isRoot bool) bool { func checkFragment(v string, isRoot bool) bool {
maxLength := maxDomainNameFragmentLength maxLength := maxDomainNameFragmentLength
if isRoot { if isRoot {
@ -787,24 +980,29 @@ func isAlNum(c uint8) bool {
} }
// splitAndCheck splits domain name into parts and validates it. // splitAndCheck splits domain name into parts and validates it.
func splitAndCheck(name string, allowMultipleFragments bool) []string { func splitAndCheck(name string) []string {
l := len(name) checkDomainNameLength(name)
if l < minDomainNameLength || maxDomainNameLength < l {
return nil
}
fragments := std.StringSplit(name, ".") fragments := std.StringSplit(name, ".")
l = len(fragments) l := len(fragments)
if l > 2 && !allowMultipleFragments {
return nil
}
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
if !checkFragment(fragments[i], i == l-1) { if !checkFragment(fragments[i], i == l-1) {
return nil panic(errInvalidDomainName + " '" + name + "': invalid fragment '" + fragments[i] + "'")
} }
} }
return fragments return fragments
} }
// checkDomainNameLength panics if domain name length is out of boundaries.
func checkDomainNameLength(name string) {
l := len(name)
if l > maxDomainNameLength {
panic(errInvalidDomainName + " '" + name + "': domain name too long: got = " + std.Itoa(l, 10) + ", max = " + std.Itoa(maxDomainNameLength, 10))
}
if l < minDomainNameLength {
panic(errInvalidDomainName + " '" + name + "': domain name too short: got = " + std.Itoa(l, 10) + ", min = " + std.Itoa(minDomainNameLength, 10))
}
}
// checkIPv4 checks record on IPv4 compliance. // checkIPv4 checks record on IPv4 compliance.
func checkIPv4(data string) bool { func checkIPv4(data string) bool {
l := len(data) l := len(data)
@ -915,12 +1113,9 @@ func checkIPv6(data string) bool {
return true return true
} }
// tokenIDFromName returns token ID (domain.root) from provided name. // tokenIDFromName returns token ID (domain.root) from the provided name.
func tokenIDFromName(name string) string { func tokenIDFromName(name string) string {
fragments := splitAndCheck(name, true) fragments := splitAndCheck(name)
if fragments == nil {
panic("invalid domain name format")
}
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
sum := 0 sum := 0
@ -931,7 +1126,7 @@ func tokenIDFromName(name string) string {
nsBytes := storage.Get(ctx, nameKey) nsBytes := storage.Get(ctx, nameKey)
if nsBytes != nil { if nsBytes != nil {
ns := std.Deserialize(nsBytes.([]byte)).(NameState) ns := std.Deserialize(nsBytes.([]byte)).(NameState)
if runtime.GetTime() < ns.Expiration { if int64(runtime.GetTime()) < ns.Expiration {
return name[sum:] return name[sum:]
} }
} }
@ -940,7 +1135,7 @@ func tokenIDFromName(name string) string {
return name return name
} }
// resolve resolves provided name using record with the specified type and given // resolve resolves the provided name using record with the specified type and given
// maximum redirections constraint. // maximum redirections constraint.
func resolve(ctx storage.Context, res []string, name string, typ RecordType, redirect int) []string { func resolve(ctx storage.Context, res []string, name string, typ RecordType, redirect int) []string {
if redirect < 0 { if redirect < 0 {
@ -979,3 +1174,16 @@ func getAllRecords(ctx storage.Context, name string) iterator.Iterator {
recordsKey := getRecordsKey(tokenID, name) recordsKey := getRecordsKey(tokenID, name)
return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues) return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues)
} }
func updateSubdDomainCounter(ctx storage.Context, rootZone []byte, countZone int) {
countSubDomain := 0
delInfoRaw := storage.Get(ctx, append([]byte{prefixCountSubDomains}, rootZone...))
if delInfoRaw != nil {
countSubDomain = common.FromFixedWidth64(delInfoRaw.([]byte))
}
if delInfoRaw != nil || countZone == 1 {
countSubDomain = countSubDomain + 1
storage.Put(ctx, append([]byte{prefixCountSubDomains}, rootZone...), common.ToFixedWidth64(countSubDomain))
}
}

View file

@ -3,7 +3,7 @@ package nns
// RecordType is domain name service record types. // RecordType is domain name service record types.
type RecordType byte type RecordType byte
// Record types defined in [RFC 1035](https://tools.ietf.org/html/rfc1035) // Record types are defined in [RFC 1035](https://tools.ietf.org/html/rfc1035)
const ( const (
// A represents address record type. // A represents address record type.
A RecordType = 1 A RecordType = 1
@ -15,7 +15,7 @@ const (
TXT RecordType = 16 TXT RecordType = 16
) )
// Record types defined in [RFC 3596](https://tools.ietf.org/html/rfc3596) // Record types are defined in [RFC 3596](https://tools.ietf.org/html/rfc3596)
const ( const (
// AAAA represents IPv6 address record type. // AAAA represents IPv6 address record type.
AAAA RecordType = 28 AAAA RecordType = 28

12
policy/config.yml Normal file
View file

@ -0,0 +1,12 @@
name: "APE"
permissions:
- methods: ["update"]
safemethods:
- "getAdmin"
- "listChains"
- "getChain"
- "listChainsByPrefix"
- "listTargets"
- "listChainNames"
- "iteratorChainsByPrefix"
- "version"

14
policy/doc.go Normal file
View file

@ -0,0 +1,14 @@
/*
# Contract storage scheme
| Key | Value | Description |
|------------------------------------------|--------|-----------------------------------|
| 'c' + uint16(len(container)) + container | []byte | Namespace chain |
| 'n' + uint16(len(namespace)) + namespace | []byte | Container chain |
| 'm' + entity name (namespace/container) | []byte | Mapped name to an encoded number |
| 'Counter' | uint64 | Integer counter used for mapping |
*/
package policy

266
policy/policy_contract.go Normal file
View file

@ -0,0 +1,266 @@
package policy
import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Kind represents the object the chain is attached to.
// Currently only namespace and container are supported.
type Kind byte
const (
Namespace = 'n'
Container = 'c'
User = 'u'
Group = 'g'
IAM = 'i'
)
const (
ownerKeyPrefix = 'o'
)
const (
mappingKeyPrefix = 'm'
counterKey = "Counter"
)
const (
// ErrNotAuthorized is returned when the none of the transaction signers
// belongs to the list of autorized keys.
ErrNotAuthorized = "none of the signers is authorized to change the contract"
)
// _deploy function sets up initial list of inner ring public keys.
func _deploy(data any, isUpdate bool) {
if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return
}
args := data.(struct {
Admin interop.Hash160
})
ctx := storage.GetContext()
if args.Admin != nil {
if len(args.Admin) != 20 {
panic("invaliad admin hash length")
}
storage.Put(ctx, []byte{ownerKeyPrefix}, args.Admin)
}
storage.Put(ctx, counterKey, 0)
}
func checkAuthorization(ctx storage.Context) {
admin := getAdmin(ctx)
if admin != nil && runtime.CheckWitness(admin) {
return
}
if runtime.CheckWitness(common.AlphabetAddress()) {
return
}
panic(ErrNotAuthorized)
}
// Version returns the version of the contract.
func Version() int {
return common.Version
}
// Update method updates contract source code and manifest. It can be invoked
// by committee only.
func Update(script []byte, manifest []byte, data any) {
if !common.HasUpdateAccess() {
panic("only committee can update contract")
}
management.UpdateWithData(script, manifest, common.AppendVersion(data))
runtime.Log("policy contract updated")
}
func SetAdmin(addr interop.Hash160) {
common.CheckAlphabetWitness()
ctx := storage.GetContext()
storage.Put(ctx, []byte{ownerKeyPrefix}, addr)
}
func GetAdmin() interop.Hash160 {
ctx := storage.GetReadOnlyContext()
return getAdmin(ctx)
}
func getAdmin(ctx storage.Context) interop.Hash160 {
return storage.Get(ctx, []byte{ownerKeyPrefix}).(interop.Hash160)
}
func storageKey(prefix Kind, counter int, name []byte) []byte {
key := append([]byte{byte(prefix)}, common.ToFixedWidth64(counter)...)
return append(key, name...)
}
func mapKey(kind Kind, name []byte) []byte {
return append([]byte{mappingKeyPrefix, byte(kind)}, name...)
}
// mapToNumeric maps a name to a number. That allows to keep more space in
// a storage key shortening long names. Short entity
// names are also mapped to prevent collisions in the map.
func mapToNumeric(ctx storage.Context, kind Kind, name []byte) (mapped int, mappingExists bool) {
mKey := mapKey(kind, name)
numericID := storage.Get(ctx, mKey)
if numericID == nil {
return 0, false
}
mapped = numericID.(int)
mappingExists = true
return
}
// mapToNumericCreateIfNotExists maps a name to a number. That allows to keep
// more space in a storage key shortening long names. Short entity
// names are also mapped to prevent collisions in the map.
// If a mapping cannot be found, then the method creates and returns it.
// mapToNumericCreateIfNotExists is NOT applicable for a read-only context.
func mapToNumericCreateIfNotExists(ctx storage.Context, kind Kind, name []byte) int {
mKey := mapKey(kind, name)
numericID := storage.Get(ctx, mKey)
if numericID == nil {
counter := storage.Get(ctx, counterKey).(int)
counter++
storage.Put(ctx, counterKey, counter)
storage.Put(ctx, mKey, counter)
return counter
}
return numericID.(int)
}
func AddChain(entity Kind, entityName string, name []byte, chain []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
entityNameBytes := mapToNumericCreateIfNotExists(ctx, entity, []byte(entityName))
key := storageKey(entity, entityNameBytes, name)
storage.Put(ctx, key, chain)
}
func GetChain(entity Kind, entityName string, name []byte) []byte {
ctx := storage.GetReadOnlyContext()
entityNameBytes, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
panic("not found")
}
key := storageKey(entity, entityNameBytes, name)
data := storage.Get(ctx, key).([]byte)
if data == nil {
panic("not found")
}
return data
}
func RemoveChain(entity Kind, entityName string, name []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
entityNameNum, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
return
}
key := storageKey(entity, entityNameNum, name)
storage.Delete(ctx, key)
// If no chains are left for the target, then remove the mapping.
prefix := append([]byte{byte(entity)}, common.ToFixedWidth64(entityNameNum)...)
it := storage.Find(ctx, prefix, storage.KeysOnly)
if !iterator.Next(it) {
storage.Delete(ctx, mapKey(entity, []byte(entityName)))
}
}
func RemoveChainsByPrefix(entity Kind, entityName string, name []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
entityNameNum, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
return
}
key := storageKey(entity, entityNameNum, name)
it := storage.Find(ctx, key, storage.KeysOnly)
for iterator.Next(it) {
storage.Delete(ctx, iterator.Value(it).([]byte))
}
// If no chains are left for the target, then remove the mapping.
prefix := append([]byte{byte(entity)}, common.ToFixedWidth64(entityNameNum)...)
it = storage.Find(ctx, prefix, storage.KeysOnly)
if !iterator.Next(it) {
storage.Delete(ctx, mapKey(entity, []byte(entityName)))
}
}
// ListChains lists all chains for the namespace by prefix.
// container may be empty.
func ListChains(namespace, container string, name []byte) [][]byte {
result := ListChainsByPrefix(Namespace, namespace, name)
if container != "" {
result = append(result, ListChainsByPrefix(Container, container, name)...)
}
return result
}
// ListChainsByPrefix list all chains for the provided kind and entity by prefix.
func ListChainsByPrefix(entity Kind, entityName string, prefix []byte) [][]byte {
ctx := storage.GetReadOnlyContext()
result := [][]byte{}
entityNameBytes, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
return result
}
keyPrefix := storageKey(entity, entityNameBytes, prefix)
it := storage.Find(ctx, keyPrefix, storage.ValuesOnly)
for iterator.Next(it) {
result = append(result, iterator.Value(it).([]byte))
}
return result
}
func IteratorChainsByPrefix(entity Kind, entityName string, prefix []byte) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
id, _ := mapToNumeric(ctx, entity, []byte(entityName))
keyPrefix := storageKey(entity, id, prefix)
return storage.Find(ctx, keyPrefix, storage.ValuesOnly)
}
// ListTargets iterates over targets for which rules are defined.
func ListTargets(entity Kind) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
mKey := mapKey(entity, []byte{})
return storage.Find(ctx, mKey, storage.KeysOnly|storage.RemovePrefix)
}
// ListChainNames iterates over chain names for specific target.
func ListChainNames(entity Kind, entityName string) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
id, _ := mapToNumeric(ctx, entity, []byte(entityName))
keyPrefix := storageKey(entity, id, []byte{})
return storage.Find(ctx, keyPrefix, storage.KeysOnly|storage.RemovePrefix)
}

View file

@ -1,4 +1,4 @@
name: "NeoFS Multi Signature Processing" name: "Multi Signature Processing"
safemethods: ["verify", "version"] safemethods: ["verify", "version"]
permissions: permissions:
- methods: ["update"] - methods: ["update"]

View file

@ -1,22 +1,28 @@
/* /*
Processing contract is a contract deployed in NeoFS main chain. Processing contract is a contract deployed in FrostFS mainchain.
Processing contract pays for all multi signature transaction executions when notary Processing contract pays for all multisignature transaction executions when notary
service enabled in main chain. Notary service prepares multi signed transaction, service is enabled in the mainchain. Notary service prepares multisigned transactions,
however they should contain side chain GAS to be executed. It is inconvenient to however they should contain sidechain GAS to be executed. It is inconvenient to
ask Alphabet nodes to pay for these transactions: nodes can change over time, ask Alphabet nodes to pay for these transactions: nodes can change over time,
some nodes will spend side chain GAS faster, it creates economic instability. some nodes will spend sidechain GAS faster. It leads to economic instability.
Processing contract exists to solve this issue. At the Withdraw invocation of Processing contract exists to solve this issue. At the Withdraw invocation of
NeoFS contract, user pays fee directly to this contract. This fee is used to FrostFS contract, a user pays fee directly to this contract. This fee is used to
pay for Cheque invocation of NeoFS contract that returns main chain GAS back pay for Cheque invocation of FrostFS contract that returns mainchain GAS back
to the user. Address of the Processing contract is uses as the first signer in to the user. The address of the Processing contract is used as the first signer in
the multi signature transaction. Therefore NeoVM executes Verify method of the the multisignature transaction. Therefore, NeoVM executes Verify method of the
contract and if invocation is verified, then Processing contract pays for the contract and if invocation is verified, Processing contract pays for the
execution. execution.
Contract notifications # Contract notifications
Processing contract does not produce notifications to process. Processing contract does not produce notifications to process.
# Contract storage scheme
| Key | Value | Description |
|-----------------------------|------------|----------------------------------|
| `frostfsScriptHash` | Hash160 | frostFS contract hash |
*/ */
package processing package processing

View file

@ -1,78 +1,55 @@
package processing package processing
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles" "github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
)
const (
neofsContractKey = "neofsScriptHash"
multiaddrMethod = "alphabetAddress"
) )
// OnNEP17Payment is a callback for NEP-17 compatible native GAS contract. // OnNEP17Payment is a callback for NEP-17 compatible native GAS contract.
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { func OnNEP17Payment(from interop.Hash160, amount int, data any) {
caller := runtime.GetCallingScriptHash() caller := runtime.GetCallingScriptHash()
if !common.BytesEqual(caller, []byte(gas.Hash)) { if !common.BytesEqual(caller, []byte(gas.Hash)) {
common.AbortWithMessage("processing contract accepts GAS only") common.AbortWithMessage("processing contract accepts GAS only")
} }
} }
func _deploy(data interface{}, isUpdate bool) { func _deploy(data any, isUpdate bool) {
if isUpdate { if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return return
} }
args := data.(struct {
addrNeoFS interop.Hash160
})
ctx := storage.GetContext()
if len(args.addrNeoFS) != interop.Hash160Len {
panic("incorrect length of contract script hash")
}
storage.Put(ctx, neofsContractKey, args.addrNeoFS)
runtime.Log("processing contract initialized") runtime.Log("processing contract initialized")
} }
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. It can be invoked
// only by side chain committee. // only by the sidechain committee.
func Update(script []byte, manifest []byte, data interface{}) { func Update(script []byte, manifest []byte, data any) {
blockHeight := ledger.CurrentIndex() blockHeight := ledger.CurrentIndex()
alphabetKeys := roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight)) alphabetKeys := roles.GetDesignatedByRole(roles.NeoFSAlphabet, uint32(blockHeight+1))
alphabetCommittee := common.Multiaddress(alphabetKeys, true) alphabetCommittee := common.Multiaddress(alphabetKeys, true)
if !runtime.CheckWitness(alphabetCommittee) { if !runtime.CheckWitness(alphabetCommittee) {
panic("only side chain committee can update contract") panic("only side chain committee can update contract")
} }
contract.Call(interop.Hash160(management.Hash), "update", management.UpdateWithData(script, manifest, common.AppendVersion(data))
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("processing contract updated") runtime.Log("processing contract updated")
} }
// Verify method returns true if transaction contains valid multi signature of // Verify method returns true if transaction contains valid multisignature of
// Alphabet nodes of the Inner Ring. // Alphabet nodes of the Inner Ring.
func Verify() bool { func Verify() bool {
ctx := storage.GetContext() return runtime.CheckWitness(common.AlphabetAddress())
neofsContractAddr := storage.Get(ctx, neofsContractKey).(interop.Hash160)
multiaddr := contract.Call(neofsContractAddr, multiaddrMethod, contract.ReadOnly).(interop.Hash160)
return runtime.CheckWitness(multiaddr)
} }
// Version returns version of the contract. // Version returns the version of the contract.
func Version() int { func Version() int {
return common.Version return common.Version
} }

View file

@ -1,4 +1,4 @@
name: "NeoFS Notary Proxy" name: "Notary Proxy"
safemethods: ["verify", "version"] safemethods: ["verify", "version"]
permissions: permissions:
- methods: ["update"] - methods: ["update"]

View file

@ -1,21 +1,25 @@
/* /*
Proxy contract is a contract deployed in NeoFS side chain. Proxy contract is a contract deployed in FrostFS sidechain.
Proxy contract pays for all multi signature transaction executions when notary Proxy contract pays for all multisignature transaction executions when notary
service enabled in side chain. Notary service prepares multi signed transaction, service is enabled in the sidechain. Notary service prepares multisigned transactions,
however they should contain side chain GAS to be executed. It is inconvenient to however they should contain sidechain GAS to be executed. It is inconvenient to
ask Alphabet nodes to pay for these transactions: nodes can change over time, ask Alphabet nodes to pay for these transactions: nodes can change over time,
some nodes will spend side chain GAS faster, it creates economic instability. some nodes will spend sidechain GAS faster. It leads to economic instability.
Proxy contract exists to solve this issue. While Alphabet contracts hold all Proxy contract exists to solve this issue. While Alphabet contracts hold all
side chain NEO, proxy contract holds most of the side chain GAS. Alphabet sidechain NEO, proxy contract holds most of the sidechain GAS. Alphabet
contracts emits half of the available GAS to the proxy contract. Address of the contracts emit half of the available GAS to the proxy contract. The address of the
Proxy contract is used as the first signer in the multi signature transaction. Proxy contract is used as the first signer in a multisignature transaction.
Therefore NeoVM executes Verify method of the contract and if invocation is Therefore, NeoVM executes Verify method of the contract; and if invocation is
verified, then Proxy contract pays for the execution. verified, Proxy contract pays for the execution.
Contract notifications # Contract notifications
Proxy contract does not produce notifications to process. Proxy contract does not produce notifications to process.
# Contract storage scheme
Proxy contract does not use storage
*/ */
package proxy package proxy

View file

@ -1,75 +1,95 @@
package proxy package proxy
import ( import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop" "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/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
) )
const ( const accountKeyPrefix = 'a'
netmapContractKey = "netmapScriptHash"
)
// OnNEP17Payment is a callback for NEP-17 compatible native GAS contract. // OnNEP17Payment is a callback for NEP-17 compatible native GAS contract.
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) { func OnNEP17Payment(from interop.Hash160, amount int, data any) {
caller := runtime.GetCallingScriptHash() caller := runtime.GetCallingScriptHash()
if !common.BytesEqual(caller, []byte(gas.Hash)) { if !common.BytesEqual(caller, []byte(gas.Hash)) {
common.AbortWithMessage("proxy contract accepts GAS only") common.AbortWithMessage("proxy contract accepts GAS only")
} }
} }
func _deploy(data interface{}, isUpdate bool) { // OnNEP11Payment is a callback for NEP-11 compatible NNS contract.
func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data any) {
caller := runtime.GetCallingScriptHash()
nnsHash := management.GetContractByID(1).Hash
if !common.BytesEqual(caller, []byte(nnsHash)) {
common.AbortWithMessage("proxy contract accepts NNS tokens only")
}
}
func _deploy(data any, isUpdate bool) {
if isUpdate { if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return return
} }
args := data.(struct {
addrNetmap interop.Hash160
})
ctx := storage.GetContext()
if len(args.addrNetmap) != interop.Hash160Len {
panic("incorrect length of contract script hash")
}
storage.Put(ctx, netmapContractKey, args.addrNetmap)
runtime.Log("proxy contract initialized") runtime.Log("proxy contract initialized")
} }
// Update method updates contract source code and manifest. Can be invoked // Update method updates contract source code and manifest. It can be invoked
// only by committee. // only by committee.
func Update(script []byte, manifest []byte, data interface{}) { func Update(script []byte, manifest []byte, data any) {
if !common.HasUpdateAccess() { if !common.HasUpdateAccess() {
panic("only committee can update contract") panic("only committee can update contract")
} }
contract.Call(interop.Hash160(management.Hash), "update", management.UpdateWithData(script, manifest, common.AppendVersion(data))
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("proxy contract updated") runtime.Log("proxy contract updated")
} }
// Verify method returns true if transaction contains valid multi signature of // Verify method returns true if transaction contains valid multisignature of
// Alphabet nodes of the Inner Ring. // Alphabet nodes of the Inner Ring or any of the trusted accounts added via AddAccount.
func Verify() bool { func Verify() bool {
alphabet := neo.GetCommittee() if !runtime.GetScriptContainer().Sender.Equals(runtime.GetExecutingScriptHash()) {
sig := common.Multiaddress(alphabet, false) return false
if !runtime.CheckWitness(sig) {
sig = common.Multiaddress(alphabet, true)
return runtime.CheckWitness(sig)
} }
return true signers := runtime.CurrentSigners()
ctx := storage.GetReadOnlyContext()
for i := 1; /* skip sender */ i < len(signers); i++ {
if storage.Get(ctx, append([]byte{accountKeyPrefix}, signers[i].Account...)) != nil {
return true
}
}
if runtime.CheckWitness(common.CommitteeAddress()) {
return true
}
if runtime.CheckWitness(common.AlphabetAddress()) {
return true
}
return false
} }
// Version returns version of the contract. // Version returns the version of the contract.
func Version() int { func Version() int {
return common.Version return common.Version
} }
func AddAccount(addr interop.Hash160) {
common.CheckWitness(common.CommitteeAddress())
ctx := storage.GetContext()
storage.Put(ctx, append([]byte{accountKeyPrefix}, addr...), []byte{1})
}
func RemoveAccount(addr interop.Hash160) {
common.CheckWitness(common.CommitteeAddress())
ctx := storage.GetContext()
storage.Delete(ctx, append([]byte{accountKeyPrefix}, addr...))
}

View file

@ -1,13 +0,0 @@
name: "NeoFS Reputation"
safemethods: ["get", "getByID", "listByEpoch"]
permissions:
- methods: ["update"]
events:
- name: reputationPut
parameters:
- name: epoch
type: Integer
- name: peerID
type: ByteArray
- name: value
type: ByteArray

View file

@ -1,17 +0,0 @@
/*
Reputation contract is a contract deployed in NeoFS side chain.
Inner Ring nodes produce data audit for each container in each epoch. In the end
nodes produce DataAuditResult structure that contains information about audit
progress. Reputation contract provides storage for such structures and simple
interface to iterate over available DataAuditResults on specified epoch.
During settlement process, Alphabet nodes fetch all DataAuditResult structures
from the epoch and execute balance transfers from data owners to Storage and
Inner Ring nodes, if data audit succeed.
Contract notifications
Reputation contract does not produce notifications to process.
*/
package reputation

View file

@ -1,186 +0,0 @@
package reputation
import (
"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/convert"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
)
const (
notaryDisabledKey = "notary"
reputationValuePrefix = 'r'
reputationCountPrefix = 'c'
)
func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext()
if isUpdate {
// Storage migration.
storage.Delete(ctx, []byte("contractOwner"))
it := storage.Find(ctx, []byte{}, storage.None)
for iterator.Next(it) {
kv := iterator.Value(it).([][]byte)
if string(kv[0]) == notaryDisabledKey {
continue
}
if string(kv[0]) == "ballots" {
continue
}
storage.Delete(ctx, kv[0])
rawValues := std.Deserialize(kv[1]).([][]byte)
key := getReputationKey(reputationCountPrefix, kv[0])
storage.Put(ctx, key, len(rawValues))
key[0] = reputationValuePrefix
for i := range rawValues {
newKey := append(key, convert.ToBytes(i)...)
storage.Put(ctx, newKey, rawValues[i])
}
}
return
}
args := data.(struct {
notaryDisabled bool
})
// initialize the way to collect signatures
storage.Put(ctx, notaryDisabledKey, args.notaryDisabled)
if args.notaryDisabled {
common.InitVote(ctx)
runtime.Log("reputation contract notary disabled")
}
runtime.Log("reputation contract initialized")
}
// Update method updates contract source code and manifest. Can be invoked
// only by committee.
func Update(script []byte, manifest []byte, data interface{}) {
if !common.HasUpdateAccess() {
panic("only committee can update contract")
}
contract.Call(interop.Hash160(management.Hash), "update",
contract.All, script, manifest, common.AppendVersion(data))
runtime.Log("reputation contract updated")
}
// Put method saves DataAuditResult in contract storage. Can be invoked only by
// Inner Ring nodes. Does not require multi signature invocations.
//
// Epoch is an epoch number when DataAuditResult structure was generated.
// PeerID contains public keys of Inner Ring node that produced DataAuditResult.
// Value contains stable marshaled structure of DataAuditResult.
func Put(epoch int, peerID []byte, value []byte) {
ctx := storage.GetContext()
notaryDisabled := storage.Get(ctx, notaryDisabledKey).(bool)
var ( // for invocation collection without notary
alphabet []common.IRNode
nodeKey []byte
alphabetCall bool
)
if notaryDisabled {
alphabet = common.AlphabetNodes()
nodeKey = common.InnerRingInvoker(alphabet)
alphabetCall = len(nodeKey) != 0
} else {
multiaddr := common.AlphabetAddress()
alphabetCall = runtime.CheckWitness(multiaddr)
}
if !alphabetCall {
runtime.Notify("reputationPut", epoch, peerID, value)
return
}
id := storageID(epoch, peerID)
if notaryDisabled {
threshold := len(alphabet)*2/3 + 1
n := common.Vote(ctx, id, nodeKey)
if n < threshold {
return
}
common.RemoveVotes(ctx, id)
}
key := getReputationKey(reputationCountPrefix, id)
rawCnt := storage.Get(ctx, key)
cnt := 0
if rawCnt != nil {
cnt = rawCnt.(int)
}
cnt++
storage.Put(ctx, key, cnt)
key[0] = reputationValuePrefix
key = append(key, convert.ToBytes(cnt)...)
storage.Put(ctx, key, value)
}
// Get method returns list of all stable marshaled DataAuditResult structures
// produced by specified Inner Ring node in specified epoch.
func Get(epoch int, peerID []byte) [][]byte {
id := storageID(epoch, peerID)
return GetByID(id)
}
// GetByID method returns list of all stable marshaled DataAuditResult with
// specified id. Use ListByEpoch method to obtain id.
func GetByID(id []byte) [][]byte {
ctx := storage.GetReadOnlyContext()
var data [][]byte
it := storage.Find(ctx, getReputationKey(reputationValuePrefix, id), storage.ValuesOnly)
for iterator.Next(it) {
data = append(data, iterator.Value(it).([]byte))
}
return data
}
func getReputationKey(prefix byte, id []byte) []byte {
return append([]byte{prefix}, id...)
}
// ListByEpoch returns list of IDs that may be used to get reputation data
// with GetByID method.
func ListByEpoch(epoch int) [][]byte {
ctx := storage.GetReadOnlyContext()
key := getReputationKey(reputationCountPrefix, convert.ToBytes(epoch))
it := storage.Find(ctx, key, storage.KeysOnly)
var result [][]byte
for iterator.Next(it) {
key := iterator.Value(it).([]byte) // iterator MUST BE `storage.KeysOnly`
result = append(result, key[1:])
}
return result
}
// Version returns version of the contract.
func Version() int {
return common.Version
}
func storageID(epoch int, peerID []byte) []byte {
var buf interface{} = epoch
return append(buf.([]byte), peerID...)
}

View file

@ -0,0 +1,138 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package alphabet contains RPC wrappers for Alphabet contract.
package alphabet
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash}
}
// Gas invokes `gas` method of contract.
func (c *ContractReader) Gas() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "gas"))
}
// Name invokes `name` method of contract.
func (c *ContractReader) Name() (string, error) {
return unwrap.UTF8String(c.invoker.Call(c.hash, "name"))
}
// Neo invokes `neo` method of contract.
func (c *ContractReader) Neo() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "neo"))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// Emit creates a transaction invoking `emit` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Emit() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "emit")
}
// EmitTransaction creates a transaction invoking `emit` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) EmitTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "emit")
}
// EmitUnsigned creates a transaction invoking `emit` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) EmitUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "emit", nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}
// Vote creates a transaction invoking `vote` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Vote(epoch *big.Int, candidates []any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "vote", epoch, candidates)
}
// VoteTransaction creates a transaction invoking `vote` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VoteTransaction(epoch *big.Int, candidates []any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "vote", epoch, candidates)
}
// VoteUnsigned creates a transaction invoking `vote` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VoteUnsigned(epoch *big.Int, candidates []any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "vote", nil, epoch, candidates)
}

549
rpcclient/balance/client.go Normal file
View file

@ -0,0 +1,549 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package balance contains RPC wrappers for Balance contract.
package balance
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// LockEvent represents "Lock" event emitted by the contract.
type LockEvent struct {
TxID []byte
From util.Uint160
To util.Uint160
Amount *big.Int
Until *big.Int
}
// TransferXEvent represents "TransferX" event emitted by the contract.
type TransferXEvent struct {
From util.Uint160
To util.Uint160
Amount *big.Int
Details []byte
}
// MintEvent represents "Mint" event emitted by the contract.
type MintEvent struct {
To util.Uint160
Amount *big.Int
}
// BurnEvent represents "Burn" event emitted by the contract.
type BurnEvent struct {
From util.Uint160
Amount *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep17.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep17.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep17.TokenReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep17.TokenWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{*nep17.NewReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
var nep17t = nep17.New(actor, hash)
return &Contract{ContractReader{nep17t.TokenReader, actor, hash}, nep17t.TokenWriter, actor, hash}
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// Burn creates a transaction invoking `burn` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Burn(from util.Uint160, amount *big.Int, txDetails []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "burn", from, amount, txDetails)
}
// BurnTransaction creates a transaction invoking `burn` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) BurnTransaction(from util.Uint160, amount *big.Int, txDetails []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "burn", from, amount, txDetails)
}
// BurnUnsigned creates a transaction invoking `burn` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) BurnUnsigned(from util.Uint160, amount *big.Int, txDetails []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "burn", nil, from, amount, txDetails)
}
// Lock creates a transaction invoking `lock` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Lock(txDetails []byte, from util.Uint160, to util.Uint160, amount *big.Int, until *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "lock", txDetails, from, to, amount, until)
}
// LockTransaction creates a transaction invoking `lock` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) LockTransaction(txDetails []byte, from util.Uint160, to util.Uint160, amount *big.Int, until *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "lock", txDetails, from, to, amount, until)
}
// LockUnsigned creates a transaction invoking `lock` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) LockUnsigned(txDetails []byte, from util.Uint160, to util.Uint160, amount *big.Int, until *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "lock", nil, txDetails, from, to, amount, until)
}
// Mint creates a transaction invoking `mint` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Mint(to util.Uint160, amount *big.Int, txDetails []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "mint", to, amount, txDetails)
}
// MintTransaction creates a transaction invoking `mint` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) MintTransaction(to util.Uint160, amount *big.Int, txDetails []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "mint", to, amount, txDetails)
}
// MintUnsigned creates a transaction invoking `mint` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) MintUnsigned(to util.Uint160, amount *big.Int, txDetails []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "mint", nil, to, amount, txDetails)
}
// NewEpoch creates a transaction invoking `newEpoch` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) NewEpoch(epochNum *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "newEpoch", epochNum)
}
// NewEpochTransaction creates a transaction invoking `newEpoch` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) NewEpochTransaction(epochNum *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "newEpoch", epochNum)
}
// NewEpochUnsigned creates a transaction invoking `newEpoch` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) NewEpochUnsigned(epochNum *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "newEpoch", nil, epochNum)
}
// TransferX creates a transaction invoking `transferX` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) TransferX(from util.Uint160, to util.Uint160, amount *big.Int, details []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "transferX", from, to, amount, details)
}
// TransferXTransaction creates a transaction invoking `transferX` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) TransferXTransaction(from util.Uint160, to util.Uint160, amount *big.Int, details []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "transferX", from, to, amount, details)
}
// TransferXUnsigned creates a transaction invoking `transferX` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) TransferXUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, details []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "transferX", nil, from, to, amount, details)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}
// LockEventsFromApplicationLog retrieves a set of all emitted events
// with "Lock" name from the provided [result.ApplicationLog].
func LockEventsFromApplicationLog(log *result.ApplicationLog) ([]*LockEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*LockEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Lock" {
continue
}
event := new(LockEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize LockEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to LockEvent or
// returns an error if it's not possible to do to so.
func (e *LockEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 5 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.TxID, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field TxID: %w", err)
}
index++
e.From, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field From: %w", err)
}
index++
e.To, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field To: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
index++
e.Until, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Until: %w", err)
}
return nil
}
// TransferXEventsFromApplicationLog retrieves a set of all emitted events
// with "TransferX" name from the provided [result.ApplicationLog].
func TransferXEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferXEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*TransferXEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "TransferX" {
continue
}
event := new(TransferXEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize TransferXEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to TransferXEvent or
// returns an error if it's not possible to do to so.
func (e *TransferXEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 4 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.From, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field From: %w", err)
}
index++
e.To, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field To: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
index++
e.Details, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field Details: %w", err)
}
return nil
}
// MintEventsFromApplicationLog retrieves a set of all emitted events
// with "Mint" name from the provided [result.ApplicationLog].
func MintEventsFromApplicationLog(log *result.ApplicationLog) ([]*MintEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*MintEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Mint" {
continue
}
event := new(MintEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize MintEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to MintEvent or
// returns an error if it's not possible to do to so.
func (e *MintEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.To, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field To: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
return nil
}
// BurnEventsFromApplicationLog retrieves a set of all emitted events
// with "Burn" name from the provided [result.ApplicationLog].
func BurnEventsFromApplicationLog(log *result.ApplicationLog) ([]*BurnEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*BurnEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Burn" {
continue
}
event := new(BurnEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize BurnEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to BurnEvent or
// returns an error if it's not possible to do to so.
func (e *BurnEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.From, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field From: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
return nil
}

View file

@ -0,0 +1,661 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package container contains RPC wrappers for Container contract.
package container
import (
"crypto/elliptic"
"errors"
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// PutSuccessEvent represents "PutSuccess" event emitted by the contract.
type PutSuccessEvent struct {
ContainerID util.Uint256
PublicKey *keys.PublicKey
}
// DeleteSuccessEvent represents "DeleteSuccess" event emitted by the contract.
type DeleteSuccessEvent struct {
ContainerID []byte
}
// SetEACLSuccessEvent represents "SetEACLSuccess" event emitted by the contract.
type SetEACLSuccessEvent struct {
ContainerID []byte
PublicKey *keys.PublicKey
}
// StartEstimationEvent represents "StartEstimation" event emitted by the contract.
type StartEstimationEvent struct {
Epoch *big.Int
}
// StopEstimationEvent represents "StopEstimation" event emitted by the contract.
type StopEstimationEvent struct {
Epoch *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error)
TerminateSession(sessionID uuid.UUID) error
TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error)
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash}
}
// ContainersOf invokes `containersOf` method of contract.
func (c *ContractReader) ContainersOf(owner []byte) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "containersOf", owner))
}
// ContainersOfExpanded is similar to ContainersOf (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) ContainersOfExpanded(owner []byte, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "containersOf", _numOfIteratorItems, owner))
}
// Count invokes `count` method of contract.
func (c *ContractReader) Count() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "count"))
}
// DeletionInfo invokes `deletionInfo` method of contract.
func (c *ContractReader) DeletionInfo(containerID []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "deletionInfo", containerID))
}
// EACL invokes `eACL` method of contract.
func (c *ContractReader) EACL(containerID []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "eACL", containerID))
}
// Get invokes `get` method of contract.
func (c *ContractReader) Get(containerID []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "get", containerID))
}
// GetContainerSize invokes `getContainerSize` method of contract.
func (c *ContractReader) GetContainerSize(id []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "getContainerSize", id))
}
// IterateContainerSizes invokes `iterateContainerSizes` method of contract.
func (c *ContractReader) IterateContainerSizes(epoch *big.Int) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "iterateContainerSizes", epoch))
}
// IterateContainerSizesExpanded is similar to IterateContainerSizes (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) IterateContainerSizesExpanded(epoch *big.Int, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "iterateContainerSizes", _numOfIteratorItems, epoch))
}
// List invokes `list` method of contract.
func (c *ContractReader) List(owner []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "list", owner))
}
// ListContainerSizes invokes `listContainerSizes` method of contract.
func (c *ContractReader) ListContainerSizes(epoch *big.Int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listContainerSizes", epoch))
}
// Owner invokes `owner` method of contract.
func (c *ContractReader) Owner(containerID []byte) ([]byte, error) {
return unwrap.Bytes(c.invoker.Call(c.hash, "owner", containerID))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// Delete creates a transaction invoking `delete` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Delete(containerID []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "delete", containerID, signature, publicKey, token)
}
// DeleteTransaction creates a transaction invoking `delete` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DeleteTransaction(containerID []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "delete", containerID, signature, publicKey, token)
}
// DeleteUnsigned creates a transaction invoking `delete` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DeleteUnsigned(containerID []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "delete", nil, containerID, signature, publicKey, token)
}
// NewEpoch creates a transaction invoking `newEpoch` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) NewEpoch(epochNum *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "newEpoch", epochNum)
}
// NewEpochTransaction creates a transaction invoking `newEpoch` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) NewEpochTransaction(epochNum *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "newEpoch", epochNum)
}
// NewEpochUnsigned creates a transaction invoking `newEpoch` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) NewEpochUnsigned(epochNum *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "newEpoch", nil, epochNum)
}
// Put creates a transaction invoking `put` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Put(container []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "put", container, signature, publicKey, token)
}
// PutTransaction creates a transaction invoking `put` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) PutTransaction(container []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "put", container, signature, publicKey, token)
}
// PutUnsigned creates a transaction invoking `put` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) PutUnsigned(container []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "put", nil, container, signature, publicKey, token)
}
// PutContainerSize creates a transaction invoking `putContainerSize` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) PutContainerSize(epoch *big.Int, cid []byte, usedSize *big.Int, pubKey *keys.PublicKey) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "putContainerSize", epoch, cid, usedSize, pubKey)
}
// PutContainerSizeTransaction creates a transaction invoking `putContainerSize` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) PutContainerSizeTransaction(epoch *big.Int, cid []byte, usedSize *big.Int, pubKey *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "putContainerSize", epoch, cid, usedSize, pubKey)
}
// PutContainerSizeUnsigned creates a transaction invoking `putContainerSize` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) PutContainerSizeUnsigned(epoch *big.Int, cid []byte, usedSize *big.Int, pubKey *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "putContainerSize", nil, epoch, cid, usedSize, pubKey)
}
// PutNamed creates a transaction invoking `putNamed` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) PutNamed(container []byte, signature []byte, publicKey *keys.PublicKey, token []byte, name string, zone string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "putNamed", container, signature, publicKey, token, name, zone)
}
// PutNamedTransaction creates a transaction invoking `putNamed` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) PutNamedTransaction(container []byte, signature []byte, publicKey *keys.PublicKey, token []byte, name string, zone string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "putNamed", container, signature, publicKey, token, name, zone)
}
// PutNamedUnsigned creates a transaction invoking `putNamed` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) PutNamedUnsigned(container []byte, signature []byte, publicKey *keys.PublicKey, token []byte, name string, zone string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "putNamed", nil, container, signature, publicKey, token, name, zone)
}
// SetEACL creates a transaction invoking `setEACL` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetEACL(eACL []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setEACL", eACL, signature, publicKey, token)
}
// SetEACLTransaction creates a transaction invoking `setEACL` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetEACLTransaction(eACL []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setEACL", eACL, signature, publicKey, token)
}
// SetEACLUnsigned creates a transaction invoking `setEACL` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetEACLUnsigned(eACL []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setEACL", nil, eACL, signature, publicKey, token)
}
// StartContainerEstimation creates a transaction invoking `startContainerEstimation` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) StartContainerEstimation(epoch *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "startContainerEstimation", epoch)
}
// StartContainerEstimationTransaction creates a transaction invoking `startContainerEstimation` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) StartContainerEstimationTransaction(epoch *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "startContainerEstimation", epoch)
}
// StartContainerEstimationUnsigned creates a transaction invoking `startContainerEstimation` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) StartContainerEstimationUnsigned(epoch *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "startContainerEstimation", nil, epoch)
}
// StopContainerEstimation creates a transaction invoking `stopContainerEstimation` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) StopContainerEstimation(epoch *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "stopContainerEstimation", epoch)
}
// StopContainerEstimationTransaction creates a transaction invoking `stopContainerEstimation` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) StopContainerEstimationTransaction(epoch *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "stopContainerEstimation", epoch)
}
// StopContainerEstimationUnsigned creates a transaction invoking `stopContainerEstimation` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) StopContainerEstimationUnsigned(epoch *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "stopContainerEstimation", nil, epoch)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}
// PutSuccessEventsFromApplicationLog retrieves a set of all emitted events
// with "PutSuccess" name from the provided [result.ApplicationLog].
func PutSuccessEventsFromApplicationLog(log *result.ApplicationLog) ([]*PutSuccessEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*PutSuccessEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "PutSuccess" {
continue
}
event := new(PutSuccessEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize PutSuccessEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to PutSuccessEvent or
// returns an error if it's not possible to do to so.
func (e *PutSuccessEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.ContainerID, err = func(item stackitem.Item) (util.Uint256, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint256{}, err
}
u, err := util.Uint256DecodeBytesBE(b)
if err != nil {
return util.Uint256{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field ContainerID: %w", err)
}
index++
e.PublicKey, err = func(item stackitem.Item) (*keys.PublicKey, error) {
b, err := item.TryBytes()
if err != nil {
return nil, err
}
k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256())
if err != nil {
return nil, err
}
return k, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field PublicKey: %w", err)
}
return nil
}
// DeleteSuccessEventsFromApplicationLog retrieves a set of all emitted events
// with "DeleteSuccess" name from the provided [result.ApplicationLog].
func DeleteSuccessEventsFromApplicationLog(log *result.ApplicationLog) ([]*DeleteSuccessEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*DeleteSuccessEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "DeleteSuccess" {
continue
}
event := new(DeleteSuccessEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize DeleteSuccessEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to DeleteSuccessEvent or
// returns an error if it's not possible to do to so.
func (e *DeleteSuccessEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.ContainerID, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field ContainerID: %w", err)
}
return nil
}
// SetEACLSuccessEventsFromApplicationLog retrieves a set of all emitted events
// with "SetEACLSuccess" name from the provided [result.ApplicationLog].
func SetEACLSuccessEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetEACLSuccessEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SetEACLSuccessEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SetEACLSuccess" {
continue
}
event := new(SetEACLSuccessEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SetEACLSuccessEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SetEACLSuccessEvent or
// returns an error if it's not possible to do to so.
func (e *SetEACLSuccessEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.ContainerID, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field ContainerID: %w", err)
}
index++
e.PublicKey, err = func(item stackitem.Item) (*keys.PublicKey, error) {
b, err := item.TryBytes()
if err != nil {
return nil, err
}
k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256())
if err != nil {
return nil, err
}
return k, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field PublicKey: %w", err)
}
return nil
}
// StartEstimationEventsFromApplicationLog retrieves a set of all emitted events
// with "StartEstimation" name from the provided [result.ApplicationLog].
func StartEstimationEventsFromApplicationLog(log *result.ApplicationLog) ([]*StartEstimationEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*StartEstimationEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "StartEstimation" {
continue
}
event := new(StartEstimationEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize StartEstimationEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to StartEstimationEvent or
// returns an error if it's not possible to do to so.
func (e *StartEstimationEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Epoch, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Epoch: %w", err)
}
return nil
}
// StopEstimationEventsFromApplicationLog retrieves a set of all emitted events
// with "StopEstimation" name from the provided [result.ApplicationLog].
func StopEstimationEventsFromApplicationLog(log *result.ApplicationLog) ([]*StopEstimationEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*StopEstimationEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "StopEstimation" {
continue
}
event := new(StopEstimationEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize StopEstimationEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to StopEstimationEvent or
// returns an error if it's not possible to do to so.
func (e *StopEstimationEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Epoch, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Epoch: %w", err)
}
return nil
}

846
rpcclient/frostfs/client.go Normal file
View file

@ -0,0 +1,846 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package frostfs contains RPC wrappers for FrostFS contract.
package frostfs
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// DepositEvent represents "Deposit" event emitted by the contract.
type DepositEvent struct {
From util.Uint160
Amount *big.Int
Receiver util.Uint160
TxHash util.Uint256
}
// WithdrawEvent represents "Withdraw" event emitted by the contract.
type WithdrawEvent struct {
User util.Uint160
Amount *big.Int
TxHash util.Uint256
}
// ChequeEvent represents "Cheque" event emitted by the contract.
type ChequeEvent struct {
Id []byte
User util.Uint160
Amount *big.Int
LockAccount []byte
}
// BindEvent represents "Bind" event emitted by the contract.
type BindEvent struct {
User []byte
Keys []any
}
// UnbindEvent represents "Unbind" event emitted by the contract.
type UnbindEvent struct {
User []byte
Keys []any
}
// AlphabetUpdateEvent represents "AlphabetUpdate" event emitted by the contract.
type AlphabetUpdateEvent struct {
Id []byte
Alphabet []any
}
// SetConfigEvent represents "SetConfig" event emitted by the contract.
type SetConfigEvent struct {
Id []byte
Key []byte
Value []byte
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash}
}
// Config invokes `config` method of contract.
func (c *ContractReader) Config(key []byte) (any, error) {
return func(item stackitem.Item, err error) (any, error) {
if err != nil {
return nil, err
}
return item.Value(), error(nil)
}(unwrap.Item(c.invoker.Call(c.hash, "config", key)))
}
// InnerRingCandidates invokes `innerRingCandidates` method of contract.
func (c *ContractReader) InnerRingCandidates() ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "innerRingCandidates"))
}
// ListConfig invokes `listConfig` method of contract.
func (c *ContractReader) ListConfig() ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listConfig"))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// Bind creates a transaction invoking `bind` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Bind(user []byte, keys []any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "bind", user, keys)
}
// BindTransaction creates a transaction invoking `bind` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) BindTransaction(user []byte, keys []any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "bind", user, keys)
}
// BindUnsigned creates a transaction invoking `bind` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) BindUnsigned(user []byte, keys []any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "bind", nil, user, keys)
}
// Cheque creates a transaction invoking `cheque` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Cheque(id []byte, user util.Uint160, amount *big.Int, lockAcc []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "cheque", id, user, amount, lockAcc)
}
// ChequeTransaction creates a transaction invoking `cheque` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ChequeTransaction(id []byte, user util.Uint160, amount *big.Int, lockAcc []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "cheque", id, user, amount, lockAcc)
}
// ChequeUnsigned creates a transaction invoking `cheque` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ChequeUnsigned(id []byte, user util.Uint160, amount *big.Int, lockAcc []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "cheque", nil, id, user, amount, lockAcc)
}
// InnerRingCandidateAdd creates a transaction invoking `innerRingCandidateAdd` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) InnerRingCandidateAdd(key *keys.PublicKey) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "innerRingCandidateAdd", key)
}
// InnerRingCandidateAddTransaction creates a transaction invoking `innerRingCandidateAdd` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) InnerRingCandidateAddTransaction(key *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "innerRingCandidateAdd", key)
}
// InnerRingCandidateAddUnsigned creates a transaction invoking `innerRingCandidateAdd` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) InnerRingCandidateAddUnsigned(key *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "innerRingCandidateAdd", nil, key)
}
// InnerRingCandidateRemove creates a transaction invoking `innerRingCandidateRemove` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) InnerRingCandidateRemove(key *keys.PublicKey) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "innerRingCandidateRemove", key)
}
// InnerRingCandidateRemoveTransaction creates a transaction invoking `innerRingCandidateRemove` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) InnerRingCandidateRemoveTransaction(key *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "innerRingCandidateRemove", key)
}
// InnerRingCandidateRemoveUnsigned creates a transaction invoking `innerRingCandidateRemove` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) InnerRingCandidateRemoveUnsigned(key *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "innerRingCandidateRemove", nil, key)
}
// SetConfig creates a transaction invoking `setConfig` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetConfig(id []byte, key []byte, val []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setConfig", id, key, val)
}
// SetConfigTransaction creates a transaction invoking `setConfig` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetConfigTransaction(id []byte, key []byte, val []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setConfig", id, key, val)
}
// SetConfigUnsigned creates a transaction invoking `setConfig` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetConfigUnsigned(id []byte, key []byte, val []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setConfig", nil, id, key, val)
}
// Unbind creates a transaction invoking `unbind` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Unbind(user []byte, keys []any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "unbind", user, keys)
}
// UnbindTransaction creates a transaction invoking `unbind` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UnbindTransaction(user []byte, keys []any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "unbind", user, keys)
}
// UnbindUnsigned creates a transaction invoking `unbind` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UnbindUnsigned(user []byte, keys []any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "unbind", nil, user, keys)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}
// Withdraw creates a transaction invoking `withdraw` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Withdraw(user util.Uint160, amount *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "withdraw", user, amount)
}
// WithdrawTransaction creates a transaction invoking `withdraw` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) WithdrawTransaction(user util.Uint160, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "withdraw", user, amount)
}
// WithdrawUnsigned creates a transaction invoking `withdraw` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) WithdrawUnsigned(user util.Uint160, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "withdraw", nil, user, amount)
}
// DepositEventsFromApplicationLog retrieves a set of all emitted events
// with "Deposit" name from the provided [result.ApplicationLog].
func DepositEventsFromApplicationLog(log *result.ApplicationLog) ([]*DepositEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*DepositEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Deposit" {
continue
}
event := new(DepositEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize DepositEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to DepositEvent or
// returns an error if it's not possible to do to so.
func (e *DepositEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 4 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.From, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field From: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
index++
e.Receiver, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Receiver: %w", err)
}
index++
e.TxHash, err = func(item stackitem.Item) (util.Uint256, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint256{}, err
}
u, err := util.Uint256DecodeBytesBE(b)
if err != nil {
return util.Uint256{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field TxHash: %w", err)
}
return nil
}
// WithdrawEventsFromApplicationLog retrieves a set of all emitted events
// with "Withdraw" name from the provided [result.ApplicationLog].
func WithdrawEventsFromApplicationLog(log *result.ApplicationLog) ([]*WithdrawEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*WithdrawEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Withdraw" {
continue
}
event := new(WithdrawEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize WithdrawEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to WithdrawEvent or
// returns an error if it's not possible to do to so.
func (e *WithdrawEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 3 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.User, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field User: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
index++
e.TxHash, err = func(item stackitem.Item) (util.Uint256, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint256{}, err
}
u, err := util.Uint256DecodeBytesBE(b)
if err != nil {
return util.Uint256{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field TxHash: %w", err)
}
return nil
}
// ChequeEventsFromApplicationLog retrieves a set of all emitted events
// with "Cheque" name from the provided [result.ApplicationLog].
func ChequeEventsFromApplicationLog(log *result.ApplicationLog) ([]*ChequeEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*ChequeEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Cheque" {
continue
}
event := new(ChequeEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize ChequeEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to ChequeEvent or
// returns an error if it's not possible to do to so.
func (e *ChequeEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 4 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Id, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field Id: %w", err)
}
index++
e.User, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field User: %w", err)
}
index++
e.Amount, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Amount: %w", err)
}
index++
e.LockAccount, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field LockAccount: %w", err)
}
return nil
}
// BindEventsFromApplicationLog retrieves a set of all emitted events
// with "Bind" name from the provided [result.ApplicationLog].
func BindEventsFromApplicationLog(log *result.ApplicationLog) ([]*BindEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*BindEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Bind" {
continue
}
event := new(BindEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize BindEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to BindEvent or
// returns an error if it's not possible to do to so.
func (e *BindEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.User, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field User: %w", err)
}
index++
e.Keys, err = func(item stackitem.Item) ([]any, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]any, len(arr))
for i := range res {
res[i], err = arr[i].Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Keys: %w", err)
}
return nil
}
// UnbindEventsFromApplicationLog retrieves a set of all emitted events
// with "Unbind" name from the provided [result.ApplicationLog].
func UnbindEventsFromApplicationLog(log *result.ApplicationLog) ([]*UnbindEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*UnbindEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "Unbind" {
continue
}
event := new(UnbindEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize UnbindEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to UnbindEvent or
// returns an error if it's not possible to do to so.
func (e *UnbindEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.User, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field User: %w", err)
}
index++
e.Keys, err = func(item stackitem.Item) ([]any, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]any, len(arr))
for i := range res {
res[i], err = arr[i].Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Keys: %w", err)
}
return nil
}
// AlphabetUpdateEventsFromApplicationLog retrieves a set of all emitted events
// with "AlphabetUpdate" name from the provided [result.ApplicationLog].
func AlphabetUpdateEventsFromApplicationLog(log *result.ApplicationLog) ([]*AlphabetUpdateEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*AlphabetUpdateEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "AlphabetUpdate" {
continue
}
event := new(AlphabetUpdateEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize AlphabetUpdateEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to AlphabetUpdateEvent or
// returns an error if it's not possible to do to so.
func (e *AlphabetUpdateEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Id, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field Id: %w", err)
}
index++
e.Alphabet, err = func(item stackitem.Item) ([]any, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]any, len(arr))
for i := range res {
res[i], err = arr[i].Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Alphabet: %w", err)
}
return nil
}
// SetConfigEventsFromApplicationLog retrieves a set of all emitted events
// with "SetConfig" name from the provided [result.ApplicationLog].
func SetConfigEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetConfigEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SetConfigEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SetConfig" {
continue
}
event := new(SetConfigEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SetConfigEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to SetConfigEvent or
// returns an error if it's not possible to do to so.
func (e *SetConfigEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 3 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Id, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field Id: %w", err)
}
index++
e.Key, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field Key: %w", err)
}
index++
e.Value, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field Value: %w", err)
}
return nil
}

File diff suppressed because it is too large Load diff

629
rpcclient/netmap/client.go Normal file
View file

@ -0,0 +1,629 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package netmap contains RPC wrappers for Netmap contract.
package netmap
import (
"crypto/elliptic"
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// AddPeerEvent represents "AddPeer" event emitted by the contract.
type AddPeerEvent struct {
NodeInfo []byte
}
// AddPeerSuccessEvent represents "AddPeerSuccess" event emitted by the contract.
type AddPeerSuccessEvent struct {
PublicKey *keys.PublicKey
}
// UpdateStateEvent represents "UpdateState" event emitted by the contract.
type UpdateStateEvent struct {
State *big.Int
PublicKey *keys.PublicKey
}
// UpdateStateSuccessEvent represents "UpdateStateSuccess" event emitted by the contract.
type UpdateStateSuccessEvent struct {
PublicKey *keys.PublicKey
State *big.Int
}
// NewEpochEvent represents "NewEpoch" event emitted by the contract.
type NewEpochEvent struct {
Epoch *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash}
}
// Config invokes `config` method of contract.
func (c *ContractReader) Config(key []byte) (any, error) {
return func(item stackitem.Item, err error) (any, error) {
if err != nil {
return nil, err
}
return item.Value(), error(nil)
}(unwrap.Item(c.invoker.Call(c.hash, "config", key)))
}
// Epoch invokes `epoch` method of contract.
func (c *ContractReader) Epoch() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "epoch"))
}
// ListConfig invokes `listConfig` method of contract.
func (c *ContractReader) ListConfig() ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listConfig"))
}
// Netmap invokes `netmap` method of contract.
func (c *ContractReader) Netmap() ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "netmap"))
}
// NetmapCandidates invokes `netmapCandidates` method of contract.
func (c *ContractReader) NetmapCandidates() ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "netmapCandidates"))
}
// Snapshot invokes `snapshot` method of contract.
func (c *ContractReader) Snapshot(diff *big.Int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "snapshot", diff))
}
// SnapshotByEpoch invokes `snapshotByEpoch` method of contract.
func (c *ContractReader) SnapshotByEpoch(epoch *big.Int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "snapshotByEpoch", epoch))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// AddPeer creates a transaction invoking `addPeer` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) AddPeer(nodeInfo []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "addPeer", nodeInfo)
}
// AddPeerTransaction creates a transaction invoking `addPeer` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) AddPeerTransaction(nodeInfo []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "addPeer", nodeInfo)
}
// AddPeerUnsigned creates a transaction invoking `addPeer` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) AddPeerUnsigned(nodeInfo []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "addPeer", nil, nodeInfo)
}
// AddPeerIR creates a transaction invoking `addPeerIR` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) AddPeerIR(nodeInfo []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "addPeerIR", nodeInfo)
}
// AddPeerIRTransaction creates a transaction invoking `addPeerIR` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) AddPeerIRTransaction(nodeInfo []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "addPeerIR", nodeInfo)
}
// AddPeerIRUnsigned creates a transaction invoking `addPeerIR` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) AddPeerIRUnsigned(nodeInfo []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "addPeerIR", nil, nodeInfo)
}
// LastEpochBlock creates a transaction invoking `lastEpochBlock` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) LastEpochBlock() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "lastEpochBlock")
}
// LastEpochBlockTransaction creates a transaction invoking `lastEpochBlock` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) LastEpochBlockTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "lastEpochBlock")
}
// LastEpochBlockUnsigned creates a transaction invoking `lastEpochBlock` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) LastEpochBlockUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "lastEpochBlock", nil)
}
// NewEpoch creates a transaction invoking `newEpoch` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) NewEpoch(epochNum *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "newEpoch", epochNum)
}
// NewEpochTransaction creates a transaction invoking `newEpoch` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) NewEpochTransaction(epochNum *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "newEpoch", epochNum)
}
// NewEpochUnsigned creates a transaction invoking `newEpoch` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) NewEpochUnsigned(epochNum *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "newEpoch", nil, epochNum)
}
// SetConfig creates a transaction invoking `setConfig` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetConfig(id []byte, key []byte, val []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setConfig", id, key, val)
}
// SetConfigTransaction creates a transaction invoking `setConfig` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetConfigTransaction(id []byte, key []byte, val []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setConfig", id, key, val)
}
// SetConfigUnsigned creates a transaction invoking `setConfig` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetConfigUnsigned(id []byte, key []byte, val []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setConfig", nil, id, key, val)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}
// UpdateSnapshotCount creates a transaction invoking `updateSnapshotCount` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UpdateSnapshotCount(count *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "updateSnapshotCount", count)
}
// UpdateSnapshotCountTransaction creates a transaction invoking `updateSnapshotCount` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateSnapshotCountTransaction(count *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "updateSnapshotCount", count)
}
// UpdateSnapshotCountUnsigned creates a transaction invoking `updateSnapshotCount` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateSnapshotCountUnsigned(count *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "updateSnapshotCount", nil, count)
}
// UpdateState creates a transaction invoking `updateState` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UpdateState(state *big.Int, publicKey *keys.PublicKey) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "updateState", state, publicKey)
}
// UpdateStateTransaction creates a transaction invoking `updateState` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateStateTransaction(state *big.Int, publicKey *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "updateState", state, publicKey)
}
// UpdateStateUnsigned creates a transaction invoking `updateState` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateStateUnsigned(state *big.Int, publicKey *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "updateState", nil, state, publicKey)
}
// UpdateStateIR creates a transaction invoking `updateStateIR` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UpdateStateIR(state *big.Int, publicKey *keys.PublicKey) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "updateStateIR", state, publicKey)
}
// UpdateStateIRTransaction creates a transaction invoking `updateStateIR` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateStateIRTransaction(state *big.Int, publicKey *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "updateStateIR", state, publicKey)
}
// UpdateStateIRUnsigned creates a transaction invoking `updateStateIR` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateStateIRUnsigned(state *big.Int, publicKey *keys.PublicKey) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "updateStateIR", nil, state, publicKey)
}
// AddPeerEventsFromApplicationLog retrieves a set of all emitted events
// with "AddPeer" name from the provided [result.ApplicationLog].
func AddPeerEventsFromApplicationLog(log *result.ApplicationLog) ([]*AddPeerEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*AddPeerEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "AddPeer" {
continue
}
event := new(AddPeerEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize AddPeerEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to AddPeerEvent or
// returns an error if it's not possible to do to so.
func (e *AddPeerEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.NodeInfo, err = arr[index].TryBytes()
if err != nil {
return fmt.Errorf("field NodeInfo: %w", err)
}
return nil
}
// AddPeerSuccessEventsFromApplicationLog retrieves a set of all emitted events
// with "AddPeerSuccess" name from the provided [result.ApplicationLog].
func AddPeerSuccessEventsFromApplicationLog(log *result.ApplicationLog) ([]*AddPeerSuccessEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*AddPeerSuccessEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "AddPeerSuccess" {
continue
}
event := new(AddPeerSuccessEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize AddPeerSuccessEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to AddPeerSuccessEvent or
// returns an error if it's not possible to do to so.
func (e *AddPeerSuccessEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.PublicKey, err = func(item stackitem.Item) (*keys.PublicKey, error) {
b, err := item.TryBytes()
if err != nil {
return nil, err
}
k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256())
if err != nil {
return nil, err
}
return k, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field PublicKey: %w", err)
}
return nil
}
// UpdateStateEventsFromApplicationLog retrieves a set of all emitted events
// with "UpdateState" name from the provided [result.ApplicationLog].
func UpdateStateEventsFromApplicationLog(log *result.ApplicationLog) ([]*UpdateStateEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*UpdateStateEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "UpdateState" {
continue
}
event := new(UpdateStateEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize UpdateStateEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to UpdateStateEvent or
// returns an error if it's not possible to do to so.
func (e *UpdateStateEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.State, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field State: %w", err)
}
index++
e.PublicKey, err = func(item stackitem.Item) (*keys.PublicKey, error) {
b, err := item.TryBytes()
if err != nil {
return nil, err
}
k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256())
if err != nil {
return nil, err
}
return k, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field PublicKey: %w", err)
}
return nil
}
// UpdateStateSuccessEventsFromApplicationLog retrieves a set of all emitted events
// with "UpdateStateSuccess" name from the provided [result.ApplicationLog].
func UpdateStateSuccessEventsFromApplicationLog(log *result.ApplicationLog) ([]*UpdateStateSuccessEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*UpdateStateSuccessEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "UpdateStateSuccess" {
continue
}
event := new(UpdateStateSuccessEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize UpdateStateSuccessEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to UpdateStateSuccessEvent or
// returns an error if it's not possible to do to so.
func (e *UpdateStateSuccessEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.PublicKey, err = func(item stackitem.Item) (*keys.PublicKey, error) {
b, err := item.TryBytes()
if err != nil {
return nil, err
}
k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256())
if err != nil {
return nil, err
}
return k, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field PublicKey: %w", err)
}
index++
e.State, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field State: %w", err)
}
return nil
}
// NewEpochEventsFromApplicationLog retrieves a set of all emitted events
// with "NewEpoch" name from the provided [result.ApplicationLog].
func NewEpochEventsFromApplicationLog(log *result.ApplicationLog) ([]*NewEpochEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*NewEpochEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "NewEpoch" {
continue
}
event := new(NewEpochEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize NewEpochEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to NewEpochEvent or
// returns an error if it's not possible to do to so.
func (e *NewEpochEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Epoch, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Epoch: %w", err)
}
return nil
}

742
rpcclient/nns/client.go Normal file
View file

@ -0,0 +1,742 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nameservice contains RPC wrappers for NameService contract.
package nameservice
import (
"errors"
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
"unicode/utf8"
)
// RegisterDomainEvent represents "RegisterDomain" event emitted by the contract.
type RegisterDomainEvent struct {
Name string
}
// AddRecordEvent represents "AddRecord" event emitted by the contract.
type AddRecordEvent struct {
Name string
Type *big.Int
}
// DeleteRecordEvent represents "DeleteRecord" event emitted by the contract.
type DeleteRecordEvent struct {
Name string
Type *big.Int
}
// DeleteRecordsEvent represents "DeleteRecords" event emitted by the contract.
type DeleteRecordsEvent struct {
Name string
Type *big.Int
}
// DeleteDomainEvent represents "DeleteDomain" event emitted by the contract.
type DeleteDomainEvent struct {
Name string
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.NonDivisibleReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.BaseWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
var nep11ndt = nep11.NewNonDivisible(actor, hash)
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash}
}
// GetAllRecords invokes `getAllRecords` method of contract.
func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name))
}
// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name))
}
// GetPrice invokes `getPrice` method of contract.
func (c *ContractReader) GetPrice() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice"))
}
// GetRecords invokes `getRecords` method of contract.
func (c *ContractReader) GetRecords(name string, typ *big.Int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "getRecords", name, typ))
}
// IsAvailable invokes `isAvailable` method of contract.
func (c *ContractReader) IsAvailable(name string) (bool, error) {
return unwrap.Bool(c.invoker.Call(c.hash, "isAvailable", name))
}
// Resolve invokes `resolve` method of contract.
func (c *ContractReader) Resolve(name string, typ *big.Int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "resolve", name, typ))
}
// Roots invokes `roots` method of contract.
func (c *ContractReader) Roots() (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "roots"))
}
// RootsExpanded is similar to Roots (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) RootsExpanded(_numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "roots", _numOfIteratorItems))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// AddRecord creates a transaction invoking `addRecord` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) AddRecord(name string, typ *big.Int, data string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "addRecord", name, typ, data)
}
// AddRecordTransaction creates a transaction invoking `addRecord` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) AddRecordTransaction(name string, typ *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "addRecord", name, typ, data)
}
// AddRecordUnsigned creates a transaction invoking `addRecord` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) AddRecordUnsigned(name string, typ *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "addRecord", nil, name, typ, data)
}
// DeleteDomain creates a transaction invoking `deleteDomain` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) DeleteDomain(name string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "deleteDomain", name)
}
// DeleteDomainTransaction creates a transaction invoking `deleteDomain` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DeleteDomainTransaction(name string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "deleteDomain", name)
}
// DeleteDomainUnsigned creates a transaction invoking `deleteDomain` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DeleteDomainUnsigned(name string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "deleteDomain", nil, name)
}
func (c *Contract) scriptForDeleteRecord(name string, typ *big.Int, data string) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "deleteRecord", name, typ, data)
}
// DeleteRecord creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) DeleteRecord(name string, typ *big.Int, data string) (util.Uint256, uint32, error) {
script, err := c.scriptForDeleteRecord(name, typ, data)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// DeleteRecordTransaction creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DeleteRecordTransaction(name string, typ *big.Int, data string) (*transaction.Transaction, error) {
script, err := c.scriptForDeleteRecord(name, typ, data)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// DeleteRecordUnsigned creates a transaction invoking `deleteRecord` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DeleteRecordUnsigned(name string, typ *big.Int, data string) (*transaction.Transaction, error) {
script, err := c.scriptForDeleteRecord(name, typ, data)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// DeleteRecords creates a transaction invoking `deleteRecords` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) DeleteRecords(name string, typ *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "deleteRecords", name, typ)
}
// DeleteRecordsTransaction creates a transaction invoking `deleteRecords` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DeleteRecordsTransaction(name string, typ *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "deleteRecords", name, typ)
}
// DeleteRecordsUnsigned creates a transaction invoking `deleteRecords` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DeleteRecordsUnsigned(name string, typ *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "deleteRecords", nil, name, typ)
}
func (c *Contract) scriptForRegister(name string, owner util.Uint160, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner, email, refresh, retry, expire, ttl)
}
// Register creates a transaction invoking `register` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Register(name string, owner util.Uint160, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) (util.Uint256, uint32, error) {
script, err := c.scriptForRegister(name, owner, email, refresh, retry, expire, ttl)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// RegisterTransaction creates a transaction invoking `register` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RegisterTransaction(name string, owner util.Uint160, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) (*transaction.Transaction, error) {
script, err := c.scriptForRegister(name, owner, email, refresh, retry, expire, ttl)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// RegisterUnsigned creates a transaction invoking `register` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RegisterUnsigned(name string, owner util.Uint160, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) (*transaction.Transaction, error) {
script, err := c.scriptForRegister(name, owner, email, refresh, retry, expire, ttl)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// Renew creates a transaction invoking `renew` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Renew(name string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "renew", name)
}
// RenewTransaction creates a transaction invoking `renew` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RenewTransaction(name string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "renew", name)
}
// RenewUnsigned creates a transaction invoking `renew` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RenewUnsigned(name string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name)
}
// SetAdmin creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetAdmin(name string, admin util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setAdmin", name, admin)
}
// SetAdminTransaction creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetAdminTransaction(name string, admin util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setAdmin", name, admin)
}
// SetAdminUnsigned creates a transaction invoking `setAdmin` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetAdminUnsigned(name string, admin util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, name, admin)
}
// SetPrice creates a transaction invoking `setPrice` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetPrice(price *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setPrice", price)
}
// SetPriceTransaction creates a transaction invoking `setPrice` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetPriceTransaction(price *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setPrice", price)
}
// SetPriceUnsigned creates a transaction invoking `setPrice` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetPriceUnsigned(price *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setPrice", nil, price)
}
// SetRecord creates a transaction invoking `setRecord` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetRecord(name string, typ *big.Int, id *big.Int, data string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setRecord", name, typ, id, data)
}
// SetRecordTransaction creates a transaction invoking `setRecord` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetRecordTransaction(name string, typ *big.Int, id *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setRecord", name, typ, id, data)
}
// SetRecordUnsigned creates a transaction invoking `setRecord` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetRecordUnsigned(name string, typ *big.Int, id *big.Int, data string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setRecord", nil, name, typ, id, data)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest string, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest string, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest string, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest, data)
}
// UpdateSOA creates a transaction invoking `updateSOA` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) UpdateSOA(name string, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "updateSOA", name, email, refresh, retry, expire, ttl)
}
// UpdateSOATransaction creates a transaction invoking `updateSOA` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateSOATransaction(name string, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "updateSOA", name, email, refresh, retry, expire, ttl)
}
// UpdateSOAUnsigned creates a transaction invoking `updateSOA` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateSOAUnsigned(name string, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "updateSOA", nil, name, email, refresh, retry, expire, ttl)
}
// RegisterDomainEventsFromApplicationLog retrieves a set of all emitted events
// with "RegisterDomain" name from the provided [result.ApplicationLog].
func RegisterDomainEventsFromApplicationLog(log *result.ApplicationLog) ([]*RegisterDomainEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*RegisterDomainEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "RegisterDomain" {
continue
}
event := new(RegisterDomainEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize RegisterDomainEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to RegisterDomainEvent or
// returns an error if it's not possible to do to so.
func (e *RegisterDomainEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
return nil
}
// AddRecordEventsFromApplicationLog retrieves a set of all emitted events
// with "AddRecord" name from the provided [result.ApplicationLog].
func AddRecordEventsFromApplicationLog(log *result.ApplicationLog) ([]*AddRecordEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*AddRecordEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "AddRecord" {
continue
}
event := new(AddRecordEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize AddRecordEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to AddRecordEvent or
// returns an error if it's not possible to do to so.
func (e *AddRecordEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.Type, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Type: %w", err)
}
return nil
}
// DeleteRecordEventsFromApplicationLog retrieves a set of all emitted events
// with "DeleteRecord" name from the provided [result.ApplicationLog].
func DeleteRecordEventsFromApplicationLog(log *result.ApplicationLog) ([]*DeleteRecordEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*DeleteRecordEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "DeleteRecord" {
continue
}
event := new(DeleteRecordEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize DeleteRecordEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to DeleteRecordEvent or
// returns an error if it's not possible to do to so.
func (e *DeleteRecordEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.Type, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Type: %w", err)
}
return nil
}
// DeleteRecordsEventsFromApplicationLog retrieves a set of all emitted events
// with "DeleteRecords" name from the provided [result.ApplicationLog].
func DeleteRecordsEventsFromApplicationLog(log *result.ApplicationLog) ([]*DeleteRecordsEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*DeleteRecordsEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "DeleteRecords" {
continue
}
event := new(DeleteRecordsEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize DeleteRecordsEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to DeleteRecordsEvent or
// returns an error if it's not possible to do to so.
func (e *DeleteRecordsEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.Type, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Type: %w", err)
}
return nil
}
// DeleteDomainEventsFromApplicationLog retrieves a set of all emitted events
// with "DeleteDomain" name from the provided [result.ApplicationLog].
func DeleteDomainEventsFromApplicationLog(log *result.ApplicationLog) ([]*DeleteDomainEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*DeleteDomainEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "DeleteDomain" {
continue
}
event := new(DeleteDomainEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize DeleteDomainEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to DeleteDomainEvent or
// returns an error if it's not possible to do to so.
func (e *DeleteDomainEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
return nil
}

234
rpcclient/policy/client.go Normal file
View file

@ -0,0 +1,234 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package ape contains RPC wrappers for APE contract.
package ape
import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error)
TerminateSession(sessionID uuid.UUID) error
TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error)
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash}
}
// GetAdmin invokes `getAdmin` method of contract.
func (c *ContractReader) GetAdmin() (util.Uint160, error) {
return unwrap.Uint160(c.invoker.Call(c.hash, "getAdmin"))
}
// GetChain invokes `getChain` method of contract.
func (c *ContractReader) GetChain(entity *big.Int, entityName string, name []byte) ([]byte, error) {
return unwrap.Bytes(c.invoker.Call(c.hash, "getChain", entity, entityName, name))
}
// IteratorChainsByPrefix invokes `iteratorChainsByPrefix` method of contract.
func (c *ContractReader) IteratorChainsByPrefix(entity *big.Int, entityName string, prefix []byte) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "iteratorChainsByPrefix", entity, entityName, prefix))
}
// IteratorChainsByPrefixExpanded is similar to IteratorChainsByPrefix (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) IteratorChainsByPrefixExpanded(entity *big.Int, entityName string, prefix []byte, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "iteratorChainsByPrefix", _numOfIteratorItems, entity, entityName, prefix))
}
// ListChainNames invokes `listChainNames` method of contract.
func (c *ContractReader) ListChainNames(entity *big.Int, entityName string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "listChainNames", entity, entityName))
}
// ListChainNamesExpanded is similar to ListChainNames (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) ListChainNamesExpanded(entity *big.Int, entityName string, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "listChainNames", _numOfIteratorItems, entity, entityName))
}
// ListChains invokes `listChains` method of contract.
func (c *ContractReader) ListChains(namespace string, container string, name []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listChains", namespace, container, name))
}
// ListChainsByPrefix invokes `listChainsByPrefix` method of contract.
func (c *ContractReader) ListChainsByPrefix(entity *big.Int, entityName string, prefix []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listChainsByPrefix", entity, entityName, prefix))
}
// ListTargets invokes `listTargets` method of contract.
func (c *ContractReader) ListTargets(entity *big.Int) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "listTargets", entity))
}
// ListTargetsExpanded is similar to ListTargets (uses the same contract
// method), but can be useful if the server used doesn't support sessions and
// doesn't expand iterators. It creates a script that will get the specified
// number of result items from the iterator right in the VM and return them to
// you. It's only limited by VM stack and GAS available for RPC invocations.
func (c *ContractReader) ListTargetsExpanded(entity *big.Int, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "listTargets", _numOfIteratorItems, entity))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// AddChain creates a transaction invoking `addChain` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) AddChain(entity *big.Int, entityName string, name []byte, chain []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "addChain", entity, entityName, name, chain)
}
// AddChainTransaction creates a transaction invoking `addChain` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) AddChainTransaction(entity *big.Int, entityName string, name []byte, chain []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "addChain", entity, entityName, name, chain)
}
// AddChainUnsigned creates a transaction invoking `addChain` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) AddChainUnsigned(entity *big.Int, entityName string, name []byte, chain []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "addChain", nil, entity, entityName, name, chain)
}
// RemoveChain creates a transaction invoking `removeChain` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) RemoveChain(entity *big.Int, entityName string, name []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "removeChain", entity, entityName, name)
}
// RemoveChainTransaction creates a transaction invoking `removeChain` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RemoveChainTransaction(entity *big.Int, entityName string, name []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "removeChain", entity, entityName, name)
}
// RemoveChainUnsigned creates a transaction invoking `removeChain` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RemoveChainUnsigned(entity *big.Int, entityName string, name []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "removeChain", nil, entity, entityName, name)
}
// RemoveChainsByPrefix creates a transaction invoking `removeChainsByPrefix` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) RemoveChainsByPrefix(entity *big.Int, entityName string, name []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "removeChainsByPrefix", entity, entityName, name)
}
// RemoveChainsByPrefixTransaction creates a transaction invoking `removeChainsByPrefix` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RemoveChainsByPrefixTransaction(entity *big.Int, entityName string, name []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "removeChainsByPrefix", entity, entityName, name)
}
// RemoveChainsByPrefixUnsigned creates a transaction invoking `removeChainsByPrefix` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RemoveChainsByPrefixUnsigned(entity *big.Int, entityName string, name []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "removeChainsByPrefix", nil, entity, entityName, name)
}
// SetAdmin creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetAdmin(addr util.Uint160) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "setAdmin", addr)
}
// SetAdminTransaction creates a transaction invoking `setAdmin` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetAdminTransaction(addr util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "setAdmin", addr)
}
// SetAdminUnsigned creates a transaction invoking `setAdmin` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetAdminUnsigned(addr util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, addr)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}

View file

@ -0,0 +1,84 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package multisignatureprocessing contains RPC wrappers for Multi Signature Processing contract.
package multisignatureprocessing
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash}
}
// Verify invokes `verify` method of contract.
func (c *ContractReader) Verify() (bool, error) {
return unwrap.Bool(c.invoker.Call(c.hash, "verify"))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}

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