WIP: IR unit tests #279

Closed
dstepanov-yadro wants to merge 11 commits from dstepanov-yadro/frostfs-node:object-3607 into master
56 changed files with 3080 additions and 225 deletions

View file

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

View file

@ -33,6 +33,10 @@ type (
newEpochHandler func()
containerEstimationStopper interface {
StopEstimation(p container.StopEstimationPrm) error
}
epochTimerArgs struct {
l *logger.Logger
@ -40,8 +44,8 @@ type (
newEpochHandlers []newEpochHandler
cnrWrapper *container.Client // to invoke stop container estimation
epoch epochState // to specify which epoch to stop, and epoch duration
cnrWrapper containerEstimationStopper // to invoke stop container estimation
epoch epochState // to specify which epoch to stop, and epoch duration
stopEstimationDMul uint32 // X: X/Y of epoch in blocks
stopEstimationDDiv uint32 // Y: X/Y of epoch in blocks

View file

@ -0,0 +1,167 @@
package innerring
import (
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
"github.com/stretchr/testify/require"
)
func TestEpochTimer(t *testing.T) {
t.Parallel()
alphaState := &testAlphabetState{isAlphabet: true}
neh := &testNewEpochHandler{}
cnrStopper := &testContainerEstStopper{}
epochState := &testEpochState{
counter: 99,
duration: 10,
}
collectHandler := &testEventHandler{}
distributeHandler := &testEventHandler{}
args := &epochTimerArgs{
l: test.NewLogger(t, true),
alphabetState: alphaState,
newEpochHandlers: []newEpochHandler{neh.Handle},
cnrWrapper: cnrStopper,
epoch: epochState,
stopEstimationDMul: 2,
stopEstimationDDiv: 10,
collectBasicIncome: subEpochEventHandler{
handler: collectHandler.Handle,
durationMul: 3,
durationDiv: 10,
},
distributeBasicIncome: subEpochEventHandler{
handler: distributeHandler.Handle,
durationMul: 4,
durationDiv: 10,
},
}
et := newEpochTimer(args)
err := et.Reset()
require.NoError(t, err, "failed to reset timer")
et.Tick(100)
require.Equal(t, 0, neh.called, "invalid new epoch handler calls")
require.Equal(t, 0, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 0, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 0, distributeHandler.called, "invalid distribute basic income calls")
et.Tick(101)
require.Equal(t, 0, neh.called, "invalid new epoch handler calls")
require.Equal(t, 1, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 0, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 0, distributeHandler.called, "invalid distribute basic income calls")
et.Tick(102)
require.Equal(t, 0, neh.called, "invalid new epoch handler calls")
require.Equal(t, 1, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 1, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 0, distributeHandler.called, "invalid distribute basic income calls")
et.Tick(103)
require.Equal(t, 0, neh.called, "invalid new epoch handler calls")
require.Equal(t, 1, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 1, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 1, distributeHandler.called, "invalid distribute basic income calls")
var h uint32
for h = 104; h < 109; h++ {
et.Tick(h)
require.Equal(t, 0, neh.called, "invalid new epoch handler calls")
require.Equal(t, 1, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 1, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 1, distributeHandler.called, "invalid distribute basic income calls")
}
et.Tick(109)
require.Equal(t, 1, neh.called, "invalid new epoch handler calls")
require.Equal(t, 1, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 1, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 1, distributeHandler.called, "invalid distribute basic income calls")
et.Tick(110)
require.Equal(t, 1, neh.called, "invalid new epoch handler calls")
require.Equal(t, 1, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 1, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 1, distributeHandler.called, "invalid distribute basic income calls")
et.Tick(111)
require.Equal(t, 1, neh.called, "invalid new epoch handler calls")
require.Equal(t, 2, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 1, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 1, distributeHandler.called, "invalid distribute basic income calls")
et.Tick(112)
require.Equal(t, 1, neh.called, "invalid new epoch handler calls")
require.Equal(t, 2, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 2, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 1, distributeHandler.called, "invalid distribute basic income calls")
et.Tick(113)
require.Equal(t, 1, neh.called, "invalid new epoch handler calls")
require.Equal(t, 2, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 2, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 2, distributeHandler.called, "invalid distribute basic income calls")
for h = 114; h < 119; h++ {
et.Tick(h)
require.Equal(t, 1, neh.called, "invalid new epoch handler calls")
require.Equal(t, 2, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 2, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 2, distributeHandler.called, "invalid distribute basic income calls")
}
et.Tick(120)
require.Equal(t, 2, neh.called, "invalid new epoch handler calls")
require.Equal(t, 2, cnrStopper.called, "invalid container stop handler calls")
require.Equal(t, 2, collectHandler.called, "invalid collect basic income calls")
require.Equal(t, 2, distributeHandler.called, "invalid distribute basic income calls")
}
type testAlphabetState struct {
isAlphabet bool
}
func (s *testAlphabetState) IsAlphabet() bool {
return s.isAlphabet
}
type testNewEpochHandler struct {
called int
}
func (h *testNewEpochHandler) Handle() {
h.called++
}
type testContainerEstStopper struct {
called int
}
func (s *testContainerEstStopper) StopEstimation(_ container.StopEstimationPrm) error {
s.called++
return nil
}
type testEpochState struct {
counter uint64
duration uint64
}
func (s *testEpochState) EpochCounter() uint64 {
return s.counter
}
func (s *testEpochState) EpochDuration() uint64 {
return s.duration
}
type testEventHandler struct {
called int
}
func (h *testEventHandler) Handle(e event.Event) {
h.called++
}

View file

@ -0,0 +1,68 @@
package config
import (
"strings"
"testing"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
func TestConfig(t *testing.T) {
t.Parallel()
t.Run("all set", func(t *testing.T) {
t.Parallel()
file := strings.NewReader(
`
fee:
main_chain: 50000000
side_chain: 200000000
named_container_register: 2500000000
`,
)
v := viper.New()
v.SetConfigType("yaml")
err := v.ReadConfig(file)
require.NoError(t, err, "read config file failed")
config := NewFeeConfig(v)
require.Equal(t, fixedn.Fixed8(50000000), config.MainChainFee(), "main chain fee invalid")
require.Equal(t, fixedn.Fixed8(200000000), config.SideChainFee(), "side chain fee invalid")
require.Equal(t, fixedn.Fixed8(2500000000), config.NamedContainerRegistrationFee(), "named container register fee invalid")
})
t.Run("nothing set", func(t *testing.T) {
t.Parallel()
file := strings.NewReader("")
v := viper.New()
v.SetConfigType("yaml")
err := v.ReadConfig(file)
require.NoError(t, err, "read config file failed")
config := NewFeeConfig(v)
require.Equal(t, fixedn.Fixed8(0), config.MainChainFee(), "main chain fee invalid")
require.Equal(t, fixedn.Fixed8(0), config.SideChainFee(), "side chain fee invalid")
require.Equal(t, fixedn.Fixed8(0), config.NamedContainerRegistrationFee(), "named container register fee invalid")
})
t.Run("partially set", func(t *testing.T) {
t.Parallel()
file := strings.NewReader(
`
fee:
main_chain: 10
`,
)
v := viper.New()
v.SetConfigType("yaml")
err := v.ReadConfig(file)
require.NoError(t, err, "read config file failed")
config := NewFeeConfig(v)
require.Equal(t, fixedn.Fixed8(10), config.MainChainFee(), "main chain fee invalid")
require.Equal(t, fixedn.Fixed8(0), config.SideChainFee(), "side chain fee invalid")
require.Equal(t, fixedn.Fixed8(0), config.NamedContainerRegistrationFee(), "named container register fee invalid")
})
}

View file

@ -20,10 +20,10 @@ 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 *client.Client, withoutMainNet, withoutMainNotary, withoutSideNotary bool) (*contracts, error) {
func parseContracts(cfg *viper.Viper, morph nnsResolver, withoutMainNet, withoutMainNotary, withoutSideNotary bool) (*contracts, error) {
var (
result = new(contracts)
err error
@ -78,9 +78,9 @@ func parseContracts(cfg *viper.Viper, morph *client.Client, withoutMainNet, with
return result, nil
}
func parseAlphabetContracts(cfg *viper.Viper, morph *client.Client) (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)
@ -117,7 +117,7 @@ func parseAlphabetContracts(cfg *viper.Viper, morph *client.Client) (alphabetCon
return alpha, nil
}
func parseContract(cfg *viper.Viper, morph *client.Client, cfgName, nnsName string) (res util.Uint160, err error) {
func parseContract(cfg *viper.Viper, morph nnsResolver, cfgName, nnsName string) (res util.Uint160, err error) {
contractStr := cfg.GetString(cfgName)
if len(contractStr) == 0 {
return morph.NNSContractAddress(nnsName)
@ -125,3 +125,7 @@ func parseContract(cfg *viper.Viper, morph *client.Client, cfgName, nnsName stri
return util.Uint160DecodeStringLE(contractStr)
}
type nnsResolver interface {
NNSContractAddress(name string) (sh util.Uint160, err error)
}

View file

@ -0,0 +1,222 @@
package innerring
import (
"strings"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
func TestParseContractsSuccess(t *testing.T) {
t.Parallel()
file := strings.NewReader(`
contracts:
frostfs: ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62
processing: 597f5894867113a41e192801709c02497f611de8
audit: 219e37aed2180b87e7fe945dbf97d67125e8d73f
balance: d2aa48d14b17b11bc4c68205027884a96706dd16
container: ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6
frostfsid: 9f5866decbc751a099e74c7c7bc89f609201755a
netmap: 83c600c81d47a1b1b7cf58eb49ae7ee7240dc742
proxy: abc8794bb40a21f2db5f21ae62741eb46c8cad1c
alphabet:
amount: 2
az: c1d211fceeb4b1dc76b8e4054d11fdf887e418ea
buky: e2ba789320899658b100f331bdebb74474757920
`)
v := viper.New()
v.SetConfigType("yaml")
err := v.ReadConfig(file)
require.NoError(t, err, "read config file failed")
t.Run("all enabled", func(t *testing.T) {
t.Parallel()
c, err := parseContracts(v, nil, false, false, false)
require.NoError(t, err, "failed to parse contracts")
frostfsExp, _ := util.Uint160DecodeStringLE("ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62")
require.Equal(t, frostfsExp, c.frostfs, "invalid frostfs")
processingExp, _ := util.Uint160DecodeStringLE("597f5894867113a41e192801709c02497f611de8")
require.Equal(t, processingExp, c.processing, "invalid processing")
auditExp, _ := util.Uint160DecodeStringLE("219e37aed2180b87e7fe945dbf97d67125e8d73f")
require.Equal(t, auditExp, c.audit, "invalid audit")
balanceExp, _ := util.Uint160DecodeStringLE("d2aa48d14b17b11bc4c68205027884a96706dd16")
require.Equal(t, balanceExp, c.balance, "invalid balance")
containerExp, _ := util.Uint160DecodeStringLE("ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6")
require.Equal(t, containerExp, c.container, "invalid container")
frostfsIDExp, _ := util.Uint160DecodeStringLE("9f5866decbc751a099e74c7c7bc89f609201755a")
require.Equal(t, frostfsIDExp, c.frostfsID, "invalid frostfsID")
netmapIDExp, _ := util.Uint160DecodeStringLE("83c600c81d47a1b1b7cf58eb49ae7ee7240dc742")
require.Equal(t, netmapIDExp, c.netmap, "invalid netmap")
proxyExp, _ := util.Uint160DecodeStringLE("abc8794bb40a21f2db5f21ae62741eb46c8cad1c")
require.Equal(t, proxyExp, c.proxy, "invalid proxy")
require.Equal(t, 2, len(c.alphabet), "invalid alphabet contracts length")
azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
require.Equal(t, azExp, c.alphabet[az], "invalid az")
bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")
require.Equal(t, bukyExp, c.alphabet[buky], "invalid buky")
})
t.Run("all disabled", func(t *testing.T) {
t.Parallel()
c, err := parseContracts(v, nil, true, true, true)
require.NoError(t, err, "failed to parse contracts")
require.Equal(t, util.Uint160{}, c.frostfs, "invalid frostfs")
require.Equal(t, util.Uint160{}, c.processing, "invalid processing")
auditExp, _ := util.Uint160DecodeStringLE("219e37aed2180b87e7fe945dbf97d67125e8d73f")
require.Equal(t, auditExp, c.audit, "invalid audit")
balanceExp, _ := util.Uint160DecodeStringLE("d2aa48d14b17b11bc4c68205027884a96706dd16")
require.Equal(t, balanceExp, c.balance, "invalid balance")
containerExp, _ := util.Uint160DecodeStringLE("ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6")
require.Equal(t, containerExp, c.container, "invalid container")
frostfsIDExp, _ := util.Uint160DecodeStringLE("9f5866decbc751a099e74c7c7bc89f609201755a")
require.Equal(t, frostfsIDExp, c.frostfsID, "invalid frostfsID")
netmapIDExp, _ := util.Uint160DecodeStringLE("83c600c81d47a1b1b7cf58eb49ae7ee7240dc742")
require.Equal(t, netmapIDExp, c.netmap, "invalid netmap")
require.Equal(t, util.Uint160{}, c.proxy, "invalid proxy")
require.Equal(t, 2, len(c.alphabet), "invalid alphabet contracts length")
azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
require.Equal(t, azExp, c.alphabet[az], "invalid az")
bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")
require.Equal(t, bukyExp, c.alphabet[buky], "invalid buky")
})
t.Run("main notary & side notary disabled", func(t *testing.T) {
t.Parallel()
c, err := parseContracts(v, nil, false, true, true)
require.NoError(t, err, "failed to parse contracts")
frostfsExp, _ := util.Uint160DecodeStringLE("ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62")
require.Equal(t, frostfsExp, c.frostfs, "invalid frostfs")
require.Equal(t, util.Uint160{}, c.processing, "invalid processing")
auditExp, _ := util.Uint160DecodeStringLE("219e37aed2180b87e7fe945dbf97d67125e8d73f")
require.Equal(t, auditExp, c.audit, "invalid audit")
balanceExp, _ := util.Uint160DecodeStringLE("d2aa48d14b17b11bc4c68205027884a96706dd16")
require.Equal(t, balanceExp, c.balance, "invalid balance")
containerExp, _ := util.Uint160DecodeStringLE("ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6")
require.Equal(t, containerExp, c.container, "invalid container")
frostfsIDExp, _ := util.Uint160DecodeStringLE("9f5866decbc751a099e74c7c7bc89f609201755a")
require.Equal(t, frostfsIDExp, c.frostfsID, "invalid frostfsID")
netmapIDExp, _ := util.Uint160DecodeStringLE("83c600c81d47a1b1b7cf58eb49ae7ee7240dc742")
require.Equal(t, netmapIDExp, c.netmap, "invalid netmap")
require.Equal(t, util.Uint160{}, c.proxy, "invalid proxy")
require.Equal(t, 2, len(c.alphabet), "invalid alphabet contracts length")
azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
require.Equal(t, azExp, c.alphabet[az], "invalid az")
bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")
require.Equal(t, bukyExp, c.alphabet[buky], "invalid buky")
})
}
func TestParseContractsInvalid(t *testing.T) {
t.Parallel()
t.Run("invalid audit contract", func(t *testing.T) {
t.Parallel()
file := strings.NewReader(`
contracts:
frostfs: invalid_data
processing: 597f5894867113a41e192801709c02497f611de8
audit: 219e37aed2180b87e7fe945dbf97d67125e8d73f
balance: d2aa48d14b17b11bc4c68205027884a96706dd16
container: ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6
frostfsid: 9f5866decbc751a099e74c7c7bc89f609201755a
netmap: 83c600c81d47a1b1b7cf58eb49ae7ee7240dc742
proxy: abc8794bb40a21f2db5f21ae62741eb46c8cad1c
alphabet:
amount: 2
az: c1d211fceeb4b1dc76b8e4054d11fdf887e418ea
buky: e2ba789320899658b100f331bdebb74474757920
`)
v := viper.New()
v.SetConfigType("yaml")
err := v.ReadConfig(file)
require.NoError(t, err, "read config file failed")
_, err = parseContracts(v, nil, false, false, false)
require.Error(t, err, "unexpected success")
})
t.Run("invalid alphabet count", func(t *testing.T) {
t.Parallel()
file := strings.NewReader(`
contracts:
frostfs: ee3dee6d05dc79c24a5b8f6985e10d68b7cacc62
processing: 597f5894867113a41e192801709c02497f611de8
audit: 219e37aed2180b87e7fe945dbf97d67125e8d73f
balance: d2aa48d14b17b11bc4c68205027884a96706dd16
container: ed4a7a66fe3f9bfe50f214b49be8f215a3c886b6
frostfsid: 9f5866decbc751a099e74c7c7bc89f609201755a
netmap: 83c600c81d47a1b1b7cf58eb49ae7ee7240dc742
proxy: abc8794bb40a21f2db5f21ae62741eb46c8cad1c
alphabet:
amount: 3
az: c1d211fceeb4b1dc76b8e4054d11fdf887e418ea
buky: e2ba789320899658b100f331bdebb74474757920
`)
v := viper.New()
v.SetConfigType("yaml")
err := v.ReadConfig(file)
require.NoError(t, err, "read config file failed")
azExp, _ := util.Uint160DecodeStringLE("c1d211fceeb4b1dc76b8e4054d11fdf887e418ea")
bukyExp, _ := util.Uint160DecodeStringLE("e2ba789320899658b100f331bdebb74474757920")
morph := &testParserMorph{
values: map[string]util.Uint160{
"az": azExp,
"buky": bukyExp,
},
}
_, err = parseContracts(v, morph, false, false, false)
require.ErrorContains(t, err, "could not read all contracts: required 3, read 2", "unexpected success")
})
}
type testParserMorph struct {
values map[string]util.Uint160
}
func (m *testParserMorph) NNSContractAddress(name string) (sh util.Uint160, err error) {
if value, found := m.values[name]; found {
return value, nil
}
return util.Uint160{}, client.ErrNNSRecordNotFound
}

View file

@ -0,0 +1,225 @@
package innerring
import (
"fmt"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
"go.uber.org/atomic"
)
func TestIndexerReturnsIndexes(t *testing.T) {
t.Parallel()
commiteeKeys, err := keys.NewPublicKeysFromStrings([]string{
"03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae",
"022bb4041c50d607ff871dec7e4cd7778388e0ea6849d84ccbd9aa8f32e16a8131",
})
require.NoError(t, err, "convert string to commitee public keys failed")
cf := &testCommiteeFetcher{
keys: commiteeKeys,
}
irKeys, err := keys.NewPublicKeysFromStrings([]string{
"038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35",
"02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3",
"022bb4041c50d607ff871dec7e4cd7778388e0ea6849d84ccbd9aa8f32e16a8131",
})
require.NoError(t, err, "convert string to IR public keys failed")
irf := &testIRFetcher{
keys: irKeys,
}
t.Run("success", func(t *testing.T) {
t.Parallel()
key := irKeys[2]
indexer := newInnerRingIndexer(cf, irf, key, time.Second)
idx, err := indexer.AlphabetIndex()
require.NoError(t, err, "failed to get alphabet index")
require.Equal(t, int32(1), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.NoError(t, err, "failed to get IR index")
require.Equal(t, int32(2), idx, "invalid IR index")
size, err := indexer.InnerRingSize()
require.NoError(t, err, "failed to get IR size")
require.Equal(t, int32(3), size, "invalid IR size")
})
t.Run("not found alphabet", func(t *testing.T) {
t.Parallel()
key := irKeys[0]
indexer := newInnerRingIndexer(cf, irf, key, time.Second)
idx, err := indexer.AlphabetIndex()
require.NoError(t, err, "failed to get alphabet index")
require.Equal(t, int32(-1), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.NoError(t, err, "failed to get IR index")
require.Equal(t, int32(0), idx, "invalid IR index")
})
t.Run("not found IR", func(t *testing.T) {
t.Parallel()
key := commiteeKeys[0]
indexer := newInnerRingIndexer(cf, irf, key, time.Second)
idx, err := indexer.AlphabetIndex()
require.NoError(t, err, "failed to get alphabet index")
require.Equal(t, int32(0), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.NoError(t, err, "failed to get IR index")
require.Equal(t, int32(-1), idx, "invalid IR index")
})
}
func TestIndexerCachesIndexes(t *testing.T) {
t.Parallel()
commiteeKeys, err := keys.NewPublicKeysFromStrings([]string{})
require.NoError(t, err, "convert string to commitee public keys failed")
cf := &testCommiteeFetcher{
keys: commiteeKeys,
}
irKeys, err := keys.NewPublicKeysFromStrings([]string{})
require.NoError(t, err, "convert string to IR public keys failed")
irf := &testIRFetcher{
keys: irKeys,
}
key, err := keys.NewPublicKeyFromString("022bb4041c50d607ff871dec7e4cd7778388e0ea6849d84ccbd9aa8f32e16a8131")
require.NoError(t, err, "convert string to public key failed")
indexer := newInnerRingIndexer(cf, irf, key, time.Second)
idx, err := indexer.AlphabetIndex()
require.NoError(t, err, "failed to get alphabet index")
require.Equal(t, int32(-1), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.NoError(t, err, "failed to get IR index")
require.Equal(t, int32(-1), idx, "invalid IR index")
size, err := indexer.InnerRingSize()
require.NoError(t, err, "failed to get IR size")
require.Equal(t, int32(0), size, "invalid IR size")
require.Equal(t, int32(1), cf.calls.Load(), "invalid commitee calls count")
require.Equal(t, int32(1), irf.calls.Load(), "invalid IR calls count")
idx, err = indexer.AlphabetIndex()
require.NoError(t, err, "failed to get alphabet index")
require.Equal(t, int32(-1), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.NoError(t, err, "failed to get IR index")
require.Equal(t, int32(-1), idx, "invalid IR index")
size, err = indexer.InnerRingSize()
require.NoError(t, err, "failed to get IR size")
require.Equal(t, int32(0), size, "invalid IR size")
require.Equal(t, int32(1), cf.calls.Load(), "invalid commitee calls count")
require.Equal(t, int32(1), irf.calls.Load(), "invalid IR calls count")
time.Sleep(2 * time.Second)
idx, err = indexer.AlphabetIndex()
require.NoError(t, err, "failed to get alphabet index")
require.Equal(t, int32(-1), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.NoError(t, err, "failed to get IR index")
require.Equal(t, int32(-1), idx, "invalid IR index")
size, err = indexer.InnerRingSize()
require.NoError(t, err, "failed to get IR size")
require.Equal(t, int32(0), size, "invalid IR size")
require.Equal(t, int32(2), cf.calls.Load(), "invalid commitee calls count")
require.Equal(t, int32(2), irf.calls.Load(), "invalid IR calls count")
}
func TestIndexerThrowsErrors(t *testing.T) {
t.Parallel()
cf := &testCommiteeFetcher{
err: fmt.Errorf("test commitee error"),
}
irKeys, err := keys.NewPublicKeysFromStrings([]string{})
require.NoError(t, err, "convert string to IR public keys failed")
irf := &testIRFetcher{
keys: irKeys,
}
key, err := keys.NewPublicKeyFromString("022bb4041c50d607ff871dec7e4cd7778388e0ea6849d84ccbd9aa8f32e16a8131")
require.NoError(t, err, "convert string to public key failed")
indexer := newInnerRingIndexer(cf, irf, key, time.Second)
idx, err := indexer.AlphabetIndex()
require.ErrorContains(t, err, "test commitee error", "error from commitee not throwed")
require.Equal(t, int32(0), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.ErrorContains(t, err, "test commitee error", "error from IR not throwed")
require.Equal(t, int32(0), idx, "invalid IR index")
size, err := indexer.InnerRingSize()
require.ErrorContains(t, err, "test commitee error", "error from IR not throwed")
require.Equal(t, int32(0), size, "invalid IR size")
commiteeKeys, err := keys.NewPublicKeysFromStrings([]string{})
require.NoError(t, err, "convert string to commitee public keys failed")
cf = &testCommiteeFetcher{
keys: commiteeKeys,
}
irf = &testIRFetcher{
err: fmt.Errorf("test IR error"),
}
indexer = newInnerRingIndexer(cf, irf, key, time.Second)
idx, err = indexer.AlphabetIndex()
require.ErrorContains(t, err, "test IR error", "error from commitee not throwed")
require.Equal(t, int32(0), idx, "invalid alphabet index")
idx, err = indexer.InnerRingIndex()
require.ErrorContains(t, err, "test IR error", "error from IR not throwed")
require.Equal(t, int32(0), idx, "invalid IR index")
size, err = indexer.InnerRingSize()
require.ErrorContains(t, err, "test IR error", "error from IR not throwed")
require.Equal(t, int32(0), size, "invalid IR size")
}
type testCommiteeFetcher struct {
keys keys.PublicKeys
err error
calls atomic.Int32
}
func (f *testCommiteeFetcher) Committee() (keys.PublicKeys, error) {
f.calls.Inc()
return f.keys, f.err
}
type testIRFetcher struct {
keys keys.PublicKeys
err error
calls atomic.Int32
}
func (f *testIRFetcher) InnerRingKeys() (keys.PublicKeys, error) {
f.calls.Inc()
return f.keys, f.err
}

View file

@ -63,7 +63,7 @@ func (s *Server) initNetmapProcessor(cfg *viper.Viper,
s.netmapProcessor, err = netmap.New(&netmap.Params{
Log: s.log,
PoolSize: cfg.GetInt("workers.netmap"),
NetmapClient: s.netmapClient,
NetmapClient: netmap.NewNetmapClient(s.netmapClient),
EpochTimer: s,
EpochState: s,
AlphabetState: s,

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 (
"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)
}
}

View file

@ -0,0 +1,90 @@
package balance
import (
"testing"
"time"
frostfscontract "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfs"
balanceEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/balance"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestProcessorCallsFrostFSContractForLockEvent(t *testing.T) {
t.Parallel()
as := &testAlphabetState{
isAlphabet: true,
}
conv := &testPresicionConverter{}
cl := &testFrostFSContractClient{}
bsc := util.Uint160{100}
processor, err := New(&Params{
Log: test.NewLogger(t, true),
PoolSize: 2,
FrostFSClient: cl,
BalanceSC: bsc,
AlphabetState: as,
Converter: conv,
})
require.NoError(t, err, "failed to create processor")
processor.handleLock(balanceEvent.Lock{})
for processor.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.Equal(t, 1, cl.chequeCalls, "invalid Cheque calls")
}
func TestProcessorDoesntCallFrostFSContractIfNotAlphabet(t *testing.T) {
t.Parallel()
as := &testAlphabetState{}
conv := &testPresicionConverter{}
cl := &testFrostFSContractClient{}
bsc := util.Uint160{100}
processor, err := New(&Params{
Log: test.NewLogger(t, true),
PoolSize: 2,
FrostFSClient: cl,
BalanceSC: bsc,
AlphabetState: as,
Converter: conv,
})
require.NoError(t, err, "failed to create processor")
processor.handleLock(balanceEvent.Lock{})
for processor.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.Equal(t, 0, cl.chequeCalls, "invalid Cheque calls")
}
type testAlphabetState struct {
isAlphabet bool
}
func (s *testAlphabetState) IsAlphabet() bool {
return s.isAlphabet
}
type testPresicionConverter struct {
}
func (c *testPresicionConverter) ToFixed8(v int64) int64 {
return v
}
type testFrostFSContractClient struct {
chequeCalls int
}
func (c *testFrostFSContractClient) Cheque(p frostfscontract.ChequePrm) error {
c.chequeCalls++
return nil
}

View file

@ -25,11 +25,15 @@ type (
ToFixed8(int64) int64
}
FrostFSClient interface {
Cheque(p frostfscontract.ChequePrm) error
}
// Processor of events produced by balance contract in the morphchain.
Processor struct {
log *logger.Logger
pool *ants.Pool
frostfsClient *frostfscontract.Client
frostfsClient FrostFSClient
balanceSC util.Uint160
alphabetState AlphabetState
converter PrecisionConverter
@ -39,7 +43,7 @@ type (
Params struct {
Log *logger.Logger
PoolSize int
FrostFSClient *frostfscontract.Client
FrostFSClient FrostFSClient
BalanceSC util.Uint160
AlphabetState AlphabetState
Converter PrecisionConverter

View file

@ -36,7 +36,7 @@ func (cp *Processor) handleDelete(ev event.Event) {
// send an event to the worker pool
err := cp.pool.Submit(func() { cp.processContainerDelete(&del) })
err := cp.pool.Submit(func() { cp.processContainerDelete(del) })
if err != nil {
// there system can be moved into controlled degradation stage
cp.log.Warn(logs.ContainerContainerProcessorWorkerPoolDrained,

View file

@ -0,0 +1,337 @@
package container
import (
"crypto/ecdsa"
"encoding/hex"
"testing"
"time"
containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestPutEvent(t *testing.T) {
t.Parallel()
nst := &testNetworkState{
homHashDisabled: true,
epoch: 100,
}
cc := &testContainerClient{
get: make(map[string]*containercore.Container),
}
proc, err := New(&Params{
Log: test.NewLogger(t, true),
PoolSize: 2,
AlphabetState: &testAlphabetState{isAlphabet: true},
FrostFSIDClient: &testIDClient{},
NotaryDisabled: true,
NetworkState: nst,
ContainerClient: cc,
})
require.NoError(t, err, "failed to create processor")
p, err := keys.NewPrivateKey()
require.NoError(t, err)
var usr user.ID
user.IDFromKey(&usr, (ecdsa.PublicKey)(*p.PublicKey()))
var pp netmap.PlacementPolicy
pp.AddReplicas(netmap.ReplicaDescriptor{})
var cnr containerSDK.Container
cnr.Init()
cnr.SetOwner(usr)
cnr.SetPlacementPolicy(pp)
cnr.SetBasicACL(acl.Private)
containerSDK.DisableHomomorphicHashing(&cnr)
event := &testPutEvent{
cnr: &cnr,
pk: p,
st: nil,
}
proc.handlePut(event)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var expectedPut cntClient.PutPrm
expectedPut.SetContainer(cnr.Marshal())
expectedPut.SetKey(p.PublicKey().Bytes())
expectedPut.SetSignature(p.Sign(cnr.Marshal()))
expectedPut.SetZone("container")
require.EqualValues(t, []cntClient.PutPrm{expectedPut}, cc.put, "invalid put requests")
}
func TestDeleteEvent(t *testing.T) {
t.Parallel()
nst := &testNetworkState{
homHashDisabled: true,
epoch: 100,
}
cc := &testContainerClient{
get: make(map[string]*containercore.Container),
}
p, err := keys.NewPrivateKey()
require.NoError(t, err)
idc := &testIDClient{
publicKeys: []*keys.PublicKey{
p.PublicKey(),
},
}
proc, err := New(&Params{
Log: test.NewLogger(t, true),
PoolSize: 2,
AlphabetState: &testAlphabetState{isAlphabet: true},
FrostFSIDClient: idc,
NotaryDisabled: true,
NetworkState: nst,
ContainerClient: cc,
})
require.NoError(t, err, "failed to create processor")
var usr user.ID
user.IDFromKey(&usr, (ecdsa.PublicKey)(*p.PublicKey()))
var pp netmap.PlacementPolicy
pp.AddReplicas(netmap.ReplicaDescriptor{})
var cnr containerSDK.Container
cnr.Init()
cnr.SetOwner(usr)
cnr.SetPlacementPolicy(pp)
cnr.SetBasicACL(acl.Private)
containerSDK.DisableHomomorphicHashing(&cnr)
var cid cid.ID
containerSDK.CalculateID(&cid, cnr)
cidBin := make([]byte, 32)
cid.Encode(cidBin)
ev := containerEvent.Delete{
ContainerIDValue: cidBin,
SignatureValue: p.Sign(cidBin),
}
var signature frostfscrypto.Signature
signer := frostfsecdsa.Signer(p.PrivateKey)
require.NoError(t, signature.Calculate(signer, ev.ContainerID()), "failed to calculate signature")
cc.get[hex.EncodeToString(ev.ContainerID())] = &containercore.Container{
Value: cnr,
Signature: signature,
}
proc.handleDelete(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var expectedDelete cntClient.DeletePrm
expectedDelete.SetCID(ev.ContainerID())
expectedDelete.SetSignature(ev.Signature())
require.EqualValues(t, []cntClient.DeletePrm{expectedDelete}, cc.delete, "invalid delete requests")
}
func TestSetEACLEvent(t *testing.T) {
t.Parallel()
nst := &testNetworkState{
homHashDisabled: true,
epoch: 100,
}
cc := &testContainerClient{
get: make(map[string]*containercore.Container),
}
proc, err := New(&Params{
Log: test.NewLogger(t, true),
PoolSize: 2,
AlphabetState: &testAlphabetState{isAlphabet: true},
FrostFSIDClient: &testIDClient{},
NotaryDisabled: true,
NetworkState: nst,
ContainerClient: cc,
})
require.NoError(t, err, "failed to create processor")
p, err := keys.NewPrivateKey()
require.NoError(t, err)
var usr user.ID
user.IDFromKey(&usr, (ecdsa.PublicKey)(*p.PublicKey()))
var pp netmap.PlacementPolicy
pp.AddReplicas(netmap.ReplicaDescriptor{})
var cnr containerSDK.Container
cnr.Init()
cnr.SetOwner(usr)
cnr.SetPlacementPolicy(pp)
cnr.SetBasicACL(acl.PrivateExtended)
containerSDK.DisableHomomorphicHashing(&cnr)
var cid cid.ID
containerSDK.CalculateID(&cid, cnr)
cidBytes := make([]byte, 32)
cid.Encode(cidBytes)
var signature frostfscrypto.Signature
signer := frostfsecdsa.Signer(p.PrivateKey)
require.NoError(t, signature.Calculate(signer, cidBytes), "failed to calculate signature")
cc.get[hex.EncodeToString(cidBytes)] = &containercore.Container{
Value: cnr,
Signature: signature,
}
table := eacl.NewTable()
table.SetCID(cid)
table.SetVersion(version.Current())
r := &eacl.Record{}
r.AddObjectContainerIDFilter(eacl.MatchStringEqual, cid)
table.AddRecord(r)
event := containerEvent.SetEACL{
TableValue: table.ToV2().StableMarshal(nil),
PublicKeyValue: p.PublicKey().Bytes(),
SignatureValue: p.Sign(table.ToV2().StableMarshal(nil)),
}
proc.handleSetEACL(event)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var expectedPutEACL cntClient.PutEACLPrm
expectedPutEACL.SetTable(table.ToV2().StableMarshal(nil))
expectedPutEACL.SetKey(p.PublicKey().Bytes())
expectedPutEACL.SetSignature(p.Sign(table.ToV2().StableMarshal(nil)))
require.EqualValues(t, []cntClient.PutEACLPrm{expectedPutEACL}, cc.putEACL, "invalid set EACL requests")
}
type testAlphabetState struct {
isAlphabet bool
}
func (s *testAlphabetState) IsAlphabet() bool {
return s.isAlphabet
}
type testNetworkState struct {
homHashDisabled bool
epoch uint64
}
func (s *testNetworkState) HomomorphicHashDisabled() (bool, error) {
return s.homHashDisabled, nil
}
func (s *testNetworkState) Epoch() (uint64, error) {
return s.epoch, nil
}
type testContainerClient struct {
contractAddress util.Uint160
put []cntClient.PutPrm
get map[string]*containercore.Container
delete []cntClient.DeletePrm
putEACL []cntClient.PutEACLPrm
}
func (c *testContainerClient) ContractAddress() util.Uint160 {
return c.contractAddress
}
func (c *testContainerClient) Morph() *client.Client {
return nil
}
func (c *testContainerClient) Put(p cntClient.PutPrm) error {
c.put = append(c.put, p)
return nil
}
func (c *testContainerClient) Get(cid []byte) (*containercore.Container, error) {
key := hex.EncodeToString(cid)
if cont, found := c.get[key]; found {
return cont, nil
}
return nil, apistatus.ContainerNotFound{}
}
func (c *testContainerClient) Delete(p cntClient.DeletePrm) error {
c.delete = append(c.delete, p)
return nil
}
func (c *testContainerClient) PutEACL(p cntClient.PutEACLPrm) error {
c.putEACL = append(c.putEACL, p)
return nil
}
type testIDClient struct {
publicKeys keys.PublicKeys
}
func (c *testIDClient) AccountKeys(p frostfsid.AccountKeysPrm) (keys.PublicKeys, error) {
return c.publicKeys, nil
}
var _ putEvent = &testPutEvent{}
type testPutEvent struct {
cnr *containerSDK.Container
pk *keys.PrivateKey
st []byte
}
func (e *testPutEvent) MorphEvent() {}
func (e *testPutEvent) Container() []byte {
return e.cnr.Marshal()
}
func (e *testPutEvent) PublicKey() []byte {
return e.pk.PublicKey().Bytes()
}
func (e *testPutEvent) Signature() []byte {
return e.pk.Sign(e.cnr.Marshal())
}
func (e *testPutEvent) SessionToken() []byte {
return e.st
}
func (e *testPutEvent) NotaryRequest() *payload.P2PNotaryRequest {
return nil
}

View file

@ -120,7 +120,7 @@ func (cp *Processor) approvePutContainer(ctx *putContainerContext) {
// Process delete container operation from the user by checking container sanity
// and sending approve tx back to morph.
func (cp *Processor) processContainerDelete(e *containerEvent.Delete) {
func (cp *Processor) processContainerDelete(e containerEvent.Delete) {
if !cp.alphabetState.IsAlphabet() {
cp.log.Info(logs.ContainerNonAlphabetModeIgnoreContainerDelete)
return
@ -138,7 +138,7 @@ func (cp *Processor) processContainerDelete(e *containerEvent.Delete) {
cp.approveDeleteContainer(e)
}
func (cp *Processor) checkDeleteContainer(e *containerEvent.Delete) error {
func (cp *Processor) checkDeleteContainer(e containerEvent.Delete) error {
binCnr := e.ContainerID()
var idCnr cid.ID
@ -170,7 +170,7 @@ func (cp *Processor) checkDeleteContainer(e *containerEvent.Delete) error {
return nil
}
func (cp *Processor) approveDeleteContainer(e *containerEvent.Delete) {
func (cp *Processor) approveDeleteContainer(e containerEvent.Delete) {
var err error
prm := cntClient.DeletePrm{}

View file

@ -6,13 +6,13 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"go.uber.org/zap"
)
func (cp *Processor) processSetEACL(e container.SetEACL) {
func (cp *Processor) processSetEACL(e containerEvent.SetEACL) {
if !cp.alphabetState.IsAlphabet() {
cp.log.Info(logs.ContainerNonAlphabetModeIgnoreSetEACL)
return
@ -30,7 +30,7 @@ func (cp *Processor) processSetEACL(e container.SetEACL) {
cp.approveSetEACL(e)
}
func (cp *Processor) checkSetEACL(e container.SetEACL) error {
func (cp *Processor) checkSetEACL(e containerEvent.SetEACL) error {
binTable := e.Table()
// unmarshal table
@ -74,7 +74,7 @@ func (cp *Processor) checkSetEACL(e container.SetEACL) error {
return nil
}
func (cp *Processor) approveSetEACL(e container.SetEACL) {
func (cp *Processor) approveSetEACL(e containerEvent.SetEACL) {
var err error
prm := cntClient.PutEACLPrm{}

View file

@ -5,12 +5,16 @@ import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/panjf2000/ants/v2"
"go.uber.org/zap"
)
@ -21,13 +25,26 @@ type (
IsAlphabet() bool
}
ContClient interface {
ContractAddress() util.Uint160
Morph() *client.Client
Put(p cntClient.PutPrm) error
Get(cid []byte) (*containercore.Container, error)
Delete(p cntClient.DeletePrm) error
PutEACL(p cntClient.PutEACLPrm) error
}
IDClient interface {
AccountKeys(p frostfsid.AccountKeysPrm) (keys.PublicKeys, error)
}
// Processor of events produced by container contract in the sidechain.
Processor struct {
log *logger.Logger
pool *ants.Pool
alphabetState AlphabetState
cnrClient *container.Client // notary must be enabled
idClient *frostfsid.Client
cnrClient ContClient // notary must be enabled
idClient IDClient
netState NetworkState
notaryDisabled bool
}
@ -37,8 +54,8 @@ type (
Log *logger.Logger
PoolSize int
AlphabetState AlphabetState
ContainerClient *container.Client
FrostFSIDClient *frostfsid.Client
ContainerClient ContClient
FrostFSIDClient IDClient
NetworkState NetworkState
NotaryDisabled bool
}

View file

@ -18,7 +18,7 @@ func (np *Processor) handleDeposit(ev event.Event) {
// send event to the worker pool
err := np.pool.Submit(func() { np.processDeposit(&deposit) })
err := np.pool.Submit(func() { np.processDeposit(deposit) })
if err != nil {
// there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -34,7 +34,7 @@ func (np *Processor) handleWithdraw(ev event.Event) {
// send event to the worker pool
err := np.pool.Submit(func() { np.processWithdraw(&withdraw) })
err := np.pool.Submit(func() { np.processWithdraw(withdraw) })
if err != nil {
// there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -50,7 +50,7 @@ func (np *Processor) handleCheque(ev event.Event) {
// send event to the worker pool
err := np.pool.Submit(func() { np.processCheque(&cheque) })
err := np.pool.Submit(func() { np.processCheque(cheque) })
if err != nil {
// there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -67,7 +67,7 @@ func (np *Processor) handleConfig(ev event.Event) {
// send event to the worker pool
err := np.pool.Submit(func() { np.processConfig(&cfg) })
err := np.pool.Submit(func() { np.processConfig(cfg) })
if err != nil {
// there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -83,7 +83,7 @@ func (np *Processor) handleBind(ev event.Event) {
// send event to the worker pool
err := np.pool.Submit(func() { np.processBind(e) })
err := np.pool.Submit(func() { np.processBind(e, true) })
if err != nil {
// there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -99,7 +99,7 @@ func (np *Processor) handleUnbind(ev event.Event) {
// send event to the worker pool
err := np.pool.Submit(func() { np.processBind(e) })
err := np.pool.Submit(func() { np.processBind(e, false) })
if err != nil {
// there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,

View file

@ -0,0 +1,371 @@
package frostfs
import (
"testing"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/balance"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
frostfsEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"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 TestHandleDeposit(t *testing.T) {
t.Parallel()
es := &testEpochState{
epochCounter: 100,
}
b := &testBalaceClient{}
m := &testMorphClient{
balance: 150,
}
proc, err := newTestProc(t, func(p *Params) {
p.EpochState = es
p.BalanceClient = b
p.MorphClient = m
})
require.NoError(t, err, "failed to create processor")
ev := frostfsEvent.Deposit{
IDValue: []byte{1, 2, 3, 4, 5},
FromValue: util.Uint160{100},
ToValue: util.Uint160{200},
AmountValue: 1000,
}
proc.handleDeposit(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var expMint balance.MintPrm
expMint.SetAmount(ev.AmountValue)
expMint.SetID(ev.IDValue)
expMint.SetTo(ev.ToValue)
require.EqualValues(t, []balance.MintPrm{expMint}, b.mint, "invalid mint value")
require.EqualValues(t, []transferGas{
{
receiver: ev.ToValue,
amount: fixedn.Fixed8(50),
},
}, m.transferGas, "invalid transfer gas")
es.epochCounter = 109
proc.handleDeposit(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
expMint.SetAmount(ev.AmountValue)
expMint.SetID(ev.IDValue)
expMint.SetTo(ev.ToValue)
require.EqualValues(t, []balance.MintPrm{expMint, expMint}, b.mint, "invalid mint value")
require.EqualValues(t, []transferGas{
{
receiver: ev.ToValue,
amount: fixedn.Fixed8(50),
},
}, m.transferGas, "invalid transfer gas")
}
func TestHandleWithdraw(t *testing.T) {
t.Parallel()
es := &testEpochState{
epochCounter: 100,
}
b := &testBalaceClient{}
m := &testMorphClient{
balance: 150,
}
proc, err := newTestProc(t, func(p *Params) {
p.EpochState = es
p.BalanceClient = b
p.MorphClient = m
})
require.NoError(t, err, "failed to create processor")
ev := frostfsEvent.Withdraw{
IDValue: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
UserValue: util.Uint160{100},
AmountValue: 1000,
}
proc.handleWithdraw(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
lock, err := util.Uint160DecodeBytesBE(ev.ID()[:util.Uint160Size])
require.NoError(t, err, "failed to decode ID")
var expLock balance.LockPrm
expLock.SetAmount(ev.AmountValue)
expLock.SetID(ev.IDValue)
expLock.SetDueEpoch(int64(es.epochCounter) + int64(lockAccountLifetime))
expLock.SetLock(lock)
expLock.SetUser(ev.UserValue)
require.EqualValues(t, []balance.LockPrm{expLock}, b.lock, "invalid lock value")
}
func TestHandleCheque(t *testing.T) {
t.Parallel()
es := &testEpochState{
epochCounter: 100,
}
b := &testBalaceClient{}
m := &testMorphClient{
balance: 150,
}
proc, err := newTestProc(t, func(p *Params) {
p.BalanceClient = b
p.MorphClient = m
p.EpochState = es
})
require.NoError(t, err, "failed to create processor")
ev := frostfsEvent.Cheque{
IDValue: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
UserValue: util.Uint160{100},
AmountValue: 1000,
LockValue: util.Uint160{200},
}
proc.handleCheque(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var expBurn balance.BurnPrm
expBurn.SetAmount(ev.AmountValue)
expBurn.SetID(ev.IDValue)
expBurn.SetTo(util.Uint160{200})
require.EqualValues(t, []balance.BurnPrm{expBurn}, b.burn, "invalid burn value")
}
func TestHandleConfig(t *testing.T) {
t.Parallel()
es := &testEpochState{
epochCounter: 100,
}
nm := &testNetmapClient{}
m := &testMorphClient{
balance: 150,
}
proc, err := newTestProc(t, func(p *Params) {
p.NetmapClient = nm
p.MorphClient = m
p.EpochState = es
})
require.NoError(t, err, "failed to create processor")
ev := frostfsEvent.Config{
IDValue: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
KeyValue: []byte{1, 2, 3, 4, 5},
ValueValue: []byte{6, 7, 8, 9, 0},
TxHashValue: util.Uint256{100},
}
proc.handleConfig(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var expConfig nmClient.SetConfigPrm
expConfig.SetHash(ev.TxHashValue)
expConfig.SetID(ev.IDValue)
expConfig.SetKey(ev.KeyValue)
expConfig.SetValue(ev.ValueValue)
require.EqualValues(t, []nmClient.SetConfigPrm{expConfig}, nm.config, "invalid config value")
}
func TestHandleUnbind(t *testing.T) {
t.Parallel()
es := &testEpochState{
epochCounter: 100,
}
m := &testMorphClient{
balance: 150,
}
id := &testIDClient{}
proc, err := newTestProc(t, func(p *Params) {
p.EpochState = es
p.MorphClient = m
p.FrostFSIDClient = id
})
require.NoError(t, err, "failed to create processor")
p, err := keys.NewPrivateKey()
require.NoError(t, err)
evUnbind := frostfsEvent.Unbind{
BindCommon: frostfsEvent.BindCommon{
UserValue: util.Uint160{49}.BytesBE(),
KeysValue: [][]byte{
p.PublicKey().Bytes(),
},
TxHashValue: util.Uint256{100},
},
}
proc.handleUnbind(evUnbind)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var userID user.ID
userID.SetScriptHash(util.Uint160{49})
var expBind frostfsid.CommonBindPrm
expBind.SetOwnerID(userID.WalletBytes())
expBind.SetKeys(evUnbind.BindCommon.KeysValue)
expBind.SetHash(evUnbind.BindCommon.TxHashValue)
var expNilSlice []frostfsid.CommonBindPrm
require.EqualValues(t, []frostfsid.CommonBindPrm{expBind}, id.remove, "invalid remove keys value")
require.EqualValues(t, expNilSlice, id.add, "invalid add keys value")
evBind := frostfsEvent.Bind{
BindCommon: frostfsEvent.BindCommon{
UserValue: util.Uint160{49}.BytesBE(),
KeysValue: [][]byte{
p.PublicKey().Bytes(),
},
TxHashValue: util.Uint256{100},
},
}
proc.handleBind(evBind)
time.Sleep(time.Second)
require.EqualValues(t, []frostfsid.CommonBindPrm{expBind}, id.remove, "invalid remove keys value")
require.EqualValues(t, []frostfsid.CommonBindPrm{expBind}, id.add, "invalid add keys value")
}
func newTestProc(t *testing.T, nonDefault func(p *Params)) (*Processor, error) {
p := &Params{
Log: test.NewLogger(t, true),
PoolSize: 1,
FrostFSContract: util.Uint160{0},
FrostFSIDClient: &testIDClient{},
BalanceClient: &testBalaceClient{},
NetmapClient: &testNetmapClient{},
MorphClient: &testMorphClient{},
EpochState: &testEpochState{},
AlphabetState: &testAlphabetState{isAlphabet: true},
Converter: &testPrecisionConverter{},
MintEmitCacheSize: 100,
MintEmitThreshold: 10,
MintEmitValue: fixedn.Fixed8(50),
GasBalanceThreshold: 50,
}
nonDefault(p)
return New(p)
}
type testEpochState struct {
epochCounter uint64
}
func (s *testEpochState) EpochCounter() uint64 {
return s.epochCounter
}
type testAlphabetState struct {
isAlphabet bool
}
func (s *testAlphabetState) IsAlphabet() bool {
return s.isAlphabet
}
type testPrecisionConverter struct {
}
func (c *testPrecisionConverter) ToBalancePrecision(v int64) int64 {
return v
}
type testBalaceClient struct {
mint []balance.MintPrm
lock []balance.LockPrm
burn []balance.BurnPrm
}
func (c *testBalaceClient) Mint(p balance.MintPrm) error {
c.mint = append(c.mint, p)
return nil
}
func (c *testBalaceClient) Lock(p balance.LockPrm) error {
c.lock = append(c.lock, p)
return nil
}
func (c *testBalaceClient) Burn(p balance.BurnPrm) error {
c.burn = append(c.burn, p)
return nil
}
type testNetmapClient struct {
config []nmClient.SetConfigPrm
}
func (c *testNetmapClient) SetConfig(p nmClient.SetConfigPrm) error {
c.config = append(c.config, p)
return nil
}
type transferGas struct {
receiver util.Uint160
amount fixedn.Fixed8
}
type testMorphClient struct {
balance int64
transferGas []transferGas
}
func (c *testMorphClient) GasBalance() (res int64, err error) {
return c.balance, nil
}
func (c *testMorphClient) TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error {
c.transferGas = append(c.transferGas, transferGas{
receiver: receiver,
amount: amount,
})
return nil
}
type testIDClient struct {
add []frostfsid.CommonBindPrm
remove []frostfsid.CommonBindPrm
}
func (c *testIDClient) AddKeys(p frostfsid.CommonBindPrm) error {
c.add = append(c.add, p)
return nil
}
func (c *testIDClient) RemoveKeys(args frostfsid.CommonBindPrm) error {
c.remove = append(c.remove, args)
return nil
}

View file

@ -15,7 +15,7 @@ const (
// Process deposit event by invoking a balance contract and sending native
// gas in the sidechain.
func (np *Processor) processDeposit(deposit *frostfsEvent.Deposit) {
func (np *Processor) processDeposit(deposit frostfsEvent.Deposit) {
if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreDeposit)
return
@ -80,7 +80,7 @@ func (np *Processor) processDeposit(deposit *frostfsEvent.Deposit) {
}
// Process withdraw event by locking assets in the balance account.
func (np *Processor) processWithdraw(withdraw *frostfsEvent.Withdraw) {
func (np *Processor) processWithdraw(withdraw frostfsEvent.Withdraw) {
if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreWithdraw)
return
@ -111,7 +111,7 @@ func (np *Processor) processWithdraw(withdraw *frostfsEvent.Withdraw) {
// Process cheque event by transferring assets from the lock account back to
// the reserve account.
func (np *Processor) processCheque(cheque *frostfsEvent.Cheque) {
func (np *Processor) processCheque(cheque frostfsEvent.Cheque) {
if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreCheque)
return

View file

@ -6,7 +6,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -19,7 +18,7 @@ type bindCommon interface {
TxHash() util.Uint256
}
func (np *Processor) processBind(e bindCommon) {
func (np *Processor) processBind(e bindCommon, bind bool) {
if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreBind)
return
@ -27,10 +26,9 @@ func (np *Processor) processBind(e bindCommon) {
c := &bindCommonContext{
bindCommon: e,
bind: bind,
}
_, c.bind = e.(frostfs.Bind)
err := np.checkBindCommon(c)
if err != nil {
np.log.Error(logs.FrostFSInvalidManageKeyEvent,

View file

@ -9,7 +9,7 @@ import (
// Process config event by setting configuration value from the mainchain in
// the sidechain.
func (np *Processor) processConfig(config *frostfsEvent.Config) {
func (np *Processor) processConfig(config frostfsEvent.Config) {
if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreConfig)
return

View file

@ -6,7 +6,6 @@ import (
"sync"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/balance"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
@ -36,14 +35,34 @@ type (
ToBalancePrecision(int64) int64
}
BalanceClient interface {
Mint(p balance.MintPrm) error
Lock(p balance.LockPrm) error
Burn(p balance.BurnPrm) error
}
NetmapClient interface {
SetConfig(p nmClient.SetConfigPrm) error
}
MorphClient interface {
GasBalance() (res int64, err error)
TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error
}
IDClient interface {
AddKeys(p frostfsid.CommonBindPrm) error
RemoveKeys(args frostfsid.CommonBindPrm) error
}
// Processor of events produced by frostfs contract in main net.
Processor struct {
log *logger.Logger
pool *ants.Pool
frostfsContract util.Uint160
balanceClient *balance.Client
netmapClient *nmClient.Client
morphClient *client.Client
balanceClient BalanceClient
netmapClient NetmapClient
morphClient MorphClient
epochState EpochState
alphabetState AlphabetState
converter PrecisionConverter
@ -52,8 +71,7 @@ type (
mintEmitThreshold uint64
mintEmitValue fixedn.Fixed8
gasBalanceThreshold int64
frostfsIDClient *frostfsid.Client
frostfsIDClient IDClient
}
// Params of the processor constructor.
@ -61,10 +79,10 @@ type (
Log *logger.Logger
PoolSize int
FrostFSContract util.Uint160
FrostFSIDClient *frostfsid.Client
BalanceClient *balance.Client
NetmapClient *nmClient.Client
MorphClient *client.Client
FrostFSIDClient IDClient
BalanceClient BalanceClient
NetmapClient NetmapClient
MorphClient MorphClient
EpochState EpochState
AlphabetState AlphabetState
Converter PrecisionConverter

View file

@ -0,0 +1,304 @@
package governance
import (
"encoding/binary"
"sort"
"testing"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
frostfscontract "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfs"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/rolemanagement"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestHandleAlphabetSyncEvent(t *testing.T) {
t.Parallel()
testKeys := generateTestKeys(t)
es := &testEpochState{
epoch: 100,
}
as := &testAlphabetState{
isAlphabet: true,
}
v := &testVoter{}
irf := &testIRFetcher{
publicKeys: testKeys.sidechainKeys,
}
m := &testMorphClient{
commiteeKeys: testKeys.sidechainKeys,
}
mn := &testMainnetClient{
alphabetKeys: testKeys.mainnetKeys,
}
f := &testFrostFSClient{}
nm := &testNetmapClient{}
proc, err := New(
&Params{
Log: test.NewLogger(t, true),
EpochState: es,
AlphabetState: as,
Voter: v,
IRFetcher: irf,
NotaryDisabled: true,
MorphClient: m,
MainnetClient: mn,
FrostFSClient: f,
NetmapClient: nm,
},
)
require.NoError(t, err, "failed to create processor")
ev := Sync{
txHash: util.Uint256{100},
}
proc.HandleAlphabetSync(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.EqualValues(t, []VoteValidatorPrm{
{
Validators: testKeys.newAlphabetExp,
Hash: &ev.txHash,
},
}, v.votes, "invalid vote calls")
var irUpdateExp nmClient.UpdateIRPrm
irUpdateExp.SetKeys(testKeys.newInnerRingExp)
irUpdateExp.SetHash(ev.txHash)
require.EqualValues(t, []nmClient.UpdateIRPrm{irUpdateExp}, nm.updates, "invalid IR updates")
var expAlphabetUpdates []client.UpdateAlphabetListPrm
require.EqualValues(t, expAlphabetUpdates, m.alphabetUpdates, "invalid alphabet updates")
var expNotaryUpdates []client.UpdateNotaryListPrm
require.EqualValues(t, expNotaryUpdates, m.notaryUpdates, "invalid notary list updates")
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, es.epoch)
id := append([]byte(alphabetUpdateIDPrefix), buf...)
var expFrostFSAlphabetUpd frostfscontract.AlphabetUpdatePrm
expFrostFSAlphabetUpd.SetID(id)
expFrostFSAlphabetUpd.SetPubs(testKeys.newAlphabetExp)
require.EqualValues(t, []frostfscontract.AlphabetUpdatePrm{expFrostFSAlphabetUpd}, f.updates, "invalid FrostFS alphabet updates")
}
func TestHandleAlphabetDesignateEvent(t *testing.T) {
t.Parallel()
testKeys := generateTestKeys(t)
es := &testEpochState{
epoch: 100,
}
as := &testAlphabetState{
isAlphabet: true,
}
v := &testVoter{}
irf := &testIRFetcher{
publicKeys: testKeys.sidechainKeys,
}
m := &testMorphClient{
commiteeKeys: testKeys.sidechainKeys,
}
mn := &testMainnetClient{
alphabetKeys: testKeys.mainnetKeys,
}
f := &testFrostFSClient{}
nm := &testNetmapClient{}
proc, err := New(
&Params{
Log: test.NewLogger(t, true),
EpochState: es,
AlphabetState: as,
Voter: v,
IRFetcher: irf,
NotaryDisabled: false,
MorphClient: m,
MainnetClient: mn,
FrostFSClient: f,
NetmapClient: nm,
},
)
require.NoError(t, err, "failed to create processor")
ev := rolemanagement.Designate{
TxHash: util.Uint256{100},
Role: noderoles.NeoFSAlphabet,
}
proc.HandleAlphabetSync(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.EqualValues(t, []VoteValidatorPrm{
{
Validators: testKeys.newAlphabetExp,
Hash: &ev.TxHash,
},
}, v.votes, "invalid vote calls")
var irUpdatesExp []nmClient.UpdateIRPrm
require.EqualValues(t, irUpdatesExp, nm.updates, "invalid IR updates")
var alpabetUpdExp client.UpdateAlphabetListPrm
alpabetUpdExp.SetList(testKeys.newInnerRingExp)
alpabetUpdExp.SetHash(ev.TxHash)
require.EqualValues(t, []client.UpdateAlphabetListPrm{alpabetUpdExp}, m.alphabetUpdates, "invalid alphabet updates")
var expNotaryUpdate client.UpdateNotaryListPrm
expNotaryUpdate.SetList(testKeys.newAlphabetExp)
expNotaryUpdate.SetHash(ev.TxHash)
require.EqualValues(t, []client.UpdateNotaryListPrm{expNotaryUpdate}, m.notaryUpdates, "invalid notary list updates")
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, es.epoch)
id := append([]byte(alphabetUpdateIDPrefix), buf...)
var expFrostFSAlphabetUpd frostfscontract.AlphabetUpdatePrm
expFrostFSAlphabetUpd.SetID(id)
expFrostFSAlphabetUpd.SetPubs(testKeys.newAlphabetExp)
require.EqualValues(t, []frostfscontract.AlphabetUpdatePrm{expFrostFSAlphabetUpd}, f.updates, "invalid FrostFS alphabet updates")
}
type testKeys struct {
sidechainKeys keys.PublicKeys
mainnetKeys keys.PublicKeys
newAlphabetExp keys.PublicKeys
newInnerRingExp keys.PublicKeys
}
func generateTestKeys(t *testing.T) testKeys {
for {
var result testKeys
for i := 0; i < 4; i++ {
pk, err := keys.NewPrivateKey()
require.NoError(t, err, "failed to create private key")
result.sidechainKeys = append(result.sidechainKeys, pk.PublicKey())
}
result.mainnetKeys = append(result.mainnetKeys, result.sidechainKeys...)
pk, err := keys.NewPrivateKey()
require.NoError(t, err, "failed to create private key")
result.mainnetKeys = append(result.mainnetKeys, pk.PublicKey())
result.newAlphabetExp, err = newAlphabetList(result.sidechainKeys, result.mainnetKeys)
require.NoError(t, err, "failed to create expected new alphabet")
if len(result.newAlphabetExp) == 0 {
continue //can be happen because of random and sort
}
var irKeys keys.PublicKeys
irKeys = append(irKeys, result.sidechainKeys...)
result.newInnerRingExp, err = updateInnerRing(irKeys, result.sidechainKeys, result.newAlphabetExp)
require.NoError(t, err, "failed to create expected new IR")
sort.Sort(result.newInnerRingExp)
return result
}
}
type testEpochState struct {
epoch uint64
}
func (s *testEpochState) EpochCounter() uint64 {
return s.epoch
}
type testAlphabetState struct {
isAlphabet bool
}
func (s *testAlphabetState) IsAlphabet() bool {
return s.isAlphabet
}
type testVoter struct {
votes []VoteValidatorPrm
}
func (v *testVoter) VoteForSidechainValidator(prm VoteValidatorPrm) error {
v.votes = append(v.votes, prm)
return nil
}
type testIRFetcher struct {
publicKeys keys.PublicKeys
}
func (f *testIRFetcher) InnerRingKeys() (keys.PublicKeys, error) {
return f.publicKeys, nil
}
type testMorphClient struct {
commiteeKeys keys.PublicKeys
alphabetUpdates []client.UpdateAlphabetListPrm
notaryUpdates []client.UpdateNotaryListPrm
}
func (c *testMorphClient) Committee() (res keys.PublicKeys, err error) {
return c.commiteeKeys, nil
}
func (c *testMorphClient) UpdateNeoFSAlphabetList(prm client.UpdateAlphabetListPrm) error {
c.alphabetUpdates = append(c.alphabetUpdates, prm)
return nil
}
func (c *testMorphClient) UpdateNotaryList(prm client.UpdateNotaryListPrm) error {
c.notaryUpdates = append(c.notaryUpdates, prm)
return nil
}
type testMainnetClient struct {
alphabetKeys keys.PublicKeys
designateHash util.Uint160
}
func (c *testMainnetClient) NeoFSAlphabetList() (res keys.PublicKeys, err error) {
return c.alphabetKeys, nil
}
func (c *testMainnetClient) GetDesignateHash() util.Uint160 {
return c.designateHash
}
type testFrostFSClient struct {
updates []frostfscontract.AlphabetUpdatePrm
}
func (c *testFrostFSClient) AlphabetUpdate(p frostfscontract.AlphabetUpdatePrm) error {
c.updates = append(c.updates, p)
return nil
}
type testNetmapClient struct {
updates []nmClient.UpdateIRPrm
}
func (c *testNetmapClient) UpdateInnerRing(p nmClient.UpdateIRPrm) error {
c.updates = append(c.updates, p)
return nil
}

View file

@ -53,20 +53,39 @@ type (
InnerRingKeys() (keys.PublicKeys, error)
}
FrostFSClient interface {
AlphabetUpdate(p frostfscontract.AlphabetUpdatePrm) error
}
NetmapClient interface {
UpdateInnerRing(p nmClient.UpdateIRPrm) error
}
MainnetClient interface {
NeoFSAlphabetList() (res keys.PublicKeys, err error)
GetDesignateHash() util.Uint160
}
MorphClient interface {
Committee() (res keys.PublicKeys, err error)
UpdateNeoFSAlphabetList(prm client.UpdateAlphabetListPrm) error
UpdateNotaryList(prm client.UpdateNotaryListPrm) error
}
// Processor of events related to governance in the network.
Processor struct {
log *logger.Logger
pool *ants.Pool
frostfsClient *frostfscontract.Client
netmapClient *nmClient.Client
frostfsClient FrostFSClient
netmapClient NetmapClient
alphabetState AlphabetState
epochState EpochState
voter Voter
irFetcher IRFetcher
mainnetClient *client.Client
morphClient *client.Client
mainnetClient MainnetClient
morphClient MorphClient
notaryDisabled bool
@ -82,10 +101,10 @@ type (
Voter Voter
IRFetcher IRFetcher
MorphClient *client.Client
MainnetClient *client.Client
FrostFSClient *frostfscontract.Client
NetmapClient *nmClient.Client
MorphClient MorphClient
MainnetClient MainnetClient
FrostFSClient FrostFSClient
NetmapClient NetmapClient
NotaryDisabled bool
}

View file

@ -0,0 +1,570 @@
package netmap
import (
"fmt"
"testing"
"time"
v2netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
netmapContract "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/audit"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/governance"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/settlement"
timerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/timers"
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
netmapclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
"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/core/transaction"
"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/network/payload"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestNewEpochTick(t *testing.T) {
t.Parallel()
es := &testEpochState{
counter: 100,
}
nc := &testNetmapClient{}
proc, err := newTestProc(t, func(p *Params) {
p.NotaryDisabled = true
p.CleanupEnabled = true
p.EpochState = es
p.NetmapClient = nc
})
require.NoError(t, err, "failed to create processor")
ev := timerEvent.NewEpochTick{}
proc.HandleNewEpochTick(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.EqualValues(t, []uint64{101}, nc.newEpochs, "invalid epochs")
}
func TestNewEpoch(t *testing.T) {
t.Parallel()
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())
network := &netmap.NetMap{}
network.SetNodes([]netmap.NodeInfo{node1, node2})
es := &testEpochState{
counter: 100,
duration: 10,
}
r := &testEpochResetter{}
cc := &testContainerClient{}
nc := &testNetmapClient{
epochDuration: 20,
txHeights: map[util.Uint256]uint32{
{101}: 10_000,
},
netmap: network,
}
eh := &testEventHandler{}
proc, err := newTestProc(t, func(p *Params) {
p.NotaryDisabled = true
p.NotaryDepositHandler = eh.Handle
p.HandleAudit = eh.Handle
p.AuditSettlementsHandler = eh.Handle
p.AlphabetSyncHandler = eh.Handle
p.NetmapClient = nc
p.ContainerWrapper = cc
p.EpochTimer = r
p.EpochState = es
})
require.NoError(t, err, "failed to create processor")
ev := netmapEvent.NewEpoch{
Num: 101,
Hash: util.Uint256{101},
}
proc.handleNewEpoch(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.Equal(t, nc.epochDuration, es.duration, "invalid epoch duration")
require.Equal(t, ev.Num, es.counter, "invalid epoch counter")
require.EqualValues(t, []uint32{nc.txHeights[ev.Hash]}, r.timers, "invalid epoch timer resets")
var expEstimation cntClient.StartEstimationPrm
expEstimation.SetEpoch(ev.Num - 1)
expEstimation.SetHash(ev.Hash)
require.EqualValues(t, []cntClient.StartEstimationPrm{expEstimation}, cc.estimations, "invalid estimations")
require.EqualValues(t, []event.Event{
audit.NewAuditStartEvent(ev.Num),
settlement.NewAuditEvent(ev.Num),
governance.NewSyncEvent(ev.TxHash()),
ev,
}, eh.handledEvents, "invalid handled events")
}
func TestAddPeer(t *testing.T) {
t.Parallel()
t.Run("with notary", func(t *testing.T) {
t.Parallel()
nc := &testNetmapClient{
contractAddress: util.Uint160{47},
}
proc, err := newTestProc(t, func(p *Params) {
p.NotaryDisabled = true
p.NetmapClient = nc
})
require.NoError(t, err, "failed to create processor")
var node netmap.NodeInfo
key, err := keys.NewPublicKeyFromString("038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35")
require.NoError(t, err, "failed to parse key1")
node.SetPublicKey(key.Bytes())
ev := netmapEvent.AddPeer{
NodeBytes: node.Marshal(),
Request: &payload.P2PNotaryRequest{
MainTransaction: &transaction.Transaction{
Nonce: 100,
},
},
}
proc.handleAddPeer(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.EqualValues(t, []notaryInvoke{
{
contract: nc.contractAddress,
fee: 0,
nonce: ev.Request.MainTransaction.Nonce,
vub: nil,
method: "addPeerIR",
args: []any{ev.Node()},
},
}, nc.notaryInvokes, "invalid notary invokes")
})
t.Run("without notary", func(t *testing.T) {
t.Parallel()
nc := &testNetmapClient{
contractAddress: util.Uint160{47},
}
proc, err := newTestProc(t, func(p *Params) {
p.NotaryDisabled = true
p.NetmapClient = nc
})
require.NoError(t, err, "failed to create processor")
var node netmap.NodeInfo
key, err := keys.NewPublicKeyFromString("038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35")
require.NoError(t, err, "failed to parse key")
node.SetPublicKey(key.Bytes())
ev := netmapEvent.AddPeer{
NodeBytes: node.Marshal(),
}
proc.handleAddPeer(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var addPeerExp netmapclient.AddPeerPrm
addPeerExp.SetNodeInfo(node)
require.EqualValues(t, []netmapclient.AddPeerPrm{addPeerExp}, nc.addPeers, "invalid peers")
})
}
func TestUpdateState(t *testing.T) {
t.Parallel()
t.Run("with notary", func(t *testing.T) {
t.Parallel()
ns := &testNodeStateSettings{
maintAllowed: true,
}
nc := &testNetmapClient{}
proc, err := newTestProc(t, func(p *Params) {
p.NotaryDisabled = true
p.NodeStateSettings = ns
p.NetmapClient = nc
})
require.NoError(t, err, "failed to create processor")
key, err := keys.NewPublicKeyFromString("038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35")
require.NoError(t, err, "failed to parse key")
ev := netmapEvent.UpdatePeer{
State: netmapContract.NodeStateOnline,
PubKey: key,
Request: &payload.P2PNotaryRequest{
MainTransaction: &transaction.Transaction{
Nonce: 100,
},
},
}
proc.handleUpdateState(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
require.EqualValues(t, []*transaction.Transaction{
ev.Request.MainTransaction,
}, nc.invokedTxs, "invalid invoked transactions")
})
t.Run("without notary", func(t *testing.T) {
t.Parallel()
ns := &testNodeStateSettings{
maintAllowed: true,
}
nc := &testNetmapClient{}
proc, err := newTestProc(t, func(p *Params) {
p.NetmapClient = nc
p.NodeStateSettings = ns
})
require.NoError(t, err, "failed to create processor")
key, err := keys.NewPublicKeyFromString("038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35")
require.NoError(t, err, "failed to parse key")
ev := netmapEvent.UpdatePeer{
State: netmapContract.NodeStateOnline,
PubKey: key,
}
proc.handleUpdateState(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
var expUpdPeer netmapclient.UpdatePeerPrm
expUpdPeer.SetMaintenance()
expUpdPeer.SetOnline()
expUpdPeer.SetKey(ev.PubKey.Bytes())
require.EqualValues(t, []netmapclient.UpdatePeerPrm{expUpdPeer}, nc.peerStateUpdates, "invalid peer state updates")
})
}
func TestCleanupTick(t *testing.T) {
t.Parallel()
t.Run("notary disabled", func(t *testing.T) {
t.Parallel()
nc := &testNetmapClient{}
proc, err := newTestProc(t, func(p *Params) {
p.NetmapClient = nc
p.NotaryDisabled = true
p.CleanupEnabled = true
})
require.NoError(t, err, "failed to create processor")
key1Str := "038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35"
proc.netmapSnapshot.lastAccess[key1Str] = epochStampWithNodeInfo{
epochStamp: epochStamp{
epoch: 95,
removeFlag: false,
},
}
key2Str := "02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3"
proc.netmapSnapshot.lastAccess[key2Str] = epochStampWithNodeInfo{
epochStamp: epochStamp{
epoch: 98,
removeFlag: false,
},
}
ev := netmapCleanupTick{
epoch: 100,
txHash: util.Uint256{123},
}
proc.handleCleanupTick(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
keyExp, err := keys.NewPublicKeyFromString(key1Str)
require.NoError(t, err, "failed to parse expired key")
updExp := netmapclient.UpdatePeerPrm{}
updExp.SetKey(keyExp.Bytes())
updExp.SetHash(ev.TxHash())
require.EqualValues(t, []netmapclient.UpdatePeerPrm{updExp}, nc.peerStateUpdates, "invalid peer updates")
require.True(t, proc.netmapSnapshot.lastAccess[key1Str].removeFlag, "invalid expired removed flag")
require.False(t, proc.netmapSnapshot.lastAccess[key2Str].removeFlag, "invalid non expired removed flag")
})
t.Run("notary enabled", func(t *testing.T) {
t.Parallel()
nc := &testNetmapClient{
contractAddress: util.Uint160{111},
}
proc, err := newTestProc(t,
func(p *Params) {
p.NetmapClient = nc
p.CleanupEnabled = true
},
)
require.NoError(t, err, "failed to create processor")
key1Str := "038c862959e56b43e20f79187c4fe9e0bc7c8c66c1603e6cf0ec7f87ab6b08dc35"
proc.netmapSnapshot.lastAccess[key1Str] = epochStampWithNodeInfo{
epochStamp: epochStamp{
epoch: 95,
removeFlag: false,
},
}
key2Str := "02ac920cd7df0b61b289072e6b946e2da4e1a31b9ab1c621bb475e30fa4ab102c3"
proc.netmapSnapshot.lastAccess[key2Str] = epochStampWithNodeInfo{
epochStamp: epochStamp{
epoch: 98,
removeFlag: false,
},
}
ev := netmapCleanupTick{
epoch: 100,
txHash: util.Uint256{123},
}
proc.handleCleanupTick(ev)
for proc.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
keyExp, err := keys.NewPublicKeyFromString(key1Str)
require.NoError(t, err, "failed to parse expired key")
updExp := netmapclient.UpdatePeerPrm{}
updExp.SetKey(keyExp.Bytes())
updExp.SetHash(ev.TxHash())
require.EqualValues(t, []notaryInvoke{
{
contract: nc.contractAddress,
fee: 0,
nonce: uint32(ev.epoch),
vub: nil,
method: "updateStateIR",
args: []any{int64(v2netmap.Offline), keyExp.Bytes()},
},
}, nc.notaryInvokes, "invalid notary invokes")
require.True(t, proc.netmapSnapshot.lastAccess[key1Str].removeFlag, "invalid expired removed flag")
require.False(t, proc.netmapSnapshot.lastAccess[key2Str].removeFlag, "invalid non expired removed flag")
})
}
func newTestProc(t *testing.T, nonDefault func(p *Params)) (*Processor, error) {
ns := &testNodeStateSettings{}
es := &testEpochState{}
r := &testEpochResetter{}
as := &testAlphabetState{
isAlphabet: true,
}
cc := &testContainerClient{}
nc := &testNetmapClient{}
eh := &testEventHandler{}
p := &Params{
Log: test.NewLogger(t, true),
PoolSize: 1,
CleanupEnabled: false,
CleanupThreshold: 3,
NotaryDisabled: false,
NodeStateSettings: ns,
NodeValidator: &testValidator{},
EpochState: es,
EpochTimer: r,
AlphabetState: as,
ContainerWrapper: cc,
NetmapClient: nc,
NotaryDepositHandler: eh.Handle,
HandleAudit: eh.Handle,
AuditSettlementsHandler: eh.Handle,
AlphabetSyncHandler: eh.Handle,
}
nonDefault(p)
return New(p)
}
type testNodeStateSettings struct {
maintAllowed bool
}
func (s *testNodeStateSettings) MaintenanceModeAllowed() error {
if s.maintAllowed {
return nil
}
return fmt.Errorf("maintenance mode not allowed")
}
type testValidator struct{}
func (v *testValidator) VerifyAndUpdate(*netmap.NodeInfo) error {
return nil
}
type testEpochState struct {
counter uint64
duration uint64
}
func (s *testEpochState) SetEpochCounter(c uint64) {
s.counter = c
}
func (s *testEpochState) EpochCounter() uint64 {
return s.counter
}
func (s *testEpochState) SetEpochDuration(d uint64) {
s.duration = d
}
func (s *testEpochState) EpochDuration() uint64 {
return s.duration
}
type testEpochResetter struct {
timers []uint32
}
func (r *testEpochResetter) ResetEpochTimer(t uint32) error {
r.timers = append(r.timers, t)
return nil
}
type testAlphabetState struct {
isAlphabet bool
}
func (s *testAlphabetState) IsAlphabet() bool {
return s.isAlphabet
}
type testContainerClient struct {
estimations []cntClient.StartEstimationPrm
}
func (c *testContainerClient) StartEstimation(p cntClient.StartEstimationPrm) error {
c.estimations = append(c.estimations, p)
return nil
}
type notaryInvoke struct {
contract util.Uint160
fee fixedn.Fixed8
nonce uint32
vub *uint32
method string
args []any
}
type testNetmapClient struct {
contractAddress util.Uint160
epochDuration uint64
netmap *netmap.NetMap
txHeights map[util.Uint256]uint32
peerStateUpdates []netmapclient.UpdatePeerPrm
notaryInvokes []notaryInvoke
newEpochs []uint64
addPeers []netmapclient.AddPeerPrm
invokedTxs []*transaction.Transaction
}
func (c *testNetmapClient) UpdatePeerState(p netmapclient.UpdatePeerPrm) error {
c.peerStateUpdates = append(c.peerStateUpdates, p)
return nil
}
func (c *testNetmapClient) MorphNotaryInvoke(contract util.Uint160, fee fixedn.Fixed8, nonce uint32, vub *uint32, method string, args ...any) error {
c.notaryInvokes = append(c.notaryInvokes, notaryInvoke{
contract: contract,
fee: fee,
nonce: nonce,
vub: vub,
method: method,
args: args,
})
return nil
}
func (c *testNetmapClient) ContractAddress() util.Uint160 {
return c.contractAddress
}
func (c *testNetmapClient) EpochDuration() (uint64, error) {
return c.epochDuration, nil
}
func (c *testNetmapClient) MorphTxHeight(h util.Uint256) (uint32, error) {
if res, found := c.txHeights[h]; found {
return res, nil
}
return 0, fmt.Errorf("not found")
}
func (c *testNetmapClient) NetMap() (*netmap.NetMap, error) {
return c.netmap, nil
}
func (c *testNetmapClient) NewEpoch(epoch uint64, force bool) error {
c.newEpochs = append(c.newEpochs, epoch)
return nil
}
func (c *testNetmapClient) MorphIsValidScript(script []byte, signers []transaction.Signer) (valid bool, err error) {
return true, nil
}
func (c *testNetmapClient) AddPeer(p netmapclient.AddPeerPrm) error {
c.addPeers = append(c.addPeers, p)
return nil
}
func (c *testNetmapClient) MorphNotarySignAndInvokeTX(mainTx *transaction.Transaction) error {
c.invokedTxs = append(c.invokedTxs, mainTx)
return nil
}
type testEventHandler struct {
handledEvents []event.Event
}
func (h *testEventHandler) Handle(e event.Event) {
h.handledEvents = append(h.handledEvents, e)
}

View file

@ -39,7 +39,7 @@ func (np *Processor) processNetmapCleanupTick(ev netmapCleanupTick) {
err = np.netmapClient.UpdatePeerState(prm)
} else {
err = np.netmapClient.Morph().NotaryInvoke(
err = np.netmapClient.MorphNotaryInvoke(
np.netmapClient.ContractAddress(),
0,
uint32(ev.epoch),

View file

@ -25,7 +25,7 @@ func (np *Processor) processNewEpoch(ev netmapEvent.NewEpoch) {
np.epochState.SetEpochCounter(epoch)
h, err := np.netmapClient.Morph().TxHeight(ev.TxHash())
h, err := np.netmapClient.MorphTxHeight(ev.TxHash())
if err != nil {
np.log.Warn(logs.NetmapCantGetTransactionHeight,
zap.String("hash", ev.TxHash().StringLE()),

View file

@ -21,7 +21,7 @@ func (np *Processor) processAddPeer(ev netmapEvent.AddPeer) {
// check if notary transaction is valid, see #976
if originalRequest := ev.NotaryRequest(); originalRequest != nil {
tx := originalRequest.MainTransaction
ok, err := np.netmapClient.Morph().IsValidScript(tx.Script, tx.Signers)
ok, err := np.netmapClient.MorphIsValidScript(tx.Script, tx.Signers)
if err != nil || !ok {
np.log.Warn(logs.NetmapNonhaltNotaryTransaction,
zap.String("method", "netmap.AddPeer"),
@ -73,7 +73,7 @@ func (np *Processor) processAddPeer(ev netmapEvent.AddPeer) {
if nr := ev.NotaryRequest(); nr != nil {
// create new notary request with the original nonce
err = np.netmapClient.Morph().NotaryInvoke(
err = np.netmapClient.MorphNotaryInvoke(
np.netmapClient.ContractAddress(),
0,
nr.MainTransaction.Nonce,
@ -117,7 +117,7 @@ func (np *Processor) processUpdatePeer(ev netmapEvent.UpdatePeer) {
}
if nr := ev.NotaryRequest(); nr != nil {
err = np.netmapClient.Morph().NotarySignAndInvokeTX(nr.MainTransaction)
err = np.netmapClient.MorphNotarySignAndInvokeTX(nr.MainTransaction)
} else {
prm := netmapclient.UpdatePeerPrm{}

View file

@ -6,13 +6,16 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/netmap/nodevalidation/state"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
netmapclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"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"
)
@ -51,6 +54,23 @@ type (
VerifyAndUpdate(*netmap.NodeInfo) error
}
Client interface {
UpdatePeerState(p netmapclient.UpdatePeerPrm) error
MorphNotaryInvoke(contract util.Uint160, fee fixedn.Fixed8, nonce uint32, vub *uint32, method string, args ...any) error
ContractAddress() util.Uint160
EpochDuration() (uint64, error)
MorphTxHeight(h util.Uint256) (res uint32, err error)
NetMap() (*netmap.NetMap, error)
NewEpoch(epoch uint64, force bool) error
MorphIsValidScript(script []byte, signers []transaction.Signer) (valid bool, err error)
AddPeer(p netmapclient.AddPeerPrm) error
MorphNotarySignAndInvokeTX(mainTx *transaction.Transaction) error
}
ContainerClient interface {
StartEstimation(p cntClient.StartEstimationPrm) error
}
// Processor of events produced by network map contract
// and new epoch ticker, because it is related to contract.
Processor struct {
@ -60,8 +80,8 @@ type (
epochState EpochState
alphabetState AlphabetState
netmapClient *nmClient.Client
containerWrp *container.Client
netmapClient Client
containerWrp ContainerClient
netmapSnapshot cleanupTable
@ -81,13 +101,13 @@ type (
Params struct {
Log *logger.Logger
PoolSize int
NetmapClient *nmClient.Client
NetmapClient Client
EpochTimer EpochTimerReseter
EpochState EpochState
AlphabetState AlphabetState
CleanupEnabled bool
CleanupThreshold uint64 // in epochs
ContainerWrapper *container.Client
ContainerWrapper ContainerClient
HandleAudit event.Handler
AuditSettlementsHandler event.Handler

View file

@ -0,0 +1,59 @@
package netmap
import (
netmapclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/util"
)
func NewNetmapClient(netmapClient *netmapclient.Client) Client {
return &netmapClientWrapper{
netmapClient: netmapClient,
}
}
type netmapClientWrapper struct {
netmapClient *netmapclient.Client
}
func (w *netmapClientWrapper) UpdatePeerState(p netmapclient.UpdatePeerPrm) error {
return w.netmapClient.UpdatePeerState(p)
}
func (w *netmapClientWrapper) MorphNotaryInvoke(contract util.Uint160, fee fixedn.Fixed8, nonce uint32, vub *uint32, method string, args ...any) error {
return w.netmapClient.Morph().NotaryInvoke(contract, fee, nonce, vub, method, args...)
}
func (w *netmapClientWrapper) ContractAddress() util.Uint160 {
return w.netmapClient.ContractAddress()
}
func (w *netmapClientWrapper) EpochDuration() (uint64, error) {
return w.netmapClient.EpochDuration()
}
func (w *netmapClientWrapper) MorphTxHeight(h util.Uint256) (res uint32, err error) {
return w.netmapClient.Morph().TxHeight(h)
}
func (w *netmapClientWrapper) NetMap() (*netmap.NetMap, error) {
return w.netmapClient.NetMap()
}
func (w *netmapClientWrapper) NewEpoch(epoch uint64, force bool) error {
return w.netmapClient.NewEpoch(epoch, force)
}
func (w *netmapClientWrapper) MorphIsValidScript(script []byte, signers []transaction.Signer) (valid bool, err error) {
return w.netmapClient.Morph().IsValidScript(script, signers)
}
func (w *netmapClientWrapper) AddPeer(p netmapclient.AddPeerPrm) error {
return w.netmapClient.AddPeer(p)
}
func (w *netmapClientWrapper) MorphNotarySignAndInvokeTX(mainTx *transaction.Transaction) error {
return w.netmapClient.Morph().NotarySignAndInvokeTX(mainTx)
}

View file

@ -0,0 +1,53 @@
package innerring
import (
"testing"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
func TestServerState(t *testing.T) {
keyStr := "03ff65b6ae79134a4dce9d0d39d3851e9bab4ee97abf86e81e1c5bbc50cd2826ae"
commiteeKeys, err := keys.NewPublicKeysFromStrings([]string{keyStr})
require.NoError(t, err, "convert string to commitee public keys failed")
cf := &testCommiteeFetcher{
keys: commiteeKeys,
}
irKeys, err := keys.NewPublicKeysFromStrings([]string{keyStr})
require.NoError(t, err, "convert string to IR public keys failed")
irf := &testIRFetcher{
keys: irKeys,
}
key, err := keys.NewPublicKeyFromString(keyStr)
require.NoError(t, err, "convert string to public key failed")
require.NoError(t, err, "failed to create morph client")
srv := &Server{
statusIndex: newInnerRingIndexer(cf, irf, key, time.Second),
morphClient: &client.Client{},
}
var epoch uint64 = 100
srv.SetEpochCounter(epoch)
require.Equal(t, epoch, srv.EpochCounter(), "invalid epoch counter")
var epochDuration uint64 = 15
srv.SetEpochDuration(epochDuration)
require.Equal(t, epochDuration, srv.EpochDuration(), "invalid epoch duration")
var healthStatus control.HealthStatus = control.HealthStatus_READY
srv.setHealthStatus(healthStatus)
require.Equal(t, healthStatus, srv.HealthStatus(), "invalid health status")
require.True(t, srv.IsActive(), "invalid IsActive result")
require.True(t, srv.IsAlphabet(), "invalid IsAlphabet result")
require.Equal(t, 0, srv.InnerRingIndex(), "invalid IR index")
require.Equal(t, 1, srv.InnerRingSize(), "invalid IR index")
require.Equal(t, 0, srv.AlphabetIndex(), "invalid alphabet index")
}

View file

@ -26,8 +26,12 @@ func AsContainerSource(w *Client) containercore.Source {
return (*containerSource)(w)
}
type getContainer interface {
Get(cid []byte) (*containercore.Container, error)
}
// Get marshals container ID, and passes it to Wrapper's Get method.
func Get(c *Client, cnr cid.ID) (*containercore.Container, error) {
func Get(c getContainer, cnr cid.ID) (*containercore.Container, error) {
binCnr := make([]byte, sha256.Size)
cnr.Encode(binCnr)

View file

@ -12,34 +12,34 @@ import (
// Delete structure of container.Delete notification from morph chain.
type Delete struct {
containerID []byte
signature []byte
token []byte
ContainerIDValue []byte
SignatureValue []byte
TokenValue []byte
// For notary notifications only.
// Contains raw transactions of notary request.
notaryRequest *payload.P2PNotaryRequest
NotaryRequestValue *payload.P2PNotaryRequest
}
// MorphEvent implements Neo:Morph Event interface.
func (Delete) MorphEvent() {}
// ContainerID is a marshalled container structure, defined in API.
func (d Delete) ContainerID() []byte { return d.containerID }
func (d Delete) ContainerID() []byte { return d.ContainerIDValue }
// Signature of marshalled container by container owner.
func (d Delete) Signature() []byte { return d.signature }
func (d Delete) Signature() []byte { return d.SignatureValue }
// SessionToken returns binary token of the session
// within which the eACL was set.
func (d Delete) SessionToken() []byte {
return d.token
return d.TokenValue
}
// NotaryRequest returns raw notary request if notification
// was received via notary service. Otherwise, returns nil.
func (d Delete) NotaryRequest() *payload.P2PNotaryRequest {
return d.notaryRequest
return d.NotaryRequestValue
}
const expectedItemNumDelete = 3
@ -63,19 +63,19 @@ func ParseDelete(e *state.ContainedNotificationEvent) (event.Event, error) {
}
// parse container
ev.containerID, err = client.BytesFromStackItem(params[0])
ev.ContainerIDValue, err = client.BytesFromStackItem(params[0])
if err != nil {
return nil, fmt.Errorf("could not get container: %w", err)
}
// parse signature
ev.signature, err = client.BytesFromStackItem(params[1])
ev.SignatureValue, err = client.BytesFromStackItem(params[1])
if err != nil {
return nil, fmt.Errorf("could not get signature: %w", err)
}
// parse session token
ev.token, err = client.BytesFromStackItem(params[2])
ev.TokenValue, err = client.BytesFromStackItem(params[2])
if err != nil {
return nil, fmt.Errorf("could not get session token: %w", err)
}

View file

@ -7,19 +7,19 @@ import (
func (d *Delete) setContainerID(v []byte) {
if v != nil {
d.containerID = v
d.ContainerIDValue = v
}
}
func (d *Delete) setSignature(v []byte) {
if v != nil {
d.signature = v
d.SignatureValue = v
}
}
func (d *Delete) setToken(v []byte) {
if v != nil {
d.token = v
d.TokenValue = v
}
}
@ -62,7 +62,7 @@ func ParseDeleteNotary(ne event.NotaryEvent) (event.Event, error) {
}
}
ev.notaryRequest = ne.Raw()
ev.NotaryRequestValue = ne.Raw()
return ev, nil
}

View file

@ -63,9 +63,9 @@ func TestParseDelete(t *testing.T) {
require.NoError(t, err)
require.Equal(t, Delete{
containerID: containerID,
signature: signature,
token: token,
ContainerIDValue: containerID,
SignatureValue: signature,
TokenValue: token,
}, ev)
})
}

View file

@ -12,14 +12,14 @@ import (
// SetEACL represents structure of notification about
// modified eACL table coming from FrostFS Container contract.
type SetEACL struct {
table []byte
signature []byte
publicKey []byte
token []byte
TableValue []byte
SignatureValue []byte
PublicKeyValue []byte
TokenValue []byte
// For notary notifications only.
// Contains raw transactions of notary request.
notaryRequest *payload.P2PNotaryRequest
NotaryRequestValue *payload.P2PNotaryRequest
}
// MorphEvent implements Neo:Morph Event interface.
@ -27,30 +27,30 @@ func (SetEACL) MorphEvent() {}
// Table returns returns eACL table in a binary FrostFS API format.
func (x SetEACL) Table() []byte {
return x.table
return x.TableValue
}
// Signature returns signature of the binary table.
func (x SetEACL) Signature() []byte {
return x.signature
return x.SignatureValue
}
// PublicKey returns public keys of container
// owner in a binary format.
func (x SetEACL) PublicKey() []byte {
return x.publicKey
return x.PublicKeyValue
}
// SessionToken returns binary token of the session
// within which the eACL was set.
func (x SetEACL) SessionToken() []byte {
return x.token
return x.TokenValue
}
// NotaryRequest returns raw notary request if notification
// was received via notary service. Otherwise, returns nil.
func (x SetEACL) NotaryRequest() *payload.P2PNotaryRequest {
return x.notaryRequest
return x.NotaryRequestValue
}
const expectedItemNumEACL = 4
@ -74,25 +74,25 @@ func ParseSetEACL(e *state.ContainedNotificationEvent) (event.Event, error) {
}
// parse table
ev.table, err = client.BytesFromStackItem(params[0])
ev.TableValue, err = client.BytesFromStackItem(params[0])
if err != nil {
return nil, fmt.Errorf("could not parse binary table: %w", err)
}
// parse signature
ev.signature, err = client.BytesFromStackItem(params[1])
ev.SignatureValue, err = client.BytesFromStackItem(params[1])
if err != nil {
return nil, fmt.Errorf("could not parse table signature: %w", err)
}
// parse public key
ev.publicKey, err = client.BytesFromStackItem(params[2])
ev.PublicKeyValue, err = client.BytesFromStackItem(params[2])
if err != nil {
return nil, fmt.Errorf("could not parse binary public key: %w", err)
}
// parse session token
ev.token, err = client.BytesFromStackItem(params[3])
ev.TokenValue, err = client.BytesFromStackItem(params[3])
if err != nil {
return nil, fmt.Errorf("could not get session token: %w", err)
}

View file

@ -7,25 +7,25 @@ import (
func (x *SetEACL) setTable(v []byte) {
if v != nil {
x.table = v
x.TableValue = v
}
}
func (x *SetEACL) setSignature(v []byte) {
if v != nil {
x.signature = v
x.SignatureValue = v
}
}
func (x *SetEACL) setPublicKey(v []byte) {
if v != nil {
x.publicKey = v
x.PublicKeyValue = v
}
}
func (x *SetEACL) setToken(v []byte) {
if v != nil {
x.token = v
x.TokenValue = v
}
}
@ -69,7 +69,7 @@ func ParseSetEACLNotary(ne event.NotaryEvent) (event.Event, error) {
}
}
ev.notaryRequest = ne.Raw()
ev.NotaryRequestValue = ne.Raw()
return ev, nil
}

View file

@ -11,31 +11,31 @@ import (
)
type Bind struct {
bindCommon
BindCommon
}
type bindCommon struct {
user []byte
keys [][]byte
type BindCommon struct {
UserValue []byte
KeysValue [][]byte
// txHash is used in notary environmental
// TxHashValue is used in notary environmental
// for calculating unique but same for
// all notification receivers values.
txHash util.Uint256
TxHashValue util.Uint256
}
// TxHash returns hash of the TX with new epoch
// notification.
func (b bindCommon) TxHash() util.Uint256 {
return b.txHash
func (b BindCommon) TxHash() util.Uint256 {
return b.TxHashValue
}
// MorphEvent implements Neo:Morph Event interface.
func (bindCommon) MorphEvent() {}
func (BindCommon) MorphEvent() {}
func (b bindCommon) Keys() [][]byte { return b.keys }
func (b BindCommon) Keys() [][]byte { return b.KeysValue }
func (b bindCommon) User() []byte { return b.user }
func (b BindCommon) User() []byte { return b.UserValue }
func ParseBind(e *state.ContainedNotificationEvent) (event.Event, error) {
var (
@ -48,17 +48,17 @@ func ParseBind(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not parse stack items from notify event: %w", err)
}
err = parseBind(&ev.bindCommon, params)
err = parseBind(&ev.BindCommon, params)
if err != nil {
return nil, err
}
ev.txHash = e.Container
ev.TxHashValue = e.Container
return ev, nil
}
func parseBind(dst *bindCommon, params []stackitem.Item) error {
func parseBind(dst *BindCommon, params []stackitem.Item) error {
if ln := len(params); ln != 2 {
return event.WrongNumberOfParameters(2, ln)
}
@ -66,7 +66,7 @@ func parseBind(dst *bindCommon, params []stackitem.Item) error {
var err error
// parse user
dst.user, err = client.BytesFromStackItem(params[0])
dst.UserValue, err = client.BytesFromStackItem(params[0])
if err != nil {
return fmt.Errorf("could not get bind user: %w", err)
}
@ -77,7 +77,7 @@ func parseBind(dst *bindCommon, params []stackitem.Item) error {
return fmt.Errorf("could not get bind keys: %w", err)
}
dst.keys = make([][]byte, 0, len(bindKeys))
dst.KeysValue = make([][]byte, 0, len(bindKeys))
for i := range bindKeys {
rawKey, err := client.BytesFromStackItem(bindKeys[i])
@ -85,7 +85,7 @@ func parseBind(dst *bindCommon, params []stackitem.Item) error {
return fmt.Errorf("could not get bind public key: %w", err)
}
dst.keys = append(dst.keys, rawKey)
dst.KeysValue = append(dst.KeysValue, rawKey)
}
return nil

View file

@ -11,26 +11,26 @@ import (
// Cheque structure of frostfs.Cheque notification from mainnet chain.
type Cheque struct {
id []byte
amount int64 // Fixed8
user util.Uint160
lock util.Uint160
IDValue []byte
AmountValue int64 // Fixed8
UserValue util.Uint160
LockValue util.Uint160
}
// MorphEvent implements Neo:Morph Event interface.
func (Cheque) MorphEvent() {}
// ID is a withdraw transaction hash.
func (c Cheque) ID() []byte { return c.id }
func (c Cheque) ID() []byte { return c.IDValue }
// User returns withdraw receiver script hash from main net.
func (c Cheque) User() util.Uint160 { return c.user }
func (c Cheque) User() util.Uint160 { return c.UserValue }
// Amount of the sent assets.
func (c Cheque) Amount() int64 { return c.amount }
func (c Cheque) Amount() int64 { return c.AmountValue }
// LockAccount return script hash for balance contract wallet.
func (c Cheque) LockAccount() util.Uint160 { return c.lock }
func (c Cheque) LockAccount() util.Uint160 { return c.LockValue }
// ParseCheque from notification into cheque structure.
func ParseCheque(e *state.ContainedNotificationEvent) (event.Event, error) {
@ -49,7 +49,7 @@ func ParseCheque(e *state.ContainedNotificationEvent) (event.Event, error) {
}
// parse id
ev.id, err = client.BytesFromStackItem(params[0])
ev.IDValue, err = client.BytesFromStackItem(params[0])
if err != nil {
return nil, fmt.Errorf("could not get cheque id: %w", err)
}
@ -60,13 +60,13 @@ func ParseCheque(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not get cheque user: %w", err)
}
ev.user, err = util.Uint160DecodeBytesBE(user)
ev.UserValue, err = util.Uint160DecodeBytesBE(user)
if err != nil {
return nil, fmt.Errorf("could not convert cheque user to uint160: %w", err)
}
// parse amount
ev.amount, err = client.IntFromStackItem(params[2])
ev.AmountValue, err = client.IntFromStackItem(params[2])
if err != nil {
return nil, fmt.Errorf("could not get cheque amount: %w", err)
}
@ -77,7 +77,7 @@ func ParseCheque(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not get cheque lock account: %w", err)
}
ev.lock, err = util.Uint160DecodeBytesBE(lock)
ev.LockValue, err = util.Uint160DecodeBytesBE(lock)
if err != nil {
return nil, fmt.Errorf("could not convert cheque lock account to uint160: %w", err)
}

View file

@ -77,10 +77,10 @@ func TestParseCheque(t *testing.T) {
require.NoError(t, err)
require.Equal(t, Cheque{
id: id,
amount: amount,
user: user,
lock: lock,
IDValue: id,
AmountValue: amount,
UserValue: user,
LockValue: lock,
}, ev)
})
}

View file

@ -10,30 +10,30 @@ import (
)
type Config struct {
key []byte
value []byte
id []byte
KeyValue []byte
ValueValue []byte
IDValue []byte
// txHash is used in notary environmental
// TxHashValue is used in notary environmental
// for calculating unique but same for
// all notification receivers values.
txHash util.Uint256
TxHashValue util.Uint256
}
// TxHash returns hash of the TX with new epoch
// notification.
func (u Config) TxHash() util.Uint256 {
return u.txHash
return u.TxHashValue
}
// MorphEvent implements Neo:Morph Event interface.
func (Config) MorphEvent() {}
func (u Config) ID() []byte { return u.id }
func (u Config) ID() []byte { return u.IDValue }
func (u Config) Key() []byte { return u.key }
func (u Config) Key() []byte { return u.KeyValue }
func (u Config) Value() []byte { return u.value }
func (u Config) Value() []byte { return u.ValueValue }
func ParseConfig(e *state.ContainedNotificationEvent) (event.Event, error) {
var (
@ -51,24 +51,24 @@ func ParseConfig(e *state.ContainedNotificationEvent) (event.Event, error) {
}
// parse id
ev.id, err = client.BytesFromStackItem(params[0])
ev.IDValue, err = client.BytesFromStackItem(params[0])
if err != nil {
return nil, fmt.Errorf("could not get config update id: %w", err)
}
// parse key
ev.key, err = client.BytesFromStackItem(params[1])
ev.KeyValue, err = client.BytesFromStackItem(params[1])
if err != nil {
return nil, fmt.Errorf("could not get config key: %w", err)
}
// parse value
ev.value, err = client.BytesFromStackItem(params[2])
ev.ValueValue, err = client.BytesFromStackItem(params[2])
if err != nil {
return nil, fmt.Errorf("could not get config value: %w", err)
}
ev.txHash = e.Container
ev.TxHashValue = e.Container
return ev, nil
}

View file

@ -60,9 +60,9 @@ func TestParseConfig(t *testing.T) {
require.NoError(t, err)
require.Equal(t, Config{
id: id,
key: key,
value: value,
IDValue: id,
KeyValue: key,
ValueValue: value,
}, ev)
})
}

View file

@ -11,26 +11,26 @@ import (
// Deposit structure of frostfs.Deposit notification from mainnet chain.
type Deposit struct {
id []byte
amount int64 // Fixed8
from util.Uint160
to util.Uint160
IDValue []byte
AmountValue int64 // Fixed8
FromValue util.Uint160
ToValue util.Uint160
}
// MorphEvent implements Neo:Morph Event interface.
func (Deposit) MorphEvent() {}
// ID is a deposit transaction hash.
func (d Deposit) ID() []byte { return d.id }
func (d Deposit) ID() []byte { return d.IDValue }
// From is a script hash of asset sender in main net.
func (d Deposit) From() util.Uint160 { return d.from }
func (d Deposit) From() util.Uint160 { return d.FromValue }
// To is a script hash of asset receiver in balance contract.
func (d Deposit) To() util.Uint160 { return d.to }
func (d Deposit) To() util.Uint160 { return d.ToValue }
// Amount of transferred assets.
func (d Deposit) Amount() int64 { return d.amount }
func (d Deposit) Amount() int64 { return d.AmountValue }
// ParseDeposit notification into deposit structure.
func ParseDeposit(e *state.ContainedNotificationEvent) (event.Event, error) {
@ -51,13 +51,13 @@ func ParseDeposit(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not get deposit sender: %w", err)
}
ev.from, err = util.Uint160DecodeBytesBE(from)
ev.FromValue, err = util.Uint160DecodeBytesBE(from)
if err != nil {
return nil, fmt.Errorf("could not convert deposit sender to uint160: %w", err)
}
// parse amount
ev.amount, err = client.IntFromStackItem(params[1])
ev.AmountValue, err = client.IntFromStackItem(params[1])
if err != nil {
return nil, fmt.Errorf("could not get deposit amount: %w", err)
}
@ -68,13 +68,13 @@ func ParseDeposit(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not get deposit receiver: %w", err)
}
ev.to, err = util.Uint160DecodeBytesBE(to)
ev.ToValue, err = util.Uint160DecodeBytesBE(to)
if err != nil {
return nil, fmt.Errorf("could not convert deposit receiver to uint160: %w", err)
}
// parse id
ev.id, err = client.BytesFromStackItem(params[3])
ev.IDValue, err = client.BytesFromStackItem(params[3])
if err != nil {
return nil, fmt.Errorf("could not get deposit id: %w", err)
}

View file

@ -77,10 +77,10 @@ func TestParseDeposit(t *testing.T) {
require.NoError(t, err)
require.Equal(t, Deposit{
id: id,
amount: amount,
from: from,
to: to,
IDValue: id,
AmountValue: amount,
FromValue: from,
ToValue: to,
}, ev)
})
}

View file

@ -8,7 +8,7 @@ import (
)
type Unbind struct {
bindCommon
BindCommon
}
func ParseUnbind(e *state.ContainedNotificationEvent) (event.Event, error) {
@ -22,12 +22,12 @@ func ParseUnbind(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not parse stack items from notify event: %w", err)
}
err = parseBind(&ev.bindCommon, params)
err = parseBind(&ev.BindCommon, params)
if err != nil {
return nil, err
}
ev.txHash = e.Container
ev.TxHashValue = e.Container
return ev, nil
}

View file

@ -11,22 +11,22 @@ import (
// Withdraw structure of frostfs.Withdraw notification from mainnet chain.
type Withdraw struct {
id []byte
amount int64 // Fixed8
user util.Uint160
IDValue []byte
AmountValue int64 // Fixed8
UserValue util.Uint160
}
// MorphEvent implements Neo:Morph Event interface.
func (Withdraw) MorphEvent() {}
// ID is a withdraw transaction hash.
func (w Withdraw) ID() []byte { return w.id }
func (w Withdraw) ID() []byte { return w.IDValue }
// User returns withdraw receiver script hash from main net.
func (w Withdraw) User() util.Uint160 { return w.user }
func (w Withdraw) User() util.Uint160 { return w.UserValue }
// Amount of the withdraw assets.
func (w Withdraw) Amount() int64 { return w.amount }
func (w Withdraw) Amount() int64 { return w.AmountValue }
// ParseWithdraw notification into withdraw structure.
func ParseWithdraw(e *state.ContainedNotificationEvent) (event.Event, error) {
@ -47,19 +47,19 @@ func ParseWithdraw(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not get withdraw user: %w", err)
}
ev.user, err = util.Uint160DecodeBytesBE(user)
ev.UserValue, err = util.Uint160DecodeBytesBE(user)
if err != nil {
return nil, fmt.Errorf("could not convert withdraw user to uint160: %w", err)
}
// parse amount
ev.amount, err = client.IntFromStackItem(params[1])
ev.AmountValue, err = client.IntFromStackItem(params[1])
if err != nil {
return nil, fmt.Errorf("could not get withdraw amount: %w", err)
}
// parse id
ev.id, err = client.BytesFromStackItem(params[2])
ev.IDValue, err = client.BytesFromStackItem(params[2])
if err != nil {
return nil, fmt.Errorf("could not get withdraw id: %w", err)
}

View file

@ -64,9 +64,9 @@ func TestParseWithdraw(t *testing.T) {
require.NoError(t, err)
require.Equal(t, Withdraw{
id: id,
amount: amount,
user: user,
IDValue: id,
AmountValue: amount,
UserValue: user,
}, ev)
})
}

View file

@ -10,24 +10,24 @@ import (
)
type AddPeer struct {
node []byte
NodeBytes []byte
// For notary notifications only.
// Contains raw transactions of notary request.
notaryRequest *payload.P2PNotaryRequest
Request *payload.P2PNotaryRequest
}
// MorphEvent implements Neo:Morph Event interface.
func (AddPeer) MorphEvent() {}
func (s AddPeer) Node() []byte {
return s.node
return s.NodeBytes
}
// NotaryRequest returns raw notary request if notification
// was received via notary service. Otherwise, returns nil.
func (s AddPeer) NotaryRequest() *payload.P2PNotaryRequest {
return s.notaryRequest
return s.Request
}
const expectedItemNumAddPeer = 1
@ -47,7 +47,7 @@ func ParseAddPeer(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, event.WrongNumberOfParameters(expectedItemNumAddPeer, ln)
}
ev.node, err = client.BytesFromStackItem(params[0])
ev.NodeBytes, err = client.BytesFromStackItem(params[0])
if err != nil {
return nil, fmt.Errorf("could not get raw nodeinfo: %w", err)
}

View file

@ -7,7 +7,7 @@ import (
func (s *AddPeer) setNode(v []byte) {
if v != nil {
s.node = v
s.NodeBytes = v
}
}
@ -43,7 +43,7 @@ func ParseAddPeerNotary(ne event.NotaryEvent) (event.Event, error) {
}
}
ev.notaryRequest = ne.Raw()
ev.Request = ne.Raw()
return ev, nil
}

View file

@ -37,7 +37,7 @@ func TestParseAddPeer(t *testing.T) {
require.NoError(t, err)
require.Equal(t, AddPeer{
node: info,
NodeBytes: info,
}, ev)
})
}

View file

@ -11,12 +11,12 @@ import (
// NewEpoch is a new epoch Neo:Morph event.
type NewEpoch struct {
num uint64
Num uint64
// txHash is used in notary environmental
// Hash is used in notary environmental
// for calculating unique but same for
// all notification receivers values.
txHash util.Uint256
Hash util.Uint256
}
// MorphEvent implements Neo:Morph Event interface.
@ -24,13 +24,13 @@ func (NewEpoch) MorphEvent() {}
// EpochNumber returns new epoch number.
func (s NewEpoch) EpochNumber() uint64 {
return s.num
return s.Num
}
// TxHash returns hash of the TX with new epoch
// notification.
func (s NewEpoch) TxHash() util.Uint256 {
return s.txHash
return s.Hash
}
// ParseNewEpoch is a parser of new epoch notification event.
@ -52,7 +52,7 @@ func ParseNewEpoch(e *state.ContainedNotificationEvent) (event.Event, error) {
}
return NewEpoch{
num: uint64(prmEpochNum),
txHash: e.Container,
Num: uint64(prmEpochNum),
Hash: e.Container,
}, nil
}

View file

@ -37,7 +37,7 @@ func TestParseNewEpoch(t *testing.T) {
require.NoError(t, err)
require.Equal(t, NewEpoch{
num: epochNum,
Num: epochNum,
}, ev)
})
}

View file

@ -13,13 +13,13 @@ import (
)
type UpdatePeer struct {
publicKey *keys.PublicKey
PubKey *keys.PublicKey
state netmap.NodeState
State netmap.NodeState
// For notary notifications only.
// Contains raw transactions of notary request.
notaryRequest *payload.P2PNotaryRequest
Request *payload.P2PNotaryRequest
}
// MorphEvent implements Neo:Morph Event interface.
@ -28,27 +28,27 @@ func (UpdatePeer) MorphEvent() {}
// Online returns true if node's state is requested to be switched
// to "online".
func (s UpdatePeer) Online() bool {
return s.state == netmap.NodeStateOnline
return s.State == netmap.NodeStateOnline
}
// Maintenance returns true if node's state is requested to be switched
// to "maintenance".
func (s UpdatePeer) Maintenance() bool {
return s.state == netmap.NodeStateMaintenance
return s.State == netmap.NodeStateMaintenance
}
func (s UpdatePeer) PublicKey() *keys.PublicKey {
return s.publicKey
return s.PubKey
}
// NotaryRequest returns raw notary request if notification
// was received via notary service. Otherwise, returns nil.
func (s UpdatePeer) NotaryRequest() *payload.P2PNotaryRequest {
return s.notaryRequest
return s.Request
}
func (s *UpdatePeer) decodeState(state int64) error {
switch s.state = netmap.NodeState(state); s.state {
switch s.State = netmap.NodeState(state); s.State {
default:
return fmt.Errorf("unsupported node state %d", state)
case
@ -82,7 +82,7 @@ func ParseUpdatePeer(e *state.ContainedNotificationEvent) (event.Event, error) {
return nil, fmt.Errorf("could not get public key: %w", err)
}
ev.publicKey, err = keys.NewPublicKeyFromBytes(key, elliptic.P256())
ev.PubKey, err = keys.NewPublicKeyFromBytes(key, elliptic.P256())
if err != nil {
return nil, fmt.Errorf("could not parse public key: %w", err)
}

View file

@ -17,7 +17,7 @@ func (s *UpdatePeer) setPublicKey(v []byte) (err error) {
return errNilPubKey
}
s.publicKey, err = keys.NewPublicKeyFromBytes(v, elliptic.P256())
s.PubKey, err = keys.NewPublicKeyFromBytes(v, elliptic.P256())
if err != nil {
return fmt.Errorf("could not parse public key: %w", err)
}
@ -73,7 +73,7 @@ func ParseUpdatePeerNotary(ne event.NotaryEvent) (event.Event, error) {
}
}
ev.notaryRequest = ne.Raw()
ev.Request = ne.Raw()
return ev, nil
}

View file

@ -52,8 +52,8 @@ func TestParseUpdatePeer(t *testing.T) {
require.NoError(t, err)
require.Equal(t, UpdatePeer{
publicKey: publicKey,
state: state,
PubKey: publicKey,
State: state,
}, ev)
})
}