forked from TrueCloudLab/frostfs-contract
Compare commits
2 commits
master
...
support/v0
Author | SHA1 | Date | |
---|---|---|---|
9b81572b9d | |||
e11dfc84af |
45 changed files with 1163 additions and 1104 deletions
|
@ -1,20 +0,0 @@
|
||||||
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.20'
|
|
||||||
|
|
||||||
- name: Run commit format checker
|
|
||||||
uses: https://git.alexvan.in/alexvanin/dco-go@v1
|
|
||||||
with:
|
|
||||||
from: e19fe15e
|
|
|
@ -1,20 +0,0 @@
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
tests:
|
|
||||||
name: Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
go_versions: [ '1.19', '1.20' ]
|
|
||||||
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
|
|
21
.github/workflows/dco.yml
vendored
Normal file
21
.github/workflows/dco.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: DCO check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
commits_check_job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Commits Check
|
||||||
|
steps:
|
||||||
|
- name: Get PR Commits
|
||||||
|
id: 'get-pr-commits'
|
||||||
|
uses: tim-actions/get-pr-commits@master
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: DCO Check
|
||||||
|
uses: tim-actions/dco@master
|
||||||
|
with:
|
||||||
|
commits: ${{ steps.get-pr-commits.outputs.commits }}
|
20
.github/workflows/go.yml
vendored
Normal file
20
.github/workflows/go.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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 ./...
|
|
@ -6,8 +6,6 @@ Changelog for FrostFS Contract
|
||||||
### Added
|
### Added
|
||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
- `subnet` contract (#20)
|
|
||||||
|
|
||||||
### Updated
|
### Updated
|
||||||
### Fixed
|
### Fixed
|
||||||
### Updating from v0.17.0
|
### Updating from v0.17.0
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -22,7 +22,7 @@ all: sidechain mainnet
|
||||||
sidechain: alphabet morph nns
|
sidechain: alphabet morph nns
|
||||||
|
|
||||||
alphabet_sc = alphabet
|
alphabet_sc = alphabet
|
||||||
morph_sc = audit balance container frostfsid netmap proxy reputation
|
morph_sc = audit balance container frostfsid netmap proxy reputation subnet
|
||||||
mainnet_sc = frostfs processing
|
mainnet_sc = frostfs processing
|
||||||
nns_sc = nns
|
nns_sc = nns
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ Sidechain contracts:
|
||||||
- nns
|
- nns
|
||||||
- proxy
|
- proxy
|
||||||
- reputation
|
- reputation
|
||||||
|
- subnet
|
||||||
|
|
||||||
# Getting started
|
# Getting started
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ $ make all
|
||||||
/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 frostfs -c frostfs/config.yml -m frostfs/config.json -o frostfs/frostfs_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
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
v0.18.0
|
v0.17.0
|
||||||
|
|
|
@ -72,7 +72,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("alphabet contract updated")
|
runtime.Log("alphabet contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,5 @@ 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
|
||||||
|
|
|
@ -3,6 +3,7 @@ package audit
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"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/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"
|
||||||
|
@ -74,7 +75,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("audit contract updated")
|
runtime.Log("audit contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,5 @@ they make a list and get these AuditResultStructures from the audit contract.
|
||||||
# Contract notifications
|
# Contract notifications
|
||||||
|
|
||||||
Audit contract does not produce notifications to process.
|
Audit contract does not produce notifications to process.
|
||||||
|
|
||||||
# Contract storage scheme
|
|
||||||
|
|
||||||
| Key | Value | Description |
|
|
||||||
|--------------------|------------|-----------------------------------------------------------|
|
|
||||||
| `netmapScriptHash` | Hash160 | netmap contract hash |
|
|
||||||
| auditID | ByteArray | serialized DataAuditResult structure |
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package audit
|
package audit
|
||||||
|
|
|
@ -3,6 +3,7 @@ package balance
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"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"
|
||||||
|
@ -92,7 +93,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("balance contract updated")
|
runtime.Log("balance contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
name: "Balance"
|
name: "Balance"
|
||||||
supportedstandards: ["NEP-17"]
|
supportedstandards: ["NEP-17"]
|
||||||
safemethods:
|
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "version"]
|
||||||
- "balanceOf"
|
|
||||||
- "decimals"
|
|
||||||
- "symbol"
|
|
||||||
- "totalSupply"
|
|
||||||
- "version"
|
|
||||||
permissions:
|
permissions:
|
||||||
- methods: ["update"]
|
- methods: ["update"]
|
||||||
events:
|
events:
|
||||||
|
|
|
@ -74,14 +74,5 @@ when FrostFS contract has transferred GAS assets back to the user.
|
||||||
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
|
||||||
|
|
|
@ -4,7 +4,7 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
major = 0
|
major = 0
|
||||||
minor = 18
|
minor = 17
|
||||||
patch = 0
|
patch = 0
|
||||||
|
|
||||||
// Versions from which an update should be performed.
|
// Versions from which an update should be performed.
|
||||||
|
|
|
@ -1,36 +1,47 @@
|
||||||
name: "Container"
|
name: "Container"
|
||||||
safemethods:
|
safemethods: ["count", "containersOf", "get", "owner", "list", "eACL", "getContainerSize", "listContainerSizes", "iterateContainerSizes", "version"]
|
||||||
- "count"
|
|
||||||
- "containersOf"
|
|
||||||
- "deletionInfo"
|
|
||||||
- "eACL"
|
|
||||||
- "get"
|
|
||||||
- "getContainerSize"
|
|
||||||
- "iterateContainerSizes"
|
|
||||||
- "list"
|
|
||||||
- "listContainerSizes"
|
|
||||||
- "owner"
|
|
||||||
- "version"
|
|
||||||
permissions:
|
permissions:
|
||||||
- methods:
|
- methods: ["update", "addKey", "transferX",
|
||||||
- "addKey"
|
"register", "addRecord", "deleteRecords"]
|
||||||
- "addRecord"
|
|
||||||
- "deleteRecords"
|
|
||||||
- "register"
|
|
||||||
- "transferX"
|
|
||||||
- "update"
|
|
||||||
|
|
||||||
events:
|
events:
|
||||||
|
- name: containerPut
|
||||||
|
parameters:
|
||||||
|
- name: container
|
||||||
|
type: ByteArray
|
||||||
|
- name: signature
|
||||||
|
type: Signature
|
||||||
|
- name: publicKey
|
||||||
|
type: PublicKey
|
||||||
|
- name: token
|
||||||
|
type: ByteArray
|
||||||
- name: PutSuccess
|
- name: PutSuccess
|
||||||
parameters:
|
parameters:
|
||||||
- name: containerID
|
- name: containerID
|
||||||
type: Hash256
|
type: Hash256
|
||||||
- name: publicKey
|
- name: publicKey
|
||||||
type: PublicKey
|
type: PublicKey
|
||||||
|
- name: containerDelete
|
||||||
|
parameters:
|
||||||
|
- name: containerID
|
||||||
|
type: ByteArray
|
||||||
|
- name: signature
|
||||||
|
type: Signature
|
||||||
|
- name: token
|
||||||
|
type: ByteArray
|
||||||
- name: DeleteSuccess
|
- name: DeleteSuccess
|
||||||
parameters:
|
parameters:
|
||||||
- name: containerID
|
- name: containerID
|
||||||
type: ByteArray
|
type: ByteArray
|
||||||
|
- name: setEACL
|
||||||
|
parameters:
|
||||||
|
- name: eACL
|
||||||
|
type: ByteArray
|
||||||
|
- name: signature
|
||||||
|
type: Signature
|
||||||
|
- name: publicKey
|
||||||
|
type: PublicKey
|
||||||
|
- name: token
|
||||||
|
type: ByteArray
|
||||||
- name: SetEACLSuccess
|
- name: SetEACLSuccess
|
||||||
parameters:
|
parameters:
|
||||||
- name: containerID
|
- name: containerID
|
||||||
|
|
|
@ -64,7 +64,6 @@ const (
|
||||||
estimateKeyPrefix = "cnr"
|
estimateKeyPrefix = "cnr"
|
||||||
containerKeyPrefix = 'x'
|
containerKeyPrefix = 'x'
|
||||||
ownerKeyPrefix = 'o'
|
ownerKeyPrefix = 'o'
|
||||||
graveKeyPrefix = 'g'
|
|
||||||
estimatePostfixSize = 10
|
estimatePostfixSize = 10
|
||||||
// CleanupDelta contains the number of the last epochs for which container estimations are present.
|
// CleanupDelta contains the number of the last epochs for which container estimations are present.
|
||||||
CleanupDelta = 3
|
CleanupDelta = 3
|
||||||
|
@ -172,12 +171,13 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("container contract updated")
|
runtime.Log("container contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put method creates a new container if it has been invoked by Alphabet nodes
|
// Put method creates a new container if it has been invoked by Alphabet nodes
|
||||||
// of the Inner Ring.
|
// of the Inner Ring. Otherwise, it produces containerPut notification.
|
||||||
//
|
//
|
||||||
// Container should be a stable marshaled Container structure from API.
|
// Container should be a stable marshaled Container structure from API.
|
||||||
// Signature is a RFC6979 signature of the Container.
|
// Signature is a RFC6979 signature of the Container.
|
||||||
|
@ -303,14 +303,15 @@ func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete method removes a container from the contract storage if it has been
|
// Delete method removes a container from the contract storage if it has been
|
||||||
// invoked by Alphabet nodes of the Inner Ring.
|
// invoked by Alphabet nodes of the Inner Ring. Otherwise, it produces
|
||||||
|
// containerDelete notification.
|
||||||
//
|
//
|
||||||
// Signature is a RFC6979 signature of the container ID.
|
// Signature is a RFC6979 signature of the container ID.
|
||||||
// Token is optional and should be a stable marshaled SessionToken structure from
|
// Token is optional and should be a stable marshaled SessionToken structure from
|
||||||
// API.
|
// API.
|
||||||
//
|
//
|
||||||
// If the 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, publicKey interop.PublicKey, token []byte) {
|
func Delete(containerID []byte, signature interop.Signature, token []byte) {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
ownerID := getOwnerByID(ctx, containerID)
|
ownerID := getOwnerByID(ctx, containerID)
|
||||||
|
@ -338,25 +339,6 @@ func Delete(containerID []byte, signature interop.Signature, publicKey interop.P
|
||||||
runtime.Notify("DeleteSuccess", containerID)
|
runtime.Notify("DeleteSuccess", containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DelInfo struct {
|
|
||||||
Owner interop.Hash160
|
|
||||||
Epoch int
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
return std.Deserialize(data).(DelInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get method returns a structure that contains a stable marshaled Container structure,
|
// 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
|
// the signature, the public key of the container creator and a stable marshaled SessionToken
|
||||||
// structure if it was provided.
|
// structure if it was provided.
|
||||||
|
@ -425,7 +407,8 @@ func List(owner []byte) [][]byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEACL method sets a 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.
|
// if it was invoked by Alphabet nodes of the Inner Ring. Otherwise, it produces
|
||||||
|
// setEACL notification.
|
||||||
//
|
//
|
||||||
// EACL should be a stable marshaled EACLTable structure from API.
|
// EACL should be a stable marshaled EACLTable structure from API.
|
||||||
// Signature is a RFC6979 signature of the Container.
|
// Signature is a RFC6979 signature of the Container.
|
||||||
|
@ -616,9 +599,6 @@ func addContainer(ctx storage.Context, id, owner []byte, container Container) {
|
||||||
|
|
||||||
idKey := append([]byte{containerKeyPrefix}, id...)
|
idKey := append([]byte{containerKeyPrefix}, id...)
|
||||||
common.SetSerialized(ctx, idKey, container)
|
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) {
|
||||||
|
@ -627,14 +607,6 @@ func removeContainer(ctx storage.Context, id []byte, owner []byte) {
|
||||||
storage.Delete(ctx, containerListKey)
|
storage.Delete(ctx, containerListKey)
|
||||||
|
|
||||||
storage.Delete(ctx, append([]byte{containerKeyPrefix}, 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: epoch,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllContainers(ctx storage.Context) [][]byte {
|
func getAllContainers(ctx storage.Context) [][]byte {
|
||||||
|
|
|
@ -9,6 +9,47 @@ the same arguments.
|
||||||
|
|
||||||
# Contract notifications
|
# Contract notifications
|
||||||
|
|
||||||
|
containerPut notification. This notification is produced when a user wants to
|
||||||
|
create a new container. Alphabet nodes of the Inner Ring catch the notification and
|
||||||
|
validate container data, signature and token if 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 a container owner
|
||||||
|
wants to delete a container. Alphabet nodes of the Inner Ring catch the notification
|
||||||
|
and validate container ownership, signature and token if present.
|
||||||
|
|
||||||
|
containerDelete:
|
||||||
|
- name: containerID
|
||||||
|
type: ByteArray
|
||||||
|
- name: signature
|
||||||
|
type: Signature
|
||||||
|
- name: token
|
||||||
|
type: ByteArray
|
||||||
|
|
||||||
|
setEACL notification. This notification is produced when a container owner wants
|
||||||
|
to update an extended ACL of a container. Alphabet nodes of the Inner Ring catch
|
||||||
|
the notification and validate container ownership, signature and token if
|
||||||
|
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.
|
||||||
|
|
||||||
|
@ -23,22 +64,5 @@ 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
|
||||||
|
|
1
debian/control
vendored
1
debian/control
vendored
|
@ -31,3 +31,4 @@ Description: FrostFS-Contract contains all FrostFS related contracts.
|
||||||
- nns
|
- nns
|
||||||
- proxy
|
- proxy
|
||||||
- reputation
|
- reputation
|
||||||
|
- subnet
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
name: "FrostFS"
|
name: "FrostFS"
|
||||||
safemethods:
|
safemethods: ["alphabetAddress", "innerRingCandidates", "config", "listConfig", "version"]
|
||||||
- "alphabetAddress"
|
|
||||||
- "config"
|
|
||||||
- "innerRingCandidates"
|
|
||||||
- "listConfig"
|
|
||||||
- "version"
|
|
||||||
permissions:
|
permissions:
|
||||||
- methods: ["update", "transfer"]
|
- methods: ["update", "transfer"]
|
||||||
events:
|
events:
|
||||||
|
|
|
@ -80,15 +80,5 @@ FrostFS network configuration value.
|
||||||
type: ByteArray
|
type: ByteArray
|
||||||
- name: value
|
- name: value
|
||||||
type: ByteArray
|
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
|
package frostfs
|
||||||
|
|
|
@ -109,7 +109,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic(common.ErrAlphabetWitnessFailed)
|
panic(common.ErrAlphabetWitnessFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("frostfs contract updated")
|
runtime.Log("frostfs contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,5 @@ contract.
|
||||||
# Contract notifications
|
# Contract notifications
|
||||||
|
|
||||||
FrostFSID contract does not produce notifications to process.
|
FrostFSID contract does not produce notifications to process.
|
||||||
|
|
||||||
# Contract storage scheme
|
|
||||||
|
|
||||||
| Key | Value | Description |
|
|
||||||
|-----------------------------|------------|----------------------------------|
|
|
||||||
| `processingScriptHash` | Hash160 | netmap contract hash |
|
|
||||||
| `containerScriptHash` | Hash160 | container contract hash |
|
|
||||||
| `o` + ownerID + publicKey | ByteArray | it flags owner's public key |
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package frostfsid
|
package frostfsid
|
||||||
|
|
|
@ -3,6 +3,7 @@ package frostfsid
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"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/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
|
@ -61,7 +62,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("frostfsid contract updated")
|
runtime.Log("frostfsid contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -4,7 +4,7 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mr-tron/base58 v1.2.0
|
github.com/mr-tron/base58 v1.2.0
|
||||||
github.com/nspcc-dev/neo-go v0.101.5-0.20230808195420-5fc61be5f6c5
|
github.com/nspcc-dev/neo-go v0.99.4
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230808195420-5fc61be5f6c5
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220927123257-24c107e3a262
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.7.0
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
name: "Netmap"
|
name: "Netmap"
|
||||||
safemethods:
|
safemethods: ["epoch", "netmap", "netmapCandidates", "snapshot", "snapshotByEpoch", "config", "listConfig", "version"]
|
||||||
- "config"
|
|
||||||
- "epoch"
|
|
||||||
- "listConfig"
|
|
||||||
- "netmap"
|
|
||||||
- "netmapCandidates"
|
|
||||||
- "snapshot"
|
|
||||||
- "snapshotByEpoch"
|
|
||||||
- "version"
|
|
||||||
permissions:
|
permissions:
|
||||||
- methods: ["update", "newEpoch"]
|
- methods: ["update", "newEpoch"]
|
||||||
events:
|
events:
|
||||||
|
|
|
@ -29,18 +29,5 @@ 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
|
||||||
|
|
|
@ -127,7 +127,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("netmap contract updated")
|
runtime.Log("netmap contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
nns/doc.go
17
nns/doc.go
|
@ -1,17 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
# 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 |
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
package nns
|
|
18
nns/nns.yml
18
nns/nns.yml
|
@ -1,20 +1,8 @@
|
||||||
name: "NameService"
|
name: "NameService"
|
||||||
supportedstandards: ["NEP-11"]
|
supportedstandards: ["NEP-11"]
|
||||||
safemethods:
|
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf",
|
||||||
- "balanceOf"
|
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecord",
|
||||||
- "decimals"
|
"resolve", "getAllRecords"]
|
||||||
- "getAllRecords"
|
|
||||||
- "getPrice"
|
|
||||||
- "getRecord"
|
|
||||||
- "isAvailable"
|
|
||||||
- "ownerOf"
|
|
||||||
- "properties"
|
|
||||||
- "symbol"
|
|
||||||
- "totalSupply"
|
|
||||||
- "tokensOf"
|
|
||||||
- "tokens"
|
|
||||||
- "resolve"
|
|
||||||
- "roots"
|
|
||||||
events:
|
events:
|
||||||
- name: Transfer
|
- name: Transfer
|
||||||
parameters:
|
parameters:
|
||||||
|
|
|
@ -82,7 +82,8 @@ func Update(nef []byte, manifest string, data interface{}) {
|
||||||
// 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))
|
||||||
management.UpdateWithData(nef, []byte(manifest), common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, nef, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("nns contract updated")
|
runtime.Log("nns contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,5 @@ 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
|
||||||
|
|
|
@ -59,7 +59,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only side chain committee can update contract")
|
panic("only side chain committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("processing contract updated")
|
runtime.Log("processing contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,5 @@ 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
|
||||||
|
|
|
@ -3,6 +3,7 @@ package proxy
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"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/native/neo"
|
||||||
|
@ -34,7 +35,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("proxy contract updated")
|
runtime.Log("proxy contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,5 @@ Inner Ring nodes if data audit succeeds.
|
||||||
# Contract notifications
|
# Contract notifications
|
||||||
|
|
||||||
Reputation contract does not produce notifications to process.
|
Reputation contract does not produce notifications to process.
|
||||||
|
|
||||||
# Contract storage scheme
|
|
||||||
|
|
||||||
| Key | Value | Description |
|
|
||||||
|-----------------------------|------------|-----------------------------------|
|
|
||||||
| `c` + epoch + peerID | int | peer reputation count |
|
|
||||||
| `r` + count | ByteArray | serialized DataAuditResult struct |
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package reputation
|
package reputation
|
||||||
|
|
|
@ -2,6 +2,8 @@ package reputation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
"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/convert"
|
"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/management"
|
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||||
|
@ -34,7 +36,8 @@ func Update(script []byte, manifest []byte, data interface{}) {
|
||||||
panic("only committee can update contract")
|
panic("only committee can update contract")
|
||||||
}
|
}
|
||||||
|
|
||||||
management.UpdateWithData(script, manifest, common.AppendVersion(data))
|
contract.Call(interop.Hash160(management.Hash), "update",
|
||||||
|
contract.All, script, manifest, common.AppendVersion(data))
|
||||||
runtime.Log("reputation contract updated")
|
runtime.Log("reputation contract updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
subnet/config.yml
Normal file
23
subnet/config.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: "Subnet"
|
||||||
|
safemethods: ["version"]
|
||||||
|
permissions:
|
||||||
|
- methods: ["update"]
|
||||||
|
events:
|
||||||
|
- name: Put
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
type: ByteArray
|
||||||
|
- name: ownerKey
|
||||||
|
type: PublicKey
|
||||||
|
- name: info
|
||||||
|
type: ByteArray
|
||||||
|
- name: Delete
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
type: ByteArray
|
||||||
|
- name: RemoveNode
|
||||||
|
parameters:
|
||||||
|
- name: subnetID
|
||||||
|
type: ByteArray
|
||||||
|
- name: node
|
||||||
|
type: PublicKey
|
37
subnet/doc.go
Normal file
37
subnet/doc.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Subnet contract is a contract deployed in FrostFS sidechain.
|
||||||
|
|
||||||
|
Subnet contract stores and manages FrostFS subnetwork states. It allows registering
|
||||||
|
and deleting subnetworks, limiting access to them, and defining a list of the Storage
|
||||||
|
Nodes that can be included in them.
|
||||||
|
|
||||||
|
# Contract notifications
|
||||||
|
|
||||||
|
Put notification. This notification is produced when a new subnetwork is
|
||||||
|
registered by invoking Put method.
|
||||||
|
|
||||||
|
Put
|
||||||
|
- name: id
|
||||||
|
type: ByteArray
|
||||||
|
- name: ownerKey
|
||||||
|
type: PublicKey
|
||||||
|
- name: info
|
||||||
|
type: ByteArray
|
||||||
|
|
||||||
|
Delete notification. This notification is produced when some subnetwork is
|
||||||
|
deleted by invoking Delete method.
|
||||||
|
|
||||||
|
Delete
|
||||||
|
- name: id
|
||||||
|
type: ByteArray
|
||||||
|
|
||||||
|
RemoveNode notification. This notification is produced when some node is deleted
|
||||||
|
by invoking RemoveNode method.
|
||||||
|
|
||||||
|
RemoveNode
|
||||||
|
- name: subnetID
|
||||||
|
type: ByteArray
|
||||||
|
- name: node
|
||||||
|
type: PublicKey
|
||||||
|
*/
|
||||||
|
package subnet
|
559
subnet/subnet_contract.go
Normal file
559
subnet/subnet_contract.go
Normal file
|
@ -0,0 +1,559 @@
|
||||||
|
package subnet
|
||||||
|
|
||||||
|
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/management"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ErrInvalidSubnetID is thrown when subnet id is not a slice of 5 bytes.
|
||||||
|
ErrInvalidSubnetID = "invalid subnet ID"
|
||||||
|
// ErrInvalidGroupID is thrown when group id is not a slice of 5 bytes.
|
||||||
|
ErrInvalidGroupID = "invalid group ID"
|
||||||
|
// ErrInvalidOwner is thrown when owner has invalid format.
|
||||||
|
ErrInvalidOwner = "invalid owner"
|
||||||
|
// ErrInvalidAdmin is thrown when admin has invalid format.
|
||||||
|
ErrInvalidAdmin = "invalid administrator"
|
||||||
|
// ErrAlreadyExists is thrown when id already exists.
|
||||||
|
ErrAlreadyExists = "subnet id already exists"
|
||||||
|
// ErrNotExist is thrown when id doesn't exist.
|
||||||
|
ErrNotExist = "subnet id doesn't exist"
|
||||||
|
// ErrInvalidUser is thrown when user has invalid format.
|
||||||
|
ErrInvalidUser = "invalid user"
|
||||||
|
// ErrInvalidNode is thrown when node has invalid format.
|
||||||
|
ErrInvalidNode = "invalid node key"
|
||||||
|
// ErrAccessDenied is thrown when operation is denied for caller.
|
||||||
|
ErrAccessDenied = "access denied"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nodeAdminPrefix = 'a'
|
||||||
|
infoPrefix = 'i'
|
||||||
|
clientAdminPrefix = 'm'
|
||||||
|
nodePrefix = 'n'
|
||||||
|
ownerPrefix = 'o'
|
||||||
|
userPrefix = 'u'
|
||||||
|
notaryDisabledKey = 'z'
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
userIDSize = 27
|
||||||
|
subnetIDSize = 5
|
||||||
|
groupIDSize = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// _deploy function sets up initial list of inner ring public keys.
|
||||||
|
func _deploy(data interface{}, isUpdate bool) {
|
||||||
|
common.RmAndCheckNotaryDisabledKey(data, notaryDisabledKey)
|
||||||
|
|
||||||
|
if isUpdate {
|
||||||
|
args := data.([]interface{})
|
||||||
|
common.CheckVersion(args[len(args)-1].(int))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update method updates contract source code and manifest. It 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("subnet contract updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put creates a new subnet with the specified owner and info.
|
||||||
|
func Put(id []byte, ownerKey interop.PublicKey, info []byte) {
|
||||||
|
// V2 format
|
||||||
|
if len(id) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
if len(ownerKey) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidOwner)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
stKey := append([]byte{ownerPrefix}, id...)
|
||||||
|
if storage.Get(ctx, stKey) != nil {
|
||||||
|
panic(ErrAlreadyExists)
|
||||||
|
}
|
||||||
|
|
||||||
|
common.CheckOwnerWitness(ownerKey)
|
||||||
|
|
||||||
|
common.CheckAlphabetWitness()
|
||||||
|
|
||||||
|
storage.Put(ctx, stKey, ownerKey)
|
||||||
|
stKey[0] = infoPrefix
|
||||||
|
storage.Put(ctx, stKey, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns info about the subnet with the specified id.
|
||||||
|
func Get(id []byte) []byte {
|
||||||
|
// V2 format
|
||||||
|
if len(id) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
key := append([]byte{infoPrefix}, id...)
|
||||||
|
raw := storage.Get(ctx, key)
|
||||||
|
if raw == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
return raw.([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the subnet with the specified id.
|
||||||
|
func Delete(id []byte) {
|
||||||
|
// V2 format
|
||||||
|
if len(id) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
key := append([]byte{ownerPrefix}, id...)
|
||||||
|
raw := storage.Get(ctx, key)
|
||||||
|
if raw == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
owner := raw.([]byte)
|
||||||
|
common.CheckOwnerWitness(owner)
|
||||||
|
|
||||||
|
storage.Delete(ctx, key)
|
||||||
|
|
||||||
|
key[0] = infoPrefix
|
||||||
|
storage.Delete(ctx, key)
|
||||||
|
|
||||||
|
key[0] = nodeAdminPrefix
|
||||||
|
deleteByPrefix(ctx, key)
|
||||||
|
|
||||||
|
key[0] = nodePrefix
|
||||||
|
deleteByPrefix(ctx, key)
|
||||||
|
|
||||||
|
key[0] = clientAdminPrefix
|
||||||
|
deleteByPrefix(ctx, key)
|
||||||
|
|
||||||
|
key[0] = userPrefix
|
||||||
|
deleteByPrefix(ctx, key)
|
||||||
|
|
||||||
|
runtime.Notify("Delete", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNodeAdmin adds a new node administrator to the specified subnetwork.
|
||||||
|
func AddNodeAdmin(subnetID []byte, adminKey interop.PublicKey) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(adminKey) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
common.CheckOwnerWitness(owner)
|
||||||
|
|
||||||
|
stKey[0] = nodeAdminPrefix
|
||||||
|
|
||||||
|
if keyInList(ctx, adminKey, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
putKeyInList(ctx, adminKey, stKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNodeAdmin removes node administrator from the specified subnetwork.
|
||||||
|
// Must be called by the subnet owner only.
|
||||||
|
func RemoveNodeAdmin(subnetID []byte, adminKey interop.PublicKey) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(adminKey) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
common.CheckOwnerWitness(owner)
|
||||||
|
|
||||||
|
stKey[0] = nodeAdminPrefix
|
||||||
|
|
||||||
|
if !keyInList(ctx, adminKey, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteKeyFromList(ctx, adminKey, stKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNode adds a node to the specified subnetwork.
|
||||||
|
// Must be called by the subnet's owner or the node administrator
|
||||||
|
// only.
|
||||||
|
func AddNode(subnetID []byte, node interop.PublicKey) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(node) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = nodeAdminPrefix
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
|
||||||
|
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
||||||
|
panic(ErrAccessDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = nodePrefix
|
||||||
|
|
||||||
|
if keyInList(ctx, node, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
putKeyInList(ctx, node, stKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveNode removes a node from the specified subnetwork.
|
||||||
|
// Must be called by the subnet's owner or the node administrator
|
||||||
|
// only.
|
||||||
|
func RemoveNode(subnetID []byte, node interop.PublicKey) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(node) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = nodeAdminPrefix
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
|
||||||
|
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
||||||
|
panic(ErrAccessDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = nodePrefix
|
||||||
|
|
||||||
|
if !keyInList(ctx, node, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Delete(ctx, append(stKey, node...))
|
||||||
|
|
||||||
|
runtime.Notify("RemoveNode", subnetID, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeAllowed checks if a node is included in the
|
||||||
|
// specified subnet.
|
||||||
|
func NodeAllowed(subnetID []byte, node interop.PublicKey) bool {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(node) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = nodePrefix
|
||||||
|
|
||||||
|
return storage.Get(ctx, append(stKey, node...)) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddClientAdmin adds a new client administrator of the specified group in the specified subnetwork.
|
||||||
|
// Must be called by the owner only.
|
||||||
|
func AddClientAdmin(subnetID []byte, groupID []byte, adminPublicKey interop.PublicKey) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2 format
|
||||||
|
if len(groupID) != groupIDSize {
|
||||||
|
panic(ErrInvalidGroupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(adminPublicKey) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
common.CheckOwnerWitness(owner)
|
||||||
|
|
||||||
|
stKey[0] = clientAdminPrefix
|
||||||
|
stKey = append(stKey, groupID...)
|
||||||
|
|
||||||
|
if keyInList(ctx, adminPublicKey, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
putKeyInList(ctx, adminPublicKey, stKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveClientAdmin removes client administrator from the
|
||||||
|
// specified group in the specified subnetwork.
|
||||||
|
// Must be called by the owner only.
|
||||||
|
func RemoveClientAdmin(subnetID []byte, groupID []byte, adminPublicKey interop.PublicKey) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2 format
|
||||||
|
if len(groupID) != groupIDSize {
|
||||||
|
panic(ErrInvalidGroupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(adminPublicKey) != interop.PublicKeyCompressedLen {
|
||||||
|
panic(ErrInvalidAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
common.CheckOwnerWitness(owner)
|
||||||
|
|
||||||
|
stKey[0] = clientAdminPrefix
|
||||||
|
stKey = append(stKey, groupID...)
|
||||||
|
|
||||||
|
if !keyInList(ctx, adminPublicKey, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteKeyFromList(ctx, adminPublicKey, stKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUser adds user to the specified subnetwork and group.
|
||||||
|
// Must be called by the owner or the group's admin only.
|
||||||
|
func AddUser(subnetID []byte, groupID []byte, userID []byte) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2 format
|
||||||
|
if len(userID) != userIDSize {
|
||||||
|
panic(ErrInvalidUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2 format
|
||||||
|
if len(groupID) != groupIDSize {
|
||||||
|
panic(ErrInvalidGroupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = clientAdminPrefix
|
||||||
|
stKey = append(stKey, groupID...)
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
|
||||||
|
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
||||||
|
panic(ErrAccessDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = userPrefix
|
||||||
|
|
||||||
|
if keyInList(ctx, userID, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
putKeyInList(ctx, userID, stKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveUser removes a user from the specified subnetwork and group.
|
||||||
|
// Must be called by the owner or the group's admin only.
|
||||||
|
func RemoveUser(subnetID []byte, groupID []byte, userID []byte) {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2 format
|
||||||
|
if len(groupID) != groupIDSize {
|
||||||
|
panic(ErrInvalidGroupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2 format
|
||||||
|
if len(userID) != userIDSize {
|
||||||
|
panic(ErrInvalidUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
|
||||||
|
rawOwner := storage.Get(ctx, stKey)
|
||||||
|
if rawOwner == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = clientAdminPrefix
|
||||||
|
stKey = append(stKey, groupID...)
|
||||||
|
|
||||||
|
owner := rawOwner.([]byte)
|
||||||
|
|
||||||
|
if !calledByOwnerOrAdmin(ctx, owner, stKey) {
|
||||||
|
panic(ErrAccessDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = userPrefix
|
||||||
|
|
||||||
|
if !keyInList(ctx, userID, stKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteKeyFromList(ctx, userID, stKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAllowed returns bool that indicates if a node is included in the
|
||||||
|
// specified subnet.
|
||||||
|
func UserAllowed(subnetID []byte, user []byte) bool {
|
||||||
|
// V2 format
|
||||||
|
if len(subnetID) != subnetIDSize {
|
||||||
|
panic(ErrInvalidSubnetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
stKey := append([]byte{ownerPrefix}, subnetID...)
|
||||||
|
if storage.Get(ctx, stKey) == nil {
|
||||||
|
panic(ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
stKey[0] = userPrefix
|
||||||
|
prefixLen := len(stKey) + groupIDSize
|
||||||
|
|
||||||
|
iter := storage.Find(ctx, stKey, storage.KeysOnly)
|
||||||
|
for iterator.Next(iter) {
|
||||||
|
key := iterator.Value(iter).([]byte)
|
||||||
|
if common.BytesEqual(user, key[prefixLen:]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version returns the version of the contract.
|
||||||
|
func Version() int {
|
||||||
|
return common.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyInList(ctx storage.Context, searchedKey interop.PublicKey, prefix []byte) bool {
|
||||||
|
return storage.Get(ctx, append(prefix, searchedKey...)) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func putKeyInList(ctx storage.Context, keyToPut interop.PublicKey, prefix []byte) {
|
||||||
|
storage.Put(ctx, append(prefix, keyToPut...), []byte{1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteKeyFromList(ctx storage.Context, keyToDelete interop.PublicKey, prefix []byte) {
|
||||||
|
storage.Delete(ctx, append(prefix, keyToDelete...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteByPrefix(ctx storage.Context, prefix []byte) {
|
||||||
|
iter := storage.Find(ctx, prefix, storage.KeysOnly)
|
||||||
|
for iterator.Next(iter) {
|
||||||
|
k := iterator.Value(iter).([]byte)
|
||||||
|
storage.Delete(ctx, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calledByOwnerOrAdmin(ctx storage.Context, owner []byte, adminPrefix []byte) bool {
|
||||||
|
if runtime.CheckWitness(owner) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
iter := storage.Find(ctx, adminPrefix, storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
for iterator.Next(iter) {
|
||||||
|
key := iterator.Value(iter).([]byte)
|
||||||
|
if runtime.CheckWitness(key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
|
@ -58,12 +58,8 @@ func newContainerInvoker(t *testing.T) (*neotest.ContractInvoker, *neotest.Contr
|
||||||
}
|
}
|
||||||
|
|
||||||
func setContainerOwner(c []byte, acc neotest.Signer) {
|
func setContainerOwner(c []byte, acc neotest.Signer) {
|
||||||
copy(c[6:], signerToOwner(acc))
|
|
||||||
}
|
|
||||||
|
|
||||||
func signerToOwner(acc neotest.Signer) []byte {
|
|
||||||
owner, _ := base58.Decode(address.Uint160ToString(acc.ScriptHash()))
|
owner, _ := base58.Decode(address.Uint160ToString(acc.ScriptHash()))
|
||||||
return owner
|
copy(c[6:], owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
type testContainer struct {
|
type testContainer struct {
|
||||||
|
@ -109,15 +105,15 @@ func TestContainerCount(t *testing.T) {
|
||||||
c.Invoke(t, stackitem.Null{}, "put", cnt3.value, cnt3.sig, cnt3.pub, cnt3.token)
|
c.Invoke(t, stackitem.Null{}, "put", cnt3.value, cnt3.sig, cnt3.pub, cnt3.token)
|
||||||
checkContainerList(t, c, [][]byte{cnt1.id[:], cnt2.id[:], cnt3.id[:]})
|
checkContainerList(t, c, [][]byte{cnt1.id[:], cnt2.id[:], cnt3.id[:]})
|
||||||
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt1.id[:], cnt1.sig, cnt1.pub, cnt1.token)
|
c.Invoke(t, stackitem.Null{}, "delete", cnt1.id[:], cnt1.sig, cnt1.token)
|
||||||
checkCount(t, 2)
|
checkCount(t, 2)
|
||||||
checkContainerList(t, c, [][]byte{cnt2.id[:], cnt3.id[:]})
|
checkContainerList(t, c, [][]byte{cnt2.id[:], cnt3.id[:]})
|
||||||
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt2.id[:], cnt2.sig, cnt2.pub, cnt2.token)
|
c.Invoke(t, stackitem.Null{}, "delete", cnt2.id[:], cnt2.sig, cnt2.token)
|
||||||
checkCount(t, 1)
|
checkCount(t, 1)
|
||||||
checkContainerList(t, c, [][]byte{cnt3.id[:]})
|
checkContainerList(t, c, [][]byte{cnt3.id[:]})
|
||||||
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt3.id[:], cnt3.sig, cnt3.pub, cnt3.token)
|
c.Invoke(t, stackitem.Null{}, "delete", cnt3.id[:], cnt3.sig, cnt3.token)
|
||||||
checkCount(t, 0)
|
checkCount(t, 0)
|
||||||
checkContainerList(t, c, [][]byte{})
|
checkContainerList(t, c, [][]byte{})
|
||||||
}
|
}
|
||||||
|
@ -205,7 +201,7 @@ func TestContainerPut(t *testing.T) {
|
||||||
c.InvokeFail(t, "name is already taken", "putNamed", putArgs...)
|
c.InvokeFail(t, "name is already taken", "putNamed", putArgs...)
|
||||||
})
|
})
|
||||||
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.token)
|
||||||
cNNS.Invoke(t, stackitem.Null{}, "resolve", "mycnt.frostfs", int64(nns.TXT))
|
cNNS.Invoke(t, stackitem.Null{}, "resolve", "mycnt.frostfs", int64(nns.TXT))
|
||||||
|
|
||||||
t.Run("register in advance", func(t *testing.T) {
|
t.Run("register in advance", func(t *testing.T) {
|
||||||
|
@ -243,43 +239,19 @@ func addContainer(t *testing.T, c, cBal *neotest.ContractInvoker) (neotest.Signe
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerDelete(t *testing.T) {
|
func TestContainerDelete(t *testing.T) {
|
||||||
c, cBal, cNm := newContainerInvoker(t)
|
c, cBal, _ := newContainerInvoker(t)
|
||||||
|
|
||||||
acc, cnt := addContainer(t, c, cBal)
|
acc, cnt := addContainer(t, c, cBal)
|
||||||
cAcc := c.WithSigners(acc)
|
cAcc := c.WithSigners(acc)
|
||||||
cAcc.InvokeFail(t, common.ErrAlphabetWitnessFailed, "delete",
|
cAcc.InvokeFail(t, common.ErrAlphabetWitnessFailed, "delete",
|
||||||
cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
cnt.id[:], cnt.sig, cnt.token)
|
||||||
|
|
||||||
newDelInfo := func(acc neotest.Signer, epoch int64) *stackitem.Struct {
|
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.token)
|
||||||
return stackitem.NewStruct([]stackitem.Item{
|
|
||||||
stackitem.NewBuffer([]byte(signerToOwner(acc))),
|
|
||||||
stackitem.NewBigInteger(big.NewInt(epoch)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
c.InvokeFail(t, container.NotFoundError, "deletionInfo", cnt.id[:])
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
|
||||||
c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
|
|
||||||
|
|
||||||
t.Run("multi-epoch", func(t *testing.T) {
|
|
||||||
cNm.Invoke(t, stackitem.Null{}, "newEpoch", 1)
|
|
||||||
|
|
||||||
t.Run("epoch tick does not change deletion info", func(t *testing.T) {
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
|
||||||
c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
|
|
||||||
})
|
|
||||||
|
|
||||||
acc1, cnt1 := addContainer(t, c, cBal)
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt1.id[:], cnt1.sig, cnt1.pub, cnt1.token)
|
|
||||||
c.Invoke(t, newDelInfo(acc, 0), "deletionInfo", cnt.id[:])
|
|
||||||
c.Invoke(t, newDelInfo(acc1, 1), "deletionInfo", cnt1.id[:])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("missing container", func(t *testing.T) {
|
t.Run("missing container", func(t *testing.T) {
|
||||||
id := cnt.id
|
id := cnt.id
|
||||||
id[0] ^= 0xFF
|
id[0] ^= 0xFF
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.token)
|
||||||
c.InvokeFail(t, container.NotFoundError, "deletionInfo", id[:])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
c.InvokeFail(t, container.NotFoundError, "get", cnt.id[:])
|
c.InvokeFail(t, container.NotFoundError, "get", cnt.id[:])
|
||||||
|
@ -296,7 +268,7 @@ func TestContainerOwner(t *testing.T) {
|
||||||
c.InvokeFail(t, container.NotFoundError, "owner", id[:])
|
c.InvokeFail(t, container.NotFoundError, "owner", id[:])
|
||||||
})
|
})
|
||||||
|
|
||||||
owner := signerToOwner(acc)
|
owner, _ := base58.Decode(address.Uint160ToString(acc.ScriptHash()))
|
||||||
c.Invoke(t, stackitem.NewBuffer(owner), "owner", cnt.id[:])
|
c.Invoke(t, stackitem.NewBuffer(owner), "owner", cnt.id[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/container"
|
||||||
|
"github.com/mr-tron/base58"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
@ -55,7 +57,7 @@ func TestFrostFSID_AddKey(t *testing.T) {
|
||||||
pubs[i] = p.PublicKey().Bytes()
|
pubs[i] = p.PublicKey().Bytes()
|
||||||
}
|
}
|
||||||
acc := e.NewAccount(t)
|
acc := e.NewAccount(t)
|
||||||
owner := signerToOwner(acc)
|
owner, _ := base58.Decode(address.Uint160ToString(acc.ScriptHash()))
|
||||||
e.Invoke(t, stackitem.Null{}, "addKey", owner,
|
e.Invoke(t, stackitem.Null{}, "addKey", owner,
|
||||||
[]interface{}{pubs[0], pubs[1]})
|
[]interface{}{pubs[0], pubs[1]})
|
||||||
|
|
||||||
|
|
330
tests/subnet_test.go
Normal file
330
tests/subnet_test.go
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/subnet"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const subnetPath = "../subnet"
|
||||||
|
|
||||||
|
func deploySubnetContract(t *testing.T, e *neotest.Executor) util.Uint160 {
|
||||||
|
c := neotest.CompileFile(t, e.CommitteeHash, subnetPath, path.Join(subnetPath, "config.yml"))
|
||||||
|
args := []interface{}{false}
|
||||||
|
e.DeployContract(t, c, args)
|
||||||
|
return c.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSubnetInvoker(t *testing.T) *neotest.ContractInvoker {
|
||||||
|
e := newExecutor(t)
|
||||||
|
h := deploySubnetContract(t, e)
|
||||||
|
return e.CommitteeInvoker(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_Version(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
e.Invoke(t, common.Version, "version")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_Put(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
pub, ok := vm.ParseSignatureContract(acc.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
id := make([]byte, 5)
|
||||||
|
binary.LittleEndian.PutUint32(id, 123)
|
||||||
|
info := randomBytes(10)
|
||||||
|
|
||||||
|
e.InvokeFail(t, common.ErrWitnessFailed, "put", id, pub, info)
|
||||||
|
|
||||||
|
cAcc := e.WithSigners(acc)
|
||||||
|
cAcc.InvokeFail(t, common.ErrAlphabetWitnessFailed, "put", id, pub, info)
|
||||||
|
|
||||||
|
cBoth := e.WithSigners(e.Committee, acc)
|
||||||
|
cBoth.InvokeFail(t, subnet.ErrInvalidSubnetID, "put", []byte{1, 2, 3}, pub, info)
|
||||||
|
cBoth.InvokeFail(t, subnet.ErrInvalidOwner, "put", id, pub[10:], info)
|
||||||
|
cBoth.Invoke(t, stackitem.Null{}, "put", id, pub, info)
|
||||||
|
cAcc.Invoke(t, stackitem.NewBuffer(info), "get", id)
|
||||||
|
cBoth.InvokeFail(t, subnet.ErrAlreadyExists, "put", id, pub, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_Delete(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
e.InvokeFail(t, common.ErrWitnessFailed, "delete", id)
|
||||||
|
|
||||||
|
cAcc := e.WithSigners(owner)
|
||||||
|
cAcc.InvokeFail(t, subnet.ErrInvalidSubnetID, "delete", []byte{1, 1, 1, 1})
|
||||||
|
cAcc.Invoke(t, stackitem.Null{}, "delete", []byte{1, 1, 1, 1, 1})
|
||||||
|
cAcc.Invoke(t, stackitem.Null{}, "delete", id)
|
||||||
|
cAcc.InvokeFail(t, subnet.ErrNotExist, "get", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_AddNodeAdmin(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
adm := e.NewAccount(t)
|
||||||
|
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "addNodeAdmin"
|
||||||
|
|
||||||
|
e.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, admPub)
|
||||||
|
e.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, admPub[1:])
|
||||||
|
e.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, admPub)
|
||||||
|
|
||||||
|
cAdm := e.WithSigners(adm)
|
||||||
|
cAdm.InvokeFail(t, common.ErrOwnerWitnessFailed, method, id, admPub)
|
||||||
|
|
||||||
|
cOwner := e.WithSigners(owner)
|
||||||
|
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
||||||
|
|
||||||
|
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_RemoveNodeAdmin(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
adm := e.NewAccount(t)
|
||||||
|
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "removeNodeAdmin"
|
||||||
|
|
||||||
|
e.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, admPub)
|
||||||
|
e.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, admPub[1:])
|
||||||
|
e.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, admPub)
|
||||||
|
|
||||||
|
cAdm := e.WithSigners(adm)
|
||||||
|
cAdm.InvokeFail(t, common.ErrOwnerWitnessFailed, method, id, admPub)
|
||||||
|
|
||||||
|
cOwner := e.WithSigners(owner)
|
||||||
|
|
||||||
|
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
||||||
|
cOwner.Invoke(t, stackitem.Null{}, "addNodeAdmin", id, admPub)
|
||||||
|
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
||||||
|
cOwner.Invoke(t, stackitem.Null{}, method, id, admPub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_AddNode(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
node := e.NewAccount(t)
|
||||||
|
nodePub, ok := vm.ParseSignatureContract(node.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "addNode"
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, nodePub)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidNode, method, id, nodePub[1:])
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, nodePub)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_RemoveNode(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
node := e.NewAccount(t)
|
||||||
|
nodePub, ok := vm.ParseSignatureContract(node.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
adm := e.NewAccount(t)
|
||||||
|
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "removeNode"
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, nodePub)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidNode, method, id, nodePub[1:])
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, nodePub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addNode", id, nodePub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
||||||
|
|
||||||
|
cAdm := cOwn.WithSigners(adm)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addNodeAdmin", id, admPub)
|
||||||
|
cAdm.Invoke(t, stackitem.Null{}, method, id, nodePub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_NodeAllowed(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
node := e.NewAccount(t)
|
||||||
|
nodePub, ok := vm.ParseSignatureContract(node.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "nodeAllowed"
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, nodePub)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidNode, method, id, nodePub[1:])
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, nodePub)
|
||||||
|
cOwn.Invoke(t, stackitem.NewBool(false), method, id, nodePub)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addNode", id, nodePub)
|
||||||
|
cOwn.Invoke(t, stackitem.NewBool(true), method, id, nodePub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_AddClientAdmin(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
adm := e.NewAccount(t)
|
||||||
|
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "addClientAdmin"
|
||||||
|
|
||||||
|
groupId := randomBytes(5)
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, admPub)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, groupId, admPub[1:])
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, admPub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_RemoveClientAdmin(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
adm := e.NewAccount(t)
|
||||||
|
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "removeClientAdmin"
|
||||||
|
|
||||||
|
groupId := randomBytes(5)
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, admPub)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidAdmin, method, id, groupId, admPub[1:])
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, admPub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, admPub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_AddUser(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
adm := e.NewAccount(t)
|
||||||
|
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
user := randomBytes(27)
|
||||||
|
|
||||||
|
groupId := randomBytes(5)
|
||||||
|
|
||||||
|
const method = "addUser"
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, user)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, user)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub)
|
||||||
|
|
||||||
|
cAdm := e.WithSigners(adm)
|
||||||
|
cAdm.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_RemoveUser(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
groupId := randomBytes(5)
|
||||||
|
user := randomBytes(27)
|
||||||
|
|
||||||
|
adm := e.NewAccount(t)
|
||||||
|
admPub, ok := vm.ParseSignatureContract(adm.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
const method = "removeUser"
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrInvalidSubnetID, method, []byte{0, 0, 0, 0}, groupId, user)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, groupId, user)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addUser", id, groupId, user)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
||||||
|
|
||||||
|
cAdm := cOwn.WithSigners(adm)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addClientAdmin", id, groupId, admPub)
|
||||||
|
cAdm.Invoke(t, stackitem.Null{}, method, id, groupId, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubnet_UserAllowed(t *testing.T) {
|
||||||
|
e := newSubnetInvoker(t)
|
||||||
|
|
||||||
|
id, owner := createSubnet(t, e)
|
||||||
|
|
||||||
|
groupId := randomBytes(5)
|
||||||
|
|
||||||
|
user := randomBytes(27)
|
||||||
|
|
||||||
|
const method = "userAllowed"
|
||||||
|
|
||||||
|
cOwn := e.WithSigners(owner)
|
||||||
|
cOwn.InvokeFail(t, subnet.ErrNotExist, method, []byte{0, 0, 0, 0, 0}, user)
|
||||||
|
|
||||||
|
cOwn.Invoke(t, stackitem.NewBool(false), method, id, user)
|
||||||
|
cOwn.Invoke(t, stackitem.Null{}, "addUser", id, groupId, user)
|
||||||
|
cOwn.Invoke(t, stackitem.NewBool(true), method, id, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSubnet(t *testing.T, e *neotest.ContractInvoker) (id []byte, owner neotest.Signer) {
|
||||||
|
var (
|
||||||
|
ok bool
|
||||||
|
pub []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
owner = e.NewAccount(t)
|
||||||
|
pub, ok = vm.ParseSignatureContract(owner.Script())
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
id = make([]byte, 5)
|
||||||
|
binary.LittleEndian.PutUint32(id, 123)
|
||||||
|
info := randomBytes(10)
|
||||||
|
|
||||||
|
cBoth := e.WithSigners(e.Committee, owner)
|
||||||
|
cBoth.Invoke(t, stackitem.Null{}, "put", id, pub, info)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in a new issue