forked from TrueCloudLab/frostfs-node
[#280] ir: Add alphabet processor unit tests
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
53693071de
commit
e89fa7f69f
4 changed files with 316 additions and 15 deletions
|
@ -104,13 +104,13 @@ func (l GlagoliticLetter) String() string {
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
type alphabetContracts map[GlagoliticLetter]util.Uint160
|
type AlphabetContracts map[GlagoliticLetter]util.Uint160
|
||||||
|
|
||||||
func newAlphabetContracts() alphabetContracts {
|
func NewAlphabetContracts() AlphabetContracts {
|
||||||
return make(map[GlagoliticLetter]util.Uint160, lastLetterNum)
|
return make(map[GlagoliticLetter]util.Uint160, lastLetterNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a alphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
|
func (a AlphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
|
||||||
if ind < 0 || ind >= int(lastLetterNum) {
|
if ind < 0 || ind >= int(lastLetterNum) {
|
||||||
return util.Uint160{}, false
|
return util.Uint160{}, false
|
||||||
}
|
}
|
||||||
|
@ -120,16 +120,16 @@ func (a alphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
|
||||||
return contract, ok
|
return contract, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a alphabetContracts) indexOutOfRange(ind int) bool {
|
func (a AlphabetContracts) indexOutOfRange(ind int) bool {
|
||||||
return ind < 0 && ind >= len(a)
|
return ind < 0 && ind >= len(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a alphabetContracts) iterate(f func(GlagoliticLetter, util.Uint160)) {
|
func (a AlphabetContracts) iterate(f func(GlagoliticLetter, util.Uint160)) {
|
||||||
for letter, contract := range a {
|
for letter, contract := range a {
|
||||||
f(letter, contract)
|
f(letter, contract)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *alphabetContracts) set(l GlagoliticLetter, h util.Uint160) {
|
func (a *AlphabetContracts) set(l GlagoliticLetter, h util.Uint160) {
|
||||||
(*a)[l] = h
|
(*a)[l] = h
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type contracts struct {
|
||||||
processing util.Uint160 // in mainnet
|
processing util.Uint160 // in mainnet
|
||||||
frostfsID util.Uint160 // in morph
|
frostfsID util.Uint160 // in morph
|
||||||
|
|
||||||
alphabet alphabetContracts // in morph
|
alphabet AlphabetContracts // in morph
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseContracts(cfg *viper.Viper, morph nnsResolver, withoutMainNet, withoutMainNotary, withoutSideNotary bool) (*contracts, error) {
|
func parseContracts(cfg *viper.Viper, morph nnsResolver, withoutMainNet, withoutMainNotary, withoutSideNotary bool) (*contracts, error) {
|
||||||
|
@ -76,9 +76,9 @@ func parseContracts(cfg *viper.Viper, morph nnsResolver, withoutMainNet, without
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAlphabetContracts(cfg *viper.Viper, morph nnsResolver) (alphabetContracts, error) {
|
func parseAlphabetContracts(cfg *viper.Viper, morph nnsResolver) (AlphabetContracts, error) {
|
||||||
num := GlagoliticLetter(cfg.GetUint("contracts.alphabet.amount"))
|
num := GlagoliticLetter(cfg.GetUint("contracts.alphabet.amount"))
|
||||||
alpha := newAlphabetContracts()
|
alpha := NewAlphabetContracts()
|
||||||
|
|
||||||
if num > lastLetterNum {
|
if num > lastLetterNum {
|
||||||
return nil, fmt.Errorf("amount of alphabet contracts overflows glagolitsa %d > %d", num, lastLetterNum)
|
return nil, fmt.Errorf("amount of alphabet contracts overflows glagolitsa %d > %d", num, lastLetterNum)
|
||||||
|
|
282
pkg/innerring/processors/alphabet/handlers_test.go
Normal file
282
pkg/innerring/processors/alphabet/handlers_test.go
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
package alphabet_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/alphabet"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/timers"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProcessorEmitsGasToNetmapAndAlphabet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var emission uint64 = 100_000
|
||||||
|
var index int = 5
|
||||||
|
var parsedWallets []util.Uint160 = []util.Uint160{{20}, {25}}
|
||||||
|
|
||||||
|
alphabetContracts := innerring.NewAlphabetContracts()
|
||||||
|
for i := 0; i <= index; i++ {
|
||||||
|
alphabetContracts[innerring.GlagoliticLetter(i)] = util.Uint160{uint8(i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
morphClient := &testMorphClient{}
|
||||||
|
|
||||||
|
var node1 netmap.NodeInfo
|
||||||
|
key1, err := keys.NewPublicKeyFromString("038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35")
|
||||||
|
require.NoError(t, err, "failed to parse key1")
|
||||||
|
node1.SetPublicKey(key1.Bytes())
|
||||||
|
|
||||||
|
var node2 netmap.NodeInfo
|
||||||
|
key2, err := keys.NewPublicKeyFromString("02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3")
|
||||||
|
require.NoError(t, err, "failed to parse key2")
|
||||||
|
node2.SetPublicKey(key2.Bytes())
|
||||||
|
|
||||||
|
nodes := []netmap.NodeInfo{node1, node2}
|
||||||
|
|
||||||
|
network := &netmap.NetMap{}
|
||||||
|
network.SetNodes(nodes)
|
||||||
|
|
||||||
|
netmapClient := &testNetmapClient{
|
||||||
|
netmap: network,
|
||||||
|
}
|
||||||
|
|
||||||
|
params := &alphabet.Params{
|
||||||
|
ParsedWallets: parsedWallets,
|
||||||
|
Log: test.NewLogger(t, true),
|
||||||
|
PoolSize: 2,
|
||||||
|
StorageEmission: emission,
|
||||||
|
IRList: &testIndexer{index: index},
|
||||||
|
AlphabetContracts: alphabetContracts,
|
||||||
|
MorphClient: morphClient,
|
||||||
|
NetmapClient: netmapClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
processor, err := alphabet.New(params)
|
||||||
|
require.NoError(t, err, "failed to create processor instance")
|
||||||
|
|
||||||
|
processor.HandleGasEmission(timers.NewAlphabetEmitTick{})
|
||||||
|
|
||||||
|
processor.WaitPoolRunning()
|
||||||
|
|
||||||
|
require.EqualValues(t, []invokedMethod{
|
||||||
|
{
|
||||||
|
contract: alphabetContracts[innerring.GlagoliticLetter(index)],
|
||||||
|
fee: 0,
|
||||||
|
method: "emit",
|
||||||
|
},
|
||||||
|
}, morphClient.invokedMethods, "invalid invoked morph methods")
|
||||||
|
|
||||||
|
require.EqualValues(t, []transferGas{
|
||||||
|
{
|
||||||
|
receiver: key1.GetScriptHash(),
|
||||||
|
amount: fixedn.Fixed8(25_000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
receiver: key2.GetScriptHash(),
|
||||||
|
amount: fixedn.Fixed8(25_000),
|
||||||
|
},
|
||||||
|
}, morphClient.transferedGas, "invalid transfered Gas")
|
||||||
|
|
||||||
|
require.EqualValues(t, []batchTransferGas{
|
||||||
|
{
|
||||||
|
receivers: parsedWallets,
|
||||||
|
amount: fixedn.Fixed8(25_000),
|
||||||
|
},
|
||||||
|
}, morphClient.batchTransferedGas, "invalid batch transfered Gas")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessorEmitsGasToNetmapIfNoParsedWallets(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var emission uint64 = 100_000
|
||||||
|
var index int = 5
|
||||||
|
var parsedWallets []util.Uint160 = []util.Uint160{}
|
||||||
|
|
||||||
|
alphabetContracts := innerring.NewAlphabetContracts()
|
||||||
|
for i := 0; i <= index; i++ {
|
||||||
|
alphabetContracts[innerring.GlagoliticLetter(i)] = util.Uint160{uint8(i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
morphClient := &testMorphClient{}
|
||||||
|
|
||||||
|
var node1 netmap.NodeInfo
|
||||||
|
key1, err := keys.NewPublicKeyFromString("038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35")
|
||||||
|
require.NoError(t, err, "failed to parse key1")
|
||||||
|
node1.SetPublicKey(key1.Bytes())
|
||||||
|
|
||||||
|
var node2 netmap.NodeInfo
|
||||||
|
key2, err := keys.NewPublicKeyFromString("02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3")
|
||||||
|
require.NoError(t, err, "failed to parse key2")
|
||||||
|
node2.SetPublicKey(key2.Bytes())
|
||||||
|
|
||||||
|
nodes := []netmap.NodeInfo{node1, node2}
|
||||||
|
|
||||||
|
network := &netmap.NetMap{}
|
||||||
|
network.SetNodes(nodes)
|
||||||
|
|
||||||
|
netmapClient := &testNetmapClient{
|
||||||
|
netmap: network,
|
||||||
|
}
|
||||||
|
|
||||||
|
params := &alphabet.Params{
|
||||||
|
ParsedWallets: parsedWallets,
|
||||||
|
Log: test.NewLogger(t, true),
|
||||||
|
PoolSize: 2,
|
||||||
|
StorageEmission: emission,
|
||||||
|
IRList: &testIndexer{index: index},
|
||||||
|
AlphabetContracts: alphabetContracts,
|
||||||
|
MorphClient: morphClient,
|
||||||
|
NetmapClient: netmapClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
processor, err := alphabet.New(params)
|
||||||
|
require.NoError(t, err, "failed to create processor instance")
|
||||||
|
|
||||||
|
processor.HandleGasEmission(timers.NewAlphabetEmitTick{})
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
require.EqualValues(t, []invokedMethod{
|
||||||
|
{
|
||||||
|
contract: alphabetContracts[innerring.GlagoliticLetter(index)],
|
||||||
|
fee: 0,
|
||||||
|
method: "emit",
|
||||||
|
},
|
||||||
|
}, morphClient.invokedMethods, "invalid invoked morph methods")
|
||||||
|
|
||||||
|
require.EqualValues(t, []transferGas{
|
||||||
|
{
|
||||||
|
receiver: key1.GetScriptHash(),
|
||||||
|
amount: fixedn.Fixed8(50_000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
receiver: key2.GetScriptHash(),
|
||||||
|
amount: fixedn.Fixed8(50_000),
|
||||||
|
},
|
||||||
|
}, morphClient.transferedGas, "invalid transfered Gas")
|
||||||
|
|
||||||
|
require.Equal(t, 0, len(morphClient.batchTransferedGas), "invalid batch transfered Gas")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessorDoesntEmitGasIfNoNetmapOrParsedWallets(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var emission uint64 = 100_000
|
||||||
|
var index int = 5
|
||||||
|
var parsedWallets []util.Uint160 = []util.Uint160{}
|
||||||
|
|
||||||
|
alphabetContracts := innerring.NewAlphabetContracts()
|
||||||
|
for i := 0; i <= index; i++ {
|
||||||
|
alphabetContracts[innerring.GlagoliticLetter(i)] = util.Uint160{uint8(i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
morphClient := &testMorphClient{}
|
||||||
|
|
||||||
|
nodes := []netmap.NodeInfo{}
|
||||||
|
network := &netmap.NetMap{}
|
||||||
|
network.SetNodes(nodes)
|
||||||
|
|
||||||
|
netmapClient := &testNetmapClient{
|
||||||
|
netmap: network,
|
||||||
|
}
|
||||||
|
|
||||||
|
params := &alphabet.Params{
|
||||||
|
ParsedWallets: parsedWallets,
|
||||||
|
Log: test.NewLogger(t, true),
|
||||||
|
PoolSize: 2,
|
||||||
|
StorageEmission: emission,
|
||||||
|
IRList: &testIndexer{index: index},
|
||||||
|
AlphabetContracts: alphabetContracts,
|
||||||
|
MorphClient: morphClient,
|
||||||
|
NetmapClient: netmapClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
processor, err := alphabet.New(params)
|
||||||
|
require.NoError(t, err, "failed to create processor instance")
|
||||||
|
|
||||||
|
processor.HandleGasEmission(timers.NewAlphabetEmitTick{})
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
require.EqualValues(t, []invokedMethod{
|
||||||
|
{
|
||||||
|
contract: alphabetContracts[innerring.GlagoliticLetter(index)],
|
||||||
|
fee: 0,
|
||||||
|
method: "emit",
|
||||||
|
},
|
||||||
|
}, morphClient.invokedMethods, "invalid invoked morph methods")
|
||||||
|
|
||||||
|
require.Equal(t, 0, len(morphClient.transferedGas), "invalid transfered Gas")
|
||||||
|
|
||||||
|
require.Equal(t, 0, len(morphClient.batchTransferedGas), "invalid batch transfered Gas")
|
||||||
|
}
|
||||||
|
|
||||||
|
type testIndexer struct {
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *testIndexer) AlphabetIndex() int {
|
||||||
|
return i.index
|
||||||
|
}
|
||||||
|
|
||||||
|
type invokedMethod struct {
|
||||||
|
contract util.Uint160
|
||||||
|
fee fixedn.Fixed8
|
||||||
|
method string
|
||||||
|
args []any
|
||||||
|
}
|
||||||
|
|
||||||
|
type transferGas struct {
|
||||||
|
receiver util.Uint160
|
||||||
|
amount fixedn.Fixed8
|
||||||
|
}
|
||||||
|
|
||||||
|
type batchTransferGas struct {
|
||||||
|
receivers []util.Uint160
|
||||||
|
amount fixedn.Fixed8
|
||||||
|
}
|
||||||
|
|
||||||
|
type testMorphClient struct {
|
||||||
|
invokedMethods []invokedMethod
|
||||||
|
transferedGas []transferGas
|
||||||
|
batchTransferedGas []batchTransferGas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testMorphClient) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) error {
|
||||||
|
c.invokedMethods = append(c.invokedMethods,
|
||||||
|
invokedMethod{
|
||||||
|
contract: contract,
|
||||||
|
fee: fee,
|
||||||
|
method: method,
|
||||||
|
args: args,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (c *testMorphClient) TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error {
|
||||||
|
c.transferedGas = append(c.transferedGas, transferGas{
|
||||||
|
receiver: receiver,
|
||||||
|
amount: amount,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testMorphClient) BatchTransferGas(receivers []util.Uint160, amount fixedn.Fixed8) error {
|
||||||
|
c.batchTransferedGas = append(c.batchTransferedGas, batchTransferGas{
|
||||||
|
receivers: receivers,
|
||||||
|
amount: amount,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type testNetmapClient struct {
|
||||||
|
netmap *netmap.NetMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testNetmapClient) NetMap() (*netmap.NetMap, error) {
|
||||||
|
return c.netmap, nil
|
||||||
|
}
|
|
@ -3,12 +3,13 @@ package alphabet
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
|
||||||
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/panjf2000/ants/v2"
|
"github.com/panjf2000/ants/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -32,14 +33,24 @@ type (
|
||||||
GetByIndex(int) (util.Uint160, bool)
|
GetByIndex(int) (util.Uint160, bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
netmapClient interface {
|
||||||
|
NetMap() (*netmap.NetMap, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
morphClient interface {
|
||||||
|
Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) error
|
||||||
|
TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error
|
||||||
|
BatchTransferGas(receivers []util.Uint160, amount fixedn.Fixed8) error
|
||||||
|
}
|
||||||
|
|
||||||
// Processor of events produced for alphabet contracts in the sidechain.
|
// Processor of events produced for alphabet contracts in the sidechain.
|
||||||
Processor struct {
|
Processor struct {
|
||||||
parsedWallets []util.Uint160
|
parsedWallets []util.Uint160
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
pool *ants.Pool
|
pool *ants.Pool
|
||||||
alphabetContracts Contracts
|
alphabetContracts Contracts
|
||||||
netmapClient *nmClient.Client
|
netmapClient netmapClient
|
||||||
morphClient *client.Client
|
morphClient morphClient
|
||||||
irList Indexer
|
irList Indexer
|
||||||
storageEmission uint64
|
storageEmission uint64
|
||||||
}
|
}
|
||||||
|
@ -50,8 +61,8 @@ type (
|
||||||
Log *logger.Logger
|
Log *logger.Logger
|
||||||
PoolSize int
|
PoolSize int
|
||||||
AlphabetContracts Contracts
|
AlphabetContracts Contracts
|
||||||
NetmapClient *nmClient.Client
|
NetmapClient netmapClient
|
||||||
MorphClient *client.Client
|
MorphClient morphClient
|
||||||
IRList Indexer
|
IRList Indexer
|
||||||
StorageEmission uint64
|
StorageEmission uint64
|
||||||
}
|
}
|
||||||
|
@ -106,3 +117,11 @@ func (ap *Processor) ListenerNotaryParsers() []event.NotaryParserInfo {
|
||||||
func (ap *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo {
|
func (ap *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WaitPoolRunning waits while pool has running tasks
|
||||||
|
// For use in test only.
|
||||||
|
func (ap *Processor) WaitPoolRunning() {
|
||||||
|
for ap.pool.Running() > 0 {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue