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" return "unknown"
} }
type alphabetContracts map[GlagoliticLetter]util.Uint160 type AlphabetContracts map[GlagoliticLetter]util.Uint160
func newAlphabetContracts() alphabetContracts { func NewAlphabetContracts() AlphabetContracts {
return make(map[GlagoliticLetter]util.Uint160, lastLetterNum) return make(map[GlagoliticLetter]util.Uint160, lastLetterNum)
} }
func (a alphabetContracts) GetByIndex(ind int) (util.Uint160, bool) { func (a AlphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
if ind < 0 || ind >= int(lastLetterNum) { if ind < 0 || ind >= int(lastLetterNum) {
return util.Uint160{}, false return util.Uint160{}, false
} }
@ -120,16 +120,16 @@ func (a alphabetContracts) GetByIndex(ind int) (util.Uint160, bool) {
return contract, ok return contract, ok
} }
func (a alphabetContracts) indexOutOfRange(ind int) bool { func (a AlphabetContracts) indexOutOfRange(ind int) bool {
return ind < 0 && ind >= len(a) return ind < 0 && ind >= len(a)
} }
func (a alphabetContracts) iterate(f func(GlagoliticLetter, util.Uint160)) { func (a AlphabetContracts) iterate(f func(GlagoliticLetter, util.Uint160)) {
for letter, contract := range a { for letter, contract := range a {
f(letter, contract) f(letter, contract)
} }
} }
func (a *alphabetContracts) set(l GlagoliticLetter, h util.Uint160) { func (a *AlphabetContracts) set(l GlagoliticLetter, h util.Uint160) {
(*a)[l] = h (*a)[l] = h
} }

View file

@ -33,6 +33,10 @@ type (
newEpochHandler func() newEpochHandler func()
containerEstimationStopper interface {
StopEstimation(p container.StopEstimationPrm) error
}
epochTimerArgs struct { epochTimerArgs struct {
l *logger.Logger l *logger.Logger
@ -40,7 +44,7 @@ type (
newEpochHandlers []newEpochHandler newEpochHandlers []newEpochHandler
cnrWrapper *container.Client // to invoke stop container estimation cnrWrapper containerEstimationStopper // to invoke stop container estimation
epoch epochState // to specify which epoch to stop, and epoch duration epoch epochState // to specify which epoch to stop, and epoch duration
stopEstimationDMul uint32 // X: X/Y of epoch in blocks stopEstimationDMul uint32 // X: 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 processing util.Uint160 // in mainnet
frostfsID util.Uint160 // in morph frostfsID util.Uint160 // in morph
alphabet alphabetContracts // in morph alphabet AlphabetContracts // in morph
} }
func parseContracts(cfg *viper.Viper, morph *client.Client, withoutMainNet, withoutMainNotary, withoutSideNotary bool) (*contracts, error) { func parseContracts(cfg *viper.Viper, morph nnsResolver, withoutMainNet, withoutMainNotary, withoutSideNotary bool) (*contracts, error) {
var ( var (
result = new(contracts) result = new(contracts)
err error err error
@ -78,9 +78,9 @@ func parseContracts(cfg *viper.Viper, morph *client.Client, withoutMainNet, with
return result, nil 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")) num := GlagoliticLetter(cfg.GetUint("contracts.alphabet.amount"))
alpha := newAlphabetContracts() alpha := NewAlphabetContracts()
if num > lastLetterNum { if num > lastLetterNum {
return nil, fmt.Errorf("amount of alphabet contracts overflows glagolitsa %d > %d", num, lastLetterNum) return nil, fmt.Errorf("amount of alphabet contracts overflows glagolitsa %d > %d", num, lastLetterNum)
@ -117,7 +117,7 @@ func parseAlphabetContracts(cfg *viper.Viper, morph *client.Client) (alphabetCon
return alpha, nil 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) contractStr := cfg.GetString(cfgName)
if len(contractStr) == 0 { if len(contractStr) == 0 {
return morph.NNSContractAddress(nnsName) return morph.NNSContractAddress(nnsName)
@ -125,3 +125,7 @@ func parseContract(cfg *viper.Viper, morph *client.Client, cfgName, nnsName stri
return util.Uint160DecodeStringLE(contractStr) 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{ s.netmapProcessor, err = netmap.New(&netmap.Params{
Log: s.log, Log: s.log,
PoolSize: cfg.GetInt("workers.netmap"), PoolSize: cfg.GetInt("workers.netmap"),
NetmapClient: s.netmapClient, NetmapClient: netmap.NewNetmapClient(s.netmapClient),
EpochTimer: s, EpochTimer: s,
EpochState: s, EpochState: s,
AlphabetState: 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 ( import (
"errors" "errors"
"fmt" "fmt"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
"go.uber.org/zap" "go.uber.org/zap"
@ -32,14 +33,24 @@ type (
GetByIndex(int) (util.Uint160, bool) GetByIndex(int) (util.Uint160, bool)
} }
netmapClient interface {
NetMap() (*netmap.NetMap, error)
}
morphClient interface {
Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) error
TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error
BatchTransferGas(receivers []util.Uint160, amount fixedn.Fixed8) error
}
// Processor of events produced for alphabet contracts in the sidechain. // Processor of events produced for alphabet contracts in the sidechain.
Processor struct { Processor struct {
parsedWallets []util.Uint160 parsedWallets []util.Uint160
log *logger.Logger log *logger.Logger
pool *ants.Pool pool *ants.Pool
alphabetContracts Contracts alphabetContracts Contracts
netmapClient *nmClient.Client netmapClient netmapClient
morphClient *client.Client morphClient morphClient
irList Indexer irList Indexer
storageEmission uint64 storageEmission uint64
} }
@ -50,8 +61,8 @@ type (
Log *logger.Logger Log *logger.Logger
PoolSize int PoolSize int
AlphabetContracts Contracts AlphabetContracts Contracts
NetmapClient *nmClient.Client NetmapClient netmapClient
MorphClient *client.Client MorphClient morphClient
IRList Indexer IRList Indexer
StorageEmission uint64 StorageEmission uint64
} }
@ -106,3 +117,11 @@ func (ap *Processor) ListenerNotaryParsers() []event.NotaryParserInfo {
func (ap *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo { func (ap *Processor) ListenerNotaryHandlers() []event.NotaryHandlerInfo {
return nil return nil
} }
// WaitPoolRunning waits while pool has running tasks
// For use in test only.
func (ap *Processor) WaitPoolRunning() {
for ap.pool.Running() > 0 {
time.Sleep(10 * time.Millisecond)
}
}

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

View file

@ -36,7 +36,7 @@ func (cp *Processor) handleDelete(ev event.Event) {
// send an event to the worker pool // 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 { if err != nil {
// there system can be moved into controlled degradation stage // there system can be moved into controlled degradation stage
cp.log.Warn(logs.ContainerContainerProcessorWorkerPoolDrained, 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 // Process delete container operation from the user by checking container sanity
// and sending approve tx back to morph. // 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() { if !cp.alphabetState.IsAlphabet() {
cp.log.Info(logs.ContainerNonAlphabetModeIgnoreContainerDelete) cp.log.Info(logs.ContainerNonAlphabetModeIgnoreContainerDelete)
return return
@ -138,7 +138,7 @@ func (cp *Processor) processContainerDelete(e *containerEvent.Delete) {
cp.approveDeleteContainer(e) cp.approveDeleteContainer(e)
} }
func (cp *Processor) checkDeleteContainer(e *containerEvent.Delete) error { func (cp *Processor) checkDeleteContainer(e containerEvent.Delete) error {
binCnr := e.ContainerID() binCnr := e.ContainerID()
var idCnr cid.ID var idCnr cid.ID
@ -170,7 +170,7 @@ func (cp *Processor) checkDeleteContainer(e *containerEvent.Delete) error {
return nil return nil
} }
func (cp *Processor) approveDeleteContainer(e *containerEvent.Delete) { func (cp *Processor) approveDeleteContainer(e containerEvent.Delete) {
var err error var err error
prm := cntClient.DeletePrm{} prm := cntClient.DeletePrm{}

View file

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

View file

@ -5,12 +5,16 @@ import (
"fmt" "fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/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/client/frostfsid"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
containerEvent "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-node/pkg/util/logger" "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/core/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -21,13 +25,26 @@ type (
IsAlphabet() bool 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 of events produced by container contract in the sidechain.
Processor struct { Processor struct {
log *logger.Logger log *logger.Logger
pool *ants.Pool pool *ants.Pool
alphabetState AlphabetState alphabetState AlphabetState
cnrClient *container.Client // notary must be enabled cnrClient ContClient // notary must be enabled
idClient *frostfsid.Client idClient IDClient
netState NetworkState netState NetworkState
notaryDisabled bool notaryDisabled bool
} }
@ -37,8 +54,8 @@ type (
Log *logger.Logger Log *logger.Logger
PoolSize int PoolSize int
AlphabetState AlphabetState AlphabetState AlphabetState
ContainerClient *container.Client ContainerClient ContClient
FrostFSIDClient *frostfsid.Client FrostFSIDClient IDClient
NetworkState NetworkState NetworkState NetworkState
NotaryDisabled bool NotaryDisabled bool
} }

View file

@ -18,7 +18,7 @@ func (np *Processor) handleDeposit(ev event.Event) {
// send event to the worker pool // 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 { if err != nil {
// there system can be moved into controlled degradation stage // there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained, np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -34,7 +34,7 @@ func (np *Processor) handleWithdraw(ev event.Event) {
// send event to the worker pool // 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 { if err != nil {
// there system can be moved into controlled degradation stage // there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained, np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -50,7 +50,7 @@ func (np *Processor) handleCheque(ev event.Event) {
// send event to the worker pool // 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 { if err != nil {
// there system can be moved into controlled degradation stage // there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained, np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -67,7 +67,7 @@ func (np *Processor) handleConfig(ev event.Event) {
// send event to the worker pool // 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 { if err != nil {
// there system can be moved into controlled degradation stage // there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained, np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -83,7 +83,7 @@ func (np *Processor) handleBind(ev event.Event) {
// send event to the worker pool // 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 { if err != nil {
// there system can be moved into controlled degradation stage // there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained, np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained,
@ -99,7 +99,7 @@ func (np *Processor) handleUnbind(ev event.Event) {
// send event to the worker pool // 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 { if err != nil {
// there system can be moved into controlled degradation stage // there system can be moved into controlled degradation stage
np.log.Warn(logs.FrostFSFrostfsProcessorWorkerPoolDrained, 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 // Process deposit event by invoking a balance contract and sending native
// gas in the sidechain. // gas in the sidechain.
func (np *Processor) processDeposit(deposit *frostfsEvent.Deposit) { func (np *Processor) processDeposit(deposit frostfsEvent.Deposit) {
if !np.alphabetState.IsAlphabet() { if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreDeposit) np.log.Info(logs.FrostFSNonAlphabetModeIgnoreDeposit)
return return
@ -80,7 +80,7 @@ func (np *Processor) processDeposit(deposit *frostfsEvent.Deposit) {
} }
// Process withdraw event by locking assets in the balance account. // 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() { if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreWithdraw) np.log.Info(logs.FrostFSNonAlphabetModeIgnoreWithdraw)
return return
@ -111,7 +111,7 @@ func (np *Processor) processWithdraw(withdraw *frostfsEvent.Withdraw) {
// Process cheque event by transferring assets from the lock account back to // Process cheque event by transferring assets from the lock account back to
// the reserve account. // the reserve account.
func (np *Processor) processCheque(cheque *frostfsEvent.Cheque) { func (np *Processor) processCheque(cheque frostfsEvent.Cheque) {
if !np.alphabetState.IsAlphabet() { if !np.alphabetState.IsAlphabet() {
np.log.Info(logs.FrostFSNonAlphabetModeIgnoreCheque) np.log.Info(logs.FrostFSNonAlphabetModeIgnoreCheque)
return return

View file

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

View file

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

View file

@ -6,7 +6,6 @@ import (
"sync" "sync"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/balance" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/balance"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
@ -36,14 +35,34 @@ type (
ToBalancePrecision(int64) int64 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 of events produced by frostfs contract in main net.
Processor struct { Processor struct {
log *logger.Logger log *logger.Logger
pool *ants.Pool pool *ants.Pool
frostfsContract util.Uint160 frostfsContract util.Uint160
balanceClient *balance.Client balanceClient BalanceClient
netmapClient *nmClient.Client netmapClient NetmapClient
morphClient *client.Client morphClient MorphClient
epochState EpochState epochState EpochState
alphabetState AlphabetState alphabetState AlphabetState
converter PrecisionConverter converter PrecisionConverter
@ -52,8 +71,7 @@ type (
mintEmitThreshold uint64 mintEmitThreshold uint64
mintEmitValue fixedn.Fixed8 mintEmitValue fixedn.Fixed8
gasBalanceThreshold int64 gasBalanceThreshold int64
frostfsIDClient IDClient
frostfsIDClient *frostfsid.Client
} }
// Params of the processor constructor. // Params of the processor constructor.
@ -61,10 +79,10 @@ type (
Log *logger.Logger Log *logger.Logger
PoolSize int PoolSize int
FrostFSContract util.Uint160 FrostFSContract util.Uint160
FrostFSIDClient *frostfsid.Client FrostFSIDClient IDClient
BalanceClient *balance.Client BalanceClient BalanceClient
NetmapClient *nmClient.Client NetmapClient NetmapClient
MorphClient *client.Client MorphClient MorphClient
EpochState EpochState EpochState EpochState
AlphabetState AlphabetState AlphabetState AlphabetState
Converter PrecisionConverter 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) 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 of events related to governance in the network.
Processor struct { Processor struct {
log *logger.Logger log *logger.Logger
pool *ants.Pool pool *ants.Pool
frostfsClient *frostfscontract.Client frostfsClient FrostFSClient
netmapClient *nmClient.Client netmapClient NetmapClient
alphabetState AlphabetState alphabetState AlphabetState
epochState EpochState epochState EpochState
voter Voter voter Voter
irFetcher IRFetcher irFetcher IRFetcher
mainnetClient *client.Client mainnetClient MainnetClient
morphClient *client.Client morphClient MorphClient
notaryDisabled bool notaryDisabled bool
@ -82,10 +101,10 @@ type (
Voter Voter Voter Voter
IRFetcher IRFetcher IRFetcher IRFetcher
MorphClient *client.Client MorphClient MorphClient
MainnetClient *client.Client MainnetClient MainnetClient
FrostFSClient *frostfscontract.Client FrostFSClient FrostFSClient
NetmapClient *nmClient.Client NetmapClient NetmapClient
NotaryDisabled bool 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) err = np.netmapClient.UpdatePeerState(prm)
} else { } else {
err = np.netmapClient.Morph().NotaryInvoke( err = np.netmapClient.MorphNotaryInvoke(
np.netmapClient.ContractAddress(), np.netmapClient.ContractAddress(),
0, 0,
uint32(ev.epoch), uint32(ev.epoch),

View file

@ -25,7 +25,7 @@ func (np *Processor) processNewEpoch(ev netmapEvent.NewEpoch) {
np.epochState.SetEpochCounter(epoch) np.epochState.SetEpochCounter(epoch)
h, err := np.netmapClient.Morph().TxHeight(ev.TxHash()) h, err := np.netmapClient.MorphTxHeight(ev.TxHash())
if err != nil { if err != nil {
np.log.Warn(logs.NetmapCantGetTransactionHeight, np.log.Warn(logs.NetmapCantGetTransactionHeight,
zap.String("hash", ev.TxHash().StringLE()), 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 // check if notary transaction is valid, see #976
if originalRequest := ev.NotaryRequest(); originalRequest != nil { if originalRequest := ev.NotaryRequest(); originalRequest != nil {
tx := originalRequest.MainTransaction 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 { if err != nil || !ok {
np.log.Warn(logs.NetmapNonhaltNotaryTransaction, np.log.Warn(logs.NetmapNonhaltNotaryTransaction,
zap.String("method", "netmap.AddPeer"), zap.String("method", "netmap.AddPeer"),
@ -73,7 +73,7 @@ func (np *Processor) processAddPeer(ev netmapEvent.AddPeer) {
if nr := ev.NotaryRequest(); nr != nil { if nr := ev.NotaryRequest(); nr != nil {
// create new notary request with the original nonce // create new notary request with the original nonce
err = np.netmapClient.Morph().NotaryInvoke( err = np.netmapClient.MorphNotaryInvoke(
np.netmapClient.ContractAddress(), np.netmapClient.ContractAddress(),
0, 0,
nr.MainTransaction.Nonce, nr.MainTransaction.Nonce,
@ -117,7 +117,7 @@ func (np *Processor) processUpdatePeer(ev netmapEvent.UpdatePeer) {
} }
if nr := ev.NotaryRequest(); nr != nil { if nr := ev.NotaryRequest(); nr != nil {
err = np.netmapClient.Morph().NotarySignAndInvokeTX(nr.MainTransaction) err = np.netmapClient.MorphNotarySignAndInvokeTX(nr.MainTransaction)
} else { } else {
prm := netmapclient.UpdatePeerPrm{} prm := netmapclient.UpdatePeerPrm{}

View file

@ -6,13 +6,16 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "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/innerring/processors/netmap/nodevalidation/state"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container" cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container"
nmClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap" netmapclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
netmapEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/netmap" 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-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "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/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" "github.com/panjf2000/ants/v2"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -51,6 +54,23 @@ type (
VerifyAndUpdate(*netmap.NodeInfo) error 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 // Processor of events produced by network map contract
// and new epoch ticker, because it is related to contract. // and new epoch ticker, because it is related to contract.
Processor struct { Processor struct {
@ -60,8 +80,8 @@ type (
epochState EpochState epochState EpochState
alphabetState AlphabetState alphabetState AlphabetState
netmapClient *nmClient.Client netmapClient Client
containerWrp *container.Client containerWrp ContainerClient
netmapSnapshot cleanupTable netmapSnapshot cleanupTable
@ -81,13 +101,13 @@ type (
Params struct { Params struct {
Log *logger.Logger Log *logger.Logger
PoolSize int PoolSize int
NetmapClient *nmClient.Client NetmapClient Client
EpochTimer EpochTimerReseter EpochTimer EpochTimerReseter
EpochState EpochState EpochState EpochState
AlphabetState AlphabetState AlphabetState AlphabetState
CleanupEnabled bool CleanupEnabled bool
CleanupThreshold uint64 // in epochs CleanupThreshold uint64 // in epochs
ContainerWrapper *container.Client ContainerWrapper ContainerClient
HandleAudit event.Handler HandleAudit event.Handler
AuditSettlementsHandler 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) return (*containerSource)(w)
} }
type getContainer interface {
Get(cid []byte) (*containercore.Container, error)
}
// Get marshals container ID, and passes it to Wrapper's Get method. // 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) binCnr := make([]byte, sha256.Size)
cnr.Encode(binCnr) cnr.Encode(binCnr)

View file

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

View file

@ -7,19 +7,19 @@ import (
func (d *Delete) setContainerID(v []byte) { func (d *Delete) setContainerID(v []byte) {
if v != nil { if v != nil {
d.containerID = v d.ContainerIDValue = v
} }
} }
func (d *Delete) setSignature(v []byte) { func (d *Delete) setSignature(v []byte) {
if v != nil { if v != nil {
d.signature = v d.SignatureValue = v
} }
} }
func (d *Delete) setToken(v []byte) { func (d *Delete) setToken(v []byte) {
if v != nil { 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 return ev, nil
} }

View file

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

View file

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

View file

@ -7,25 +7,25 @@ import (
func (x *SetEACL) setTable(v []byte) { func (x *SetEACL) setTable(v []byte) {
if v != nil { if v != nil {
x.table = v x.TableValue = v
} }
} }
func (x *SetEACL) setSignature(v []byte) { func (x *SetEACL) setSignature(v []byte) {
if v != nil { if v != nil {
x.signature = v x.SignatureValue = v
} }
} }
func (x *SetEACL) setPublicKey(v []byte) { func (x *SetEACL) setPublicKey(v []byte) {
if v != nil { if v != nil {
x.publicKey = v x.PublicKeyValue = v
} }
} }
func (x *SetEACL) setToken(v []byte) { func (x *SetEACL) setToken(v []byte) {
if v != nil { 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 return ev, nil
} }

View file

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

View file

@ -11,26 +11,26 @@ import (
// Cheque structure of frostfs.Cheque notification from mainnet chain. // Cheque structure of frostfs.Cheque notification from mainnet chain.
type Cheque struct { type Cheque struct {
id []byte IDValue []byte
amount int64 // Fixed8 AmountValue int64 // Fixed8
user util.Uint160 UserValue util.Uint160
lock util.Uint160 LockValue util.Uint160
} }
// MorphEvent implements Neo:Morph Event interface. // MorphEvent implements Neo:Morph Event interface.
func (Cheque) MorphEvent() {} func (Cheque) MorphEvent() {}
// ID is a withdraw transaction hash. // 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. // 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. // 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. // 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. // ParseCheque from notification into cheque structure.
func ParseCheque(e *state.ContainedNotificationEvent) (event.Event, error) { func ParseCheque(e *state.ContainedNotificationEvent) (event.Event, error) {
@ -49,7 +49,7 @@ func ParseCheque(e *state.ContainedNotificationEvent) (event.Event, error) {
} }
// parse id // parse id
ev.id, err = client.BytesFromStackItem(params[0]) ev.IDValue, err = client.BytesFromStackItem(params[0])
if err != nil { if err != nil {
return nil, fmt.Errorf("could not get cheque id: %w", err) 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) 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 { if err != nil {
return nil, fmt.Errorf("could not convert cheque user to uint160: %w", err) return nil, fmt.Errorf("could not convert cheque user to uint160: %w", err)
} }
// parse amount // parse amount
ev.amount, err = client.IntFromStackItem(params[2]) ev.AmountValue, err = client.IntFromStackItem(params[2])
if err != nil { if err != nil {
return nil, fmt.Errorf("could not get cheque amount: %w", err) 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) 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 { if err != nil {
return nil, fmt.Errorf("could not convert cheque lock account to uint160: %w", err) 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.NoError(t, err)
require.Equal(t, Cheque{ require.Equal(t, Cheque{
id: id, IDValue: id,
amount: amount, AmountValue: amount,
user: user, UserValue: user,
lock: lock, LockValue: lock,
}, ev) }, ev)
}) })
} }

View file

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

View file

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

View file

@ -11,26 +11,26 @@ import (
// Deposit structure of frostfs.Deposit notification from mainnet chain. // Deposit structure of frostfs.Deposit notification from mainnet chain.
type Deposit struct { type Deposit struct {
id []byte IDValue []byte
amount int64 // Fixed8 AmountValue int64 // Fixed8
from util.Uint160 FromValue util.Uint160
to util.Uint160 ToValue util.Uint160
} }
// MorphEvent implements Neo:Morph Event interface. // MorphEvent implements Neo:Morph Event interface.
func (Deposit) MorphEvent() {} func (Deposit) MorphEvent() {}
// ID is a deposit transaction hash. // 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. // 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. // 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. // 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. // ParseDeposit notification into deposit structure.
func ParseDeposit(e *state.ContainedNotificationEvent) (event.Event, error) { 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) 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 { if err != nil {
return nil, fmt.Errorf("could not convert deposit sender to uint160: %w", err) return nil, fmt.Errorf("could not convert deposit sender to uint160: %w", err)
} }
// parse amount // parse amount
ev.amount, err = client.IntFromStackItem(params[1]) ev.AmountValue, err = client.IntFromStackItem(params[1])
if err != nil { if err != nil {
return nil, fmt.Errorf("could not get deposit amount: %w", err) 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) 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 { if err != nil {
return nil, fmt.Errorf("could not convert deposit receiver to uint160: %w", err) return nil, fmt.Errorf("could not convert deposit receiver to uint160: %w", err)
} }
// parse id // parse id
ev.id, err = client.BytesFromStackItem(params[3]) ev.IDValue, err = client.BytesFromStackItem(params[3])
if err != nil { if err != nil {
return nil, fmt.Errorf("could not get deposit id: %w", err) 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.NoError(t, err)
require.Equal(t, Deposit{ require.Equal(t, Deposit{
id: id, IDValue: id,
amount: amount, AmountValue: amount,
from: from, FromValue: from,
to: to, ToValue: to,
}, ev) }, ev)
}) })
} }

View file

@ -8,7 +8,7 @@ import (
) )
type Unbind struct { type Unbind struct {
bindCommon BindCommon
} }
func ParseUnbind(e *state.ContainedNotificationEvent) (event.Event, error) { 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) 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 { if err != nil {
return nil, err return nil, err
} }
ev.txHash = e.Container ev.TxHashValue = e.Container
return ev, nil return ev, nil
} }

View file

@ -11,22 +11,22 @@ import (
// Withdraw structure of frostfs.Withdraw notification from mainnet chain. // Withdraw structure of frostfs.Withdraw notification from mainnet chain.
type Withdraw struct { type Withdraw struct {
id []byte IDValue []byte
amount int64 // Fixed8 AmountValue int64 // Fixed8
user util.Uint160 UserValue util.Uint160
} }
// MorphEvent implements Neo:Morph Event interface. // MorphEvent implements Neo:Morph Event interface.
func (Withdraw) MorphEvent() {} func (Withdraw) MorphEvent() {}
// ID is a withdraw transaction hash. // 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. // 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. // 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. // ParseWithdraw notification into withdraw structure.
func ParseWithdraw(e *state.ContainedNotificationEvent) (event.Event, error) { 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) 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 { if err != nil {
return nil, fmt.Errorf("could not convert withdraw user to uint160: %w", err) return nil, fmt.Errorf("could not convert withdraw user to uint160: %w", err)
} }
// parse amount // parse amount
ev.amount, err = client.IntFromStackItem(params[1]) ev.AmountValue, err = client.IntFromStackItem(params[1])
if err != nil { if err != nil {
return nil, fmt.Errorf("could not get withdraw amount: %w", err) return nil, fmt.Errorf("could not get withdraw amount: %w", err)
} }
// parse id // parse id
ev.id, err = client.BytesFromStackItem(params[2]) ev.IDValue, err = client.BytesFromStackItem(params[2])
if err != nil { if err != nil {
return nil, fmt.Errorf("could not get withdraw id: %w", err) 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.NoError(t, err)
require.Equal(t, Withdraw{ require.Equal(t, Withdraw{
id: id, IDValue: id,
amount: amount, AmountValue: amount,
user: user, UserValue: user,
}, ev) }, ev)
}) })
} }

View file

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

View file

@ -7,7 +7,7 @@ import (
func (s *AddPeer) setNode(v []byte) { func (s *AddPeer) setNode(v []byte) {
if v != nil { 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 return ev, nil
} }

View file

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

View file

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

View file

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

View file

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

View file

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