[#50] Drop audit and reputation contracts

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
Evgenii Stratonikov 2023-11-07 15:23:14 +03:00
parent d890a7eba4
commit 0ea65ca637
10 changed files with 1 additions and 822 deletions

View file

@ -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)

View file

@ -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,
}
}

View file

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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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...)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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))
}