[#50] Drop audit and reputation contracts
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
parent
d890a7eba4
commit
0ea65ca637
10 changed files with 1 additions and 822 deletions
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 policy
|
morph_sc = balance container frostfsid netmap proxy policy
|
||||||
mainnet_sc = frostfs processing
|
mainnet_sc = frostfs processing
|
||||||
nns_sc = nns
|
nns_sc = nns
|
||||||
all_sc = $(alphabet_sc) $(morph_sc) $(mainnet_sc) $(nns_sc)
|
all_sc = $(alphabet_sc) $(morph_sc) $(mainnet_sc) $(nns_sc)
|
||||||
|
|
|
@ -1,220 +0,0 @@
|
||||||
package audit
|
|
||||||
|
|
||||||
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/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"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
auditHeader struct {
|
|
||||||
epoch int
|
|
||||||
cid []byte
|
|
||||||
from interop.PublicKey
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Audit key is a combination of the epoch, the container ID and the public key of the node that
|
|
||||||
// has executed the audit. Together, it shouldn't be 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 any = a.epoch
|
|
||||||
|
|
||||||
hashedKey := crypto.Sha256(a.from)
|
|
||||||
shortedKey := hashedKey[:maxKeySize]
|
|
||||||
|
|
||||||
return append(buf.([]byte), append(a.cid, shortedKey...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
netmapContractKey = "netmapScriptHash"
|
|
||||||
)
|
|
||||||
|
|
||||||
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 {
|
|
||||||
addrNetmap interop.Hash160
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(args.addrNetmap) != interop.Hash160Len {
|
|
||||||
panic("incorrect length of contract script hash")
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.Put(ctx, netmapContractKey, args.addrNetmap)
|
|
||||||
|
|
||||||
runtime.Log("audit contract initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update method updates contract source code and manifest. It can be invoked
|
|
||||||
// only by committee.
|
|
||||||
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("audit contract updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put method stores a stable marshalled `DataAuditResult` structure. It can be
|
|
||||||
// invoked only by Inner Ring nodes.
|
|
||||||
//
|
|
||||||
// Inner Ring nodes perform audit of containers and produce `DataAuditResult`
|
|
||||||
// structures. They are stored in audit contract and used for settlements
|
|
||||||
// in later epochs.
|
|
||||||
func Put(rawAuditResult []byte) {
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
innerRing := common.InnerRingNodes()
|
|
||||||
|
|
||||||
hdr := newAuditHeader(rawAuditResult)
|
|
||||||
presented := false
|
|
||||||
|
|
||||||
for i := range innerRing {
|
|
||||||
ir := innerRing[i]
|
|
||||||
if common.BytesEqual(ir, 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 a stable marshaled DataAuditResult structure.
|
|
||||||
//
|
|
||||||
// The 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 a list of all available DataAuditResult IDs from
|
|
||||||
// the contract storage.
|
|
||||||
func List() [][]byte {
|
|
||||||
ctx := storage.GetReadOnlyContext()
|
|
||||||
it := storage.Find(ctx, []byte{}, storage.KeysOnly)
|
|
||||||
|
|
||||||
return list(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByEpoch method returns a list of DataAuditResult IDs generated during
|
|
||||||
// the specified epoch.
|
|
||||||
func ListByEpoch(epoch int) [][]byte {
|
|
||||||
ctx := storage.GetReadOnlyContext()
|
|
||||||
var buf any = epoch
|
|
||||||
it := storage.Find(ctx, buf.([]byte), storage.KeysOnly)
|
|
||||||
|
|
||||||
return list(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByCID method returns a list of DataAuditResult IDs generated during
|
|
||||||
// the specified epoch for the specified container.
|
|
||||||
func ListByCID(epoch int, cid []byte) [][]byte {
|
|
||||||
ctx := storage.GetReadOnlyContext()
|
|
||||||
|
|
||||||
var buf any = epoch
|
|
||||||
|
|
||||||
prefix := append(buf.([]byte), cid...)
|
|
||||||
it := storage.Find(ctx, prefix, storage.KeysOnly)
|
|
||||||
|
|
||||||
return list(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByNode method returns a list of DataAuditResult IDs generated in
|
|
||||||
// the specified epoch for the specified container by the 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),
|
|
||||||
}
|
|
||||||
|
|
||||||
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 the version of the contract.
|
|
||||||
func Version() int {
|
|
||||||
return common.Version
|
|
||||||
}
|
|
||||||
|
|
||||||
// readNext reads the length from the first byte, and then reads data (max 127 bytes).
|
|
||||||
func readNext(input []byte) ([]byte, int) {
|
|
||||||
var buf any = 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 any = 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,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
name: "Audit"
|
|
||||||
safemethods: ["get", "list", "listByEpoch", "listByCID", "listByNode", "version"]
|
|
||||||
permissions:
|
|
||||||
- methods: ["update"]
|
|
29
audit/doc.go
29
audit/doc.go
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
Audit contract is a contract deployed in FrostFS sidechain.
|
|
||||||
|
|
||||||
Inner Ring nodes perform audit of the registered containers during every epoch.
|
|
||||||
If a container contains StorageGroup objects, an 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 have been examined and
|
|
||||||
see the status of these checks. Regarding this information, the container owner is
|
|
||||||
charged for data storage.
|
|
||||||
|
|
||||||
Audit contract is used as a reliable and verifiable storage for all
|
|
||||||
DataAuditResult structures. At the end of data audit routine, 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 make a list and get these AuditResultStructures from the audit contract.
|
|
||||||
|
|
||||||
# Contract notifications
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,13 +0,0 @@
|
||||||
name: "Reputation"
|
|
||||||
safemethods: ["get", "getByID", "listByEpoch"]
|
|
||||||
permissions:
|
|
||||||
- methods: ["update"]
|
|
||||||
events:
|
|
||||||
- name: reputationPut
|
|
||||||
parameters:
|
|
||||||
- name: epoch
|
|
||||||
type: Integer
|
|
||||||
- name: peerID
|
|
||||||
type: ByteArray
|
|
||||||
- name: value
|
|
||||||
type: ByteArray
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
Reputation contract is a contract deployed in FrostFS sidechain.
|
|
||||||
|
|
||||||
Inner Ring nodes produce data audit for each container during 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 succeeds.
|
|
||||||
|
|
||||||
# Contract notifications
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,119 +0,0 @@
|
||||||
package reputation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
|
|
||||||
"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/runtime"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
reputationValuePrefix = 'r'
|
|
||||||
reputationCountPrefix = 'c'
|
|
||||||
)
|
|
||||||
|
|
||||||
func _deploy(data any, isUpdate bool) {
|
|
||||||
if isUpdate {
|
|
||||||
args := data.([]any)
|
|
||||||
common.CheckVersion(args[len(args)-1].(int))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.Log("reputation contract initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update method updates contract source code and manifest. It can be invoked
|
|
||||||
// only by committee.
|
|
||||||
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("reputation contract updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put method saves DataAuditResult in contract storage. It can be invoked only by
|
|
||||||
// Inner Ring nodes. It does not require multisignature invocations.
|
|
||||||
//
|
|
||||||
// Epoch is the epoch number when DataAuditResult structure was generated.
|
|
||||||
// PeerID contains public keys of the Inner Ring node that has produced DataAuditResult.
|
|
||||||
// Value contains a stable marshaled structure of DataAuditResult.
|
|
||||||
func Put(epoch int, peerID []byte, value []byte) {
|
|
||||||
ctx := storage.GetContext()
|
|
||||||
|
|
||||||
multiaddr := common.AlphabetAddress()
|
|
||||||
if !runtime.CheckWitness(multiaddr) {
|
|
||||||
runtime.Notify("reputationPut", epoch, peerID, value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
id := storageID(epoch, peerID)
|
|
||||||
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 a list of all stable marshaled DataAuditResult structures
|
|
||||||
// produced by the specified Inner Ring node during the specified epoch.
|
|
||||||
func Get(epoch int, peerID []byte) [][]byte {
|
|
||||||
id := storageID(epoch, peerID)
|
|
||||||
return GetByID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByID method returns a list of all stable marshaled DataAuditResult with
|
|
||||||
// the specified id. Use ListByEpoch method to obtain the 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 a 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 the version of the contract.
|
|
||||||
func Version() int {
|
|
||||||
return common.Version
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageID(epoch int, peerID []byte) []byte {
|
|
||||||
var buf any = epoch
|
|
||||||
|
|
||||||
return append(buf.([]byte), peerID...)
|
|
||||||
}
|
|
|
@ -1,128 +0,0 @@
|
||||||
// Package audit contains RPC wrappers for Audit contract.
|
|
||||||
//
|
|
||||||
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
|
|
||||||
package audit
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get invokes `get` method of contract.
|
|
||||||
func (c *ContractReader) Get(id []byte) ([]byte, error) {
|
|
||||||
return unwrap.Bytes(c.invoker.Call(c.hash, "get", id))
|
|
||||||
}
|
|
||||||
|
|
||||||
// List invokes `list` method of contract.
|
|
||||||
func (c *ContractReader) List() ([]stackitem.Item, error) {
|
|
||||||
return unwrap.Array(c.invoker.Call(c.hash, "list"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByCID invokes `listByCID` method of contract.
|
|
||||||
func (c *ContractReader) ListByCID(epoch *big.Int, cid []byte) ([]stackitem.Item, error) {
|
|
||||||
return unwrap.Array(c.invoker.Call(c.hash, "listByCID", epoch, cid))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByEpoch invokes `listByEpoch` method of contract.
|
|
||||||
func (c *ContractReader) ListByEpoch(epoch *big.Int) ([]stackitem.Item, error) {
|
|
||||||
return unwrap.Array(c.invoker.Call(c.hash, "listByEpoch", epoch))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByNode invokes `listByNode` method of contract.
|
|
||||||
func (c *ContractReader) ListByNode(epoch *big.Int, cid []byte, key *keys.PublicKey) ([]stackitem.Item, error) {
|
|
||||||
return unwrap.Array(c.invoker.Call(c.hash, "listByNode", epoch, cid, key))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version invokes `version` method of contract.
|
|
||||||
func (c *ContractReader) Version() (*big.Int, error) {
|
|
||||||
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(rawAuditResult []byte) (util.Uint256, uint32, error) {
|
|
||||||
return c.actor.SendCall(c.hash, "put", rawAuditResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(rawAuditResult []byte) (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeCall(c.hash, "put", rawAuditResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(rawAuditResult []byte) (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeUnsignedCall(c.hash, "put", nil, rawAuditResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,207 +0,0 @@
|
||||||
// Package reputation contains RPC wrappers for Reputation contract.
|
|
||||||
//
|
|
||||||
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
|
|
||||||
package reputation
|
|
||||||
|
|
||||||
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/unwrap"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReputationPutEvent represents "reputationPut" event emitted by the contract.
|
|
||||||
type ReputationPutEvent struct {
|
|
||||||
Epoch *big.Int
|
|
||||||
PeerID []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}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get invokes `get` method of contract.
|
|
||||||
func (c *ContractReader) Get(epoch *big.Int, peerID []byte) ([]stackitem.Item, error) {
|
|
||||||
return unwrap.Array(c.invoker.Call(c.hash, "get", epoch, peerID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByID invokes `getByID` method of contract.
|
|
||||||
func (c *ContractReader) GetByID(id []byte) ([]stackitem.Item, error) {
|
|
||||||
return unwrap.Array(c.invoker.Call(c.hash, "getByID", id))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListByEpoch invokes `listByEpoch` method of contract.
|
|
||||||
func (c *ContractReader) ListByEpoch(epoch *big.Int) ([]stackitem.Item, error) {
|
|
||||||
return unwrap.Array(c.invoker.Call(c.hash, "listByEpoch", epoch))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(epoch *big.Int, peerID []byte, value []byte) (util.Uint256, uint32, error) {
|
|
||||||
return c.actor.SendCall(c.hash, "put", epoch, peerID, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(epoch *big.Int, peerID []byte, value []byte) (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeCall(c.hash, "put", epoch, peerID, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(epoch *big.Int, peerID []byte, value []byte) (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeUnsignedCall(c.hash, "put", nil, epoch, peerID, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version creates a transaction invoking `version` 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) Version() (util.Uint256, uint32, error) {
|
|
||||||
return c.actor.SendCall(c.hash, "version")
|
|
||||||
}
|
|
||||||
|
|
||||||
// VersionTransaction creates a transaction invoking `version` method of the contract.
|
|
||||||
// This transaction is signed, but not sent to the network, instead it's
|
|
||||||
// returned to the caller.
|
|
||||||
func (c *Contract) VersionTransaction() (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeCall(c.hash, "version")
|
|
||||||
}
|
|
||||||
|
|
||||||
// VersionUnsigned creates a transaction invoking `version` 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) VersionUnsigned() (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeUnsignedCall(c.hash, "version", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReputationPutEventsFromApplicationLog retrieves a set of all emitted events
|
|
||||||
// with "reputationPut" name from the provided [result.ApplicationLog].
|
|
||||||
func ReputationPutEventsFromApplicationLog(log *result.ApplicationLog) ([]*ReputationPutEvent, error) {
|
|
||||||
if log == nil {
|
|
||||||
return nil, errors.New("nil application log")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res []*ReputationPutEvent
|
|
||||||
for i, ex := range log.Executions {
|
|
||||||
for j, e := range ex.Events {
|
|
||||||
if e.Name != "reputationPut" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
event := new(ReputationPutEvent)
|
|
||||||
err := event.FromStackItem(e.Item)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to deserialize ReputationPutEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
|
|
||||||
}
|
|
||||||
res = append(res, event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromStackItem converts provided [stackitem.Array] to ReputationPutEvent or
|
|
||||||
// returns an error if it's not possible to do to so.
|
|
||||||
func (e *ReputationPutEvent) 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.Epoch, err = arr[index].TryInteger()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("field Epoch: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
index++
|
|
||||||
e.PeerID, err = arr[index].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("field PeerID: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
index++
|
|
||||||
e.Value, err = arr[index].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("field Value: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
||||||
)
|
|
||||||
|
|
||||||
const reputationPath = "../reputation"
|
|
||||||
|
|
||||||
func deployReputationContract(t *testing.T, e *neotest.Executor) util.Uint160 {
|
|
||||||
c := neotest.CompileFile(t, e.CommitteeHash, reputationPath,
|
|
||||||
path.Join(reputationPath, "config.yml"))
|
|
||||||
|
|
||||||
e.DeployContract(t, c, []any{})
|
|
||||||
return c.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func newReputationInvoker(t *testing.T) *neotest.ContractInvoker {
|
|
||||||
bc, acc := chain.NewSingle(t)
|
|
||||||
e := neotest.NewExecutor(t, bc, acc, acc)
|
|
||||||
h := deployReputationContract(t, e)
|
|
||||||
return e.CommitteeInvoker(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReputation_Put(t *testing.T) {
|
|
||||||
e := newReputationInvoker(t)
|
|
||||||
|
|
||||||
peerID := []byte{1, 2, 3}
|
|
||||||
e.Invoke(t, stackitem.Null{}, "put", int64(1), peerID, []byte{4})
|
|
||||||
|
|
||||||
t.Run("concurrent invocations", func(t *testing.T) {
|
|
||||||
repValue1 := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
||||||
repValue2 := []byte{10, 20, 30, 40, 50, 60, 70, 80}
|
|
||||||
tx1 := e.PrepareInvoke(t, "put", int64(1), peerID, repValue1)
|
|
||||||
tx2 := e.PrepareInvoke(t, "put", int64(1), peerID, repValue2)
|
|
||||||
|
|
||||||
e.AddNewBlock(t, tx1, tx2)
|
|
||||||
e.CheckHalt(t, tx1.Hash(), stackitem.Null{})
|
|
||||||
e.CheckHalt(t, tx2.Hash(), stackitem.Null{})
|
|
||||||
|
|
||||||
t.Run("get all", func(t *testing.T) {
|
|
||||||
result := stackitem.NewArray([]stackitem.Item{
|
|
||||||
stackitem.NewBuffer([]byte{4}),
|
|
||||||
stackitem.NewBuffer(repValue1),
|
|
||||||
stackitem.NewBuffer(repValue2),
|
|
||||||
})
|
|
||||||
e.Invoke(t, result, "get", int64(1), peerID)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReputation_ListByEpoch(t *testing.T) {
|
|
||||||
e := newReputationInvoker(t)
|
|
||||||
|
|
||||||
peerIDs := []string{"peer1", "peer2"}
|
|
||||||
e.Invoke(t, stackitem.Null{}, "put", int64(1), peerIDs[0], []byte{1})
|
|
||||||
e.Invoke(t, stackitem.Null{}, "put", int64(1), peerIDs[0], []byte{2})
|
|
||||||
e.Invoke(t, stackitem.Null{}, "put", int64(2), peerIDs[1], []byte{3})
|
|
||||||
e.Invoke(t, stackitem.Null{}, "put", int64(2), peerIDs[0], []byte{4})
|
|
||||||
e.Invoke(t, stackitem.Null{}, "put", int64(2), peerIDs[1], []byte{5})
|
|
||||||
|
|
||||||
result := stackitem.NewArray([]stackitem.Item{
|
|
||||||
stackitem.NewBuffer(append([]byte{1}, peerIDs[0]...)),
|
|
||||||
})
|
|
||||||
e.Invoke(t, result, "listByEpoch", int64(1))
|
|
||||||
|
|
||||||
result = stackitem.NewArray([]stackitem.Item{
|
|
||||||
stackitem.NewBuffer(append([]byte{2}, peerIDs[0]...)),
|
|
||||||
stackitem.NewBuffer(append([]byte{2}, peerIDs[1]...)),
|
|
||||||
})
|
|
||||||
e.Invoke(t, result, "listByEpoch", int64(2))
|
|
||||||
}
|
|
Loading…
Reference in a new issue