[#280] ir: Add alphabet processor unit tests

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
Dmitrii Stepanov 2023-04-26 10:51:30 +03:00 committed by Evgenii Stratonikov
parent 53693071de
commit e89fa7f69f
4 changed files with 316 additions and 15 deletions

View file

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

View file

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

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

View file

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