[#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"
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (a alphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
|
||||
func (a AlphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
|
||||
if ind < 0 || ind >= int(lastLetterNum) {
|
||||
return util.Uint160{}, false
|
||||
}
|
||||
|
@ -120,16 +120,16 @@ func (a alphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
|
|||
return contract, ok
|
||||
}
|
||||
|
||||
func (a alphabetContracts) indexOutOfRange(ind int) bool {
|
||||
func (a AlphabetContracts) indexOutOfRange(ind int) bool {
|
||||
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 {
|
||||
f(letter, contract)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *alphabetContracts) set(l GlagoliticLetter, h util.Uint160) {
|
||||
func (a *AlphabetContracts) set(l GlagoliticLetter, h util.Uint160) {
|
||||
(*a)[l] = h
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ type contracts struct {
|
|||
processing util.Uint160 // in mainnet
|
||||
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) {
|
||||
|
@ -76,9 +76,9 @@ func parseContracts(cfg *viper.Viper, morph nnsResolver, withoutMainNet, without
|
|||
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"))
|
||||
alpha := newAlphabetContracts()
|
||||
alpha := NewAlphabetContracts()
|
||||
|
||||
if 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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/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/panjf2000/ants/v2"
|
||||
"go.uber.org/zap"
|
||||
|
@ -32,14 +33,24 @@ type (
|
|||
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 struct {
|
||||
parsedWallets []util.Uint160
|
||||
log *logger.Logger
|
||||
pool *ants.Pool
|
||||
alphabetContracts Contracts
|
||||
netmapClient *nmClient.Client
|
||||
morphClient *client.Client
|
||||
netmapClient netmapClient
|
||||
morphClient morphClient
|
||||
irList Indexer
|
||||
storageEmission uint64
|
||||
}
|
||||
|
@ -50,8 +61,8 @@ type (
|
|||
Log *logger.Logger
|
||||
PoolSize int
|
||||
AlphabetContracts Contracts
|
||||
NetmapClient *nmClient.Client
|
||||
MorphClient *client.Client
|
||||
NetmapClient netmapClient
|
||||
MorphClient morphClient
|
||||
IRList Indexer
|
||||
StorageEmission uint64
|
||||
}
|
||||
|
@ -106,3 +117,11 @@ func (ap *Processor) ListenerNotaryParsers() []event.NotaryParserInfo {
|
|||
func (ap *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo {
|
||||
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