Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cmd/neofs-ir/defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,10 @@ func TestValidateDefaultConfig(t *testing.T) {
Settlement: config.Settlement{
BasicIncomeRate: 0,
},
Experimental: config.Experimental{ChainMetaData: false, AllowEC: false}})
Experimental: config.Experimental{ChainMetaData: config.Metadata{
Enabled: false,
SeedPort: 0,
RPCPort: 0,
P2PPort: 0,
}, AllowEC: false}})
}
9 changes: 7 additions & 2 deletions cmd/neofs-ir/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,13 @@ func TestCheckForUnknownFieldsExample(t *testing.T) {
BasicIncomeRate: 100,
},
Experimental: config.Experimental{
ChainMetaData: false,
AllowEC: true,
ChainMetaData: config.Metadata{
Enabled: false,
SeedPort: 20334,
RPCPort: 30334,
P2PPort: 20334,
},
AllowEC: true,
},
Validator: config.Validator{
Enabled: true,
Expand Down
6 changes: 5 additions & 1 deletion config/example/ir.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ settlement:
basic_income_rate: 100 # Optional: override basic income rate value from network config; applied only in debug mode

experimental:
chain_meta_data: false # Optional: allows creating containers with meta data handled via FS chain
chain_meta_data: # Optional: allows creating containers with meta data handled via FS chain, only supported with fschain.consensus configured
enabled: false
seed_port: 20334 # List of existing nodes to communicate with over Neo P2P protocol (addresses are taken from fschain.consensus.seed_nodes, but ports are changed to this value)
p2p_port: 20334 # Port for network addresses to listen Neo P2P on (addresses are taken from fschain.consensus.p2p, but ports are changed to this value)
rpc_port: 30334 # Port for network addresses to listen Neo RPC on (addresses are taken from fschain.consensus.rpc, but ports are changed to this value)
allow_ec: true # Optional: allows creating containers with EC rules

sn_validator:
Expand Down
12 changes: 10 additions & 2 deletions pkg/innerring/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,16 @@ type Settlement struct {

// Experimental configures experimental features.
type Experimental struct {
ChainMetaData bool `mapstructure:"chain_meta_data"`
AllowEC bool `mapstructure:"allow_ec"`
ChainMetaData Metadata `mapstructure:"chain_meta_data"`
AllowEC bool `mapstructure:"allow_ec"`
}

// Metadata configures metadata chain.
type Metadata struct {
Enabled bool `mapstructure:"enabled"`
SeedPort uint16 `mapstructure:"seed_port"`
RPCPort uint16 `mapstructure:"rpc_port"`
P2PPort uint16 `mapstructure:"p2p_port"`
}

// Mainnet configures mainnet chain settings.
Expand Down
181 changes: 175 additions & 6 deletions pkg/innerring/innerring.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,37 @@ package innerring

import (
"context"
"errors"
"fmt"
"math"
"math/big"
"net"
"path"
"slices"
"strconv"
"sync/atomic"
"time"

"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
"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/neorpc"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
sc "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/nspcc-dev/neofs-contract/deploy"
"github.com/nspcc-dev/neofs-node/internal/chaintime"
"github.com/nspcc-dev/neofs-node/misc"
metachaingas "github.com/nspcc-dev/neofs-node/pkg/core/metachain/gas"
"github.com/nspcc-dev/neofs-node/pkg/innerring/config"
"github.com/nspcc-dev/neofs-node/pkg/innerring/internal/blockchain"
"github.com/nspcc-dev/neofs-node/pkg/innerring/internal/metachain"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/alphabet"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/balance"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/container"
Expand Down Expand Up @@ -56,7 +72,8 @@ type (
Server struct {
log *zap.Logger

bc *blockchain.Blockchain
bc *blockchain.Blockchain
metaChain *blockchain.Blockchain

// event producers
fsChainListener event.Listener
Expand Down Expand Up @@ -94,7 +111,7 @@ type (
netmapProcessor *netmap.Processor
containerProcessor *container.Processor

workers []func(context.Context)
workers []func(context.Context) error

// Set of local resources that must be
// initialized at the very beginning of
Expand Down Expand Up @@ -242,7 +259,7 @@ func (s *Server) Start(ctx context.Context, intError chan<- error) (err error) {
}()
}

s.startWorkers(ctx)
s.startWorkers(ctx, intError)

return nil
}
Expand Down Expand Up @@ -273,9 +290,14 @@ func (s *Server) resetEpochTimer(lastTickHeight uint32) (uint64, error) {
return lastTickH.Timestamp + epochDuration*msInS, nil
}

func (s *Server) startWorkers(ctx context.Context) {
func (s *Server) startWorkers(ctx context.Context, intError chan<- error) {
for _, w := range s.workers {
go w(ctx)
go func() {
err := w(ctx)
if err != nil {
intError <- err
}
}()
Comment thread
roman-khimov marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -756,10 +778,143 @@ func New(ctx context.Context, log *zap.Logger, cfg *config.Config, errChan chan<
nodeValidators = append(nodeValidators, external.New(cfg.Validator.URL, server.key))
}

var metaActor *notary.Actor
if cfg.Experimental.ChainMetaData.Enabled {
if !isLocalConsensus {
return nil, errors.New("experimental meta-on-chain is not supported for non-consensus IR nodes")
}

v, err := server.fsChainClient.GetVersion()
if err != nil {
return nil, fmt.Errorf("fetching FS chain version: %w", err)
}

metaSeeds, err := changePort(cfg.FSChain.Consensus.SeedNodes, cfg.Experimental.ChainMetaData.SeedPort)
if err != nil {
return nil, fmt.Errorf("parsing consensus seed nodes: %w", err)
}
metaRPCs, err := changePort(cfg.FSChain.Consensus.RPC.Listen, cfg.Experimental.ChainMetaData.RPCPort)
if err != nil {
return nil, fmt.Errorf("parsing consensus RPCs: %w", err)
}
metaP2Ps, err := changePort(cfg.FSChain.Consensus.P2P.Listen, cfg.Experimental.ChainMetaData.P2PPort)
if err != nil {
return nil, fmt.Errorf("parsing consensus P2Ps: %w", err)
}

fsChainProtocol := v.Protocol
metaChainCfg := config.Consensus{
Magic: uint32(fsChainProtocol.Network) + 1,
Committee: fsChainProtocol.StandbyCommittee,
Storage: config.Storage{
Path: path.Join(path.Dir(cfg.FSChain.Consensus.Storage.Path), "meta_db.bolt"),
Type: dbconfig.BoltDB,
},
TimePerBlock: 50 * time.Millisecond,
MaxTimePerBlock: 20 * time.Second,
MaxTraceableBlocks: fsChainProtocol.MaxTraceableBlocks,
MaxValidUntilBlockIncrement: fsChainProtocol.MaxValidUntilBlockIncrement,
SeedNodes: metaSeeds,
Hardforks: config.Hardforks{},
ValidatorsHistory: config.ValidatorsHistory{},
RPC: config.RPC{
Listen: metaRPCs,
MaxWebSocketClients: cfg.FSChain.Consensus.RPC.MaxWebSocketClients,
SessionPoolSize: cfg.FSChain.Consensus.RPC.SessionPoolSize,
MaxGasInvoke: cfg.FSChain.Consensus.RPC.MaxGasInvoke,
},
P2P: config.P2P{
DialTimeout: cfg.FSChain.Consensus.P2P.DialTimeout,
ProtoTickInterval: cfg.FSChain.Consensus.P2P.ProtoTickInterval,
Listen: metaP2Ps,
Peers: cfg.FSChain.Consensus.P2P.Peers,
Ping: cfg.FSChain.Consensus.P2P.Ping,
},
SetRolesInGenesis: true,
KeepOnlyLatestState: false,
RemoveUntraceableBlocks: false,
P2PNotaryRequestPayloadPoolSize: 1000, // default for blockchain.New()
}

server.metaChain, err = metachain.NewMetaChain(&metaChainCfg, &cfg.Wallet, errChan, log.With(zap.String("component", "metadata chain (IR)")))
if err != nil {
return nil, fmt.Errorf("init meta sidechain blockchain: %w", err)
}
server.workers = append(server.workers, func(ctx context.Context) error {
defer server.metaChain.Stop()
return server.metaChain.Run(ctx)
})

alphabetList, err := server.fsChainClient.NeoFSAlphabetList()
if err != nil {
return nil, fmt.Errorf("fetching FS chain Alphabet: %w", err)
}
metaCli, err := server.metaChain.BuildWSClient(ctx)
if err != nil {
return nil, fmt.Errorf("build meta chain client: %w", err)
}
alphaAcc := wallet.NewAccountFromPrivateKey(server.key)
err = alphaAcc.ConvertMultisig(sc.GetMajorityHonestNodeCount(len(alphabetList)), alphabetList)
if err != nil {
return nil, fmt.Errorf("build meta committee acc: %w", err)
}
metaActor, err = notary.NewActor(metaCli, []actor.SignerAccount{
{
Signer: transaction.Signer{
Account: alphaAcc.ScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: alphaAcc,
},
}, wallet.NewAccountFromPrivateKey(server.key))
if err != nil {
return nil, fmt.Errorf("build meta committee actor: %w", err)
}

server.workers = append(server.workers, func(ctx context.Context) error {
simpleAcc := wallet.NewAccountFromPrivateKey(server.key)
simpleAccHash := simpleAcc.ScriptHash()
act, err := actor.New(metaCli, []actor.SignerAccount{{
Signer: transaction.Signer{
Account: simpleAccHash,
Scopes: transaction.CalledByEntry,
},
Account: simpleAcc,
}})
if err != nil {
return fmt.Errorf("new meta notary actor: %w", err)
}

gasAct := gas.New(act)
txHash, vub, err := gasAct.Transfer(
simpleAccHash,
notary.Hash,
big.NewInt(metachaingas.DefaultBalance*9/10), // default metadata chain balance but a little bit less
&notary.OnNEP17PaymentData{Account: &simpleAccHash, Till: math.MaxUint32})
if err != nil {
if !errors.Is(err, neorpc.ErrAlreadyExists) {
return fmt.Errorf("can't make notary deposit in meta chain: %w", err)
}
}

server.log.Debug("made meta chain notary deposit, awaiting...", zap.String("txHash", txHash.StringLE()), zap.Uint32("vub", vub))

_, err = act.WaitSuccess(ctx, txHash, vub, nil)
if err != nil {
return fmt.Errorf("waiting for meta chain notary deposit %s TX to be persisted: %w", txHash.StringLE(), err)
}
Comment thread
roman-khimov marked this conversation as resolved.

server.log.Debug("meta chain notary deposit successful", zap.String("tx_hash", txHash.StringLE()))

return nil
})
}

// create netmap processor
server.netmapProcessor, err = netmap.New(&netmap.Params{
Log: log,
PoolSize: cfg.Workers.Netmap,
MetaClient: metaActor,
NetmapClient: server.netmapClient,
EpochTimer: server,
EpochState: server,
Expand All @@ -786,8 +941,9 @@ func New(ctx context.Context, log *zap.Logger, cfg *config.Config, errChan chan<
PoolSize: cfg.Workers.Container,
AlphabetState: server,
ContainerClient: cnrClient,
MetaClient: metaActor,
NetworkState: server.netmapClient,
MetaEnabled: cfg.Experimental.ChainMetaData,
MetaEnabled: cfg.Experimental.ChainMetaData.Enabled,
AllowEC: cfg.Experimental.AllowEC,
ChainTime: &server.chainTime,
})
Expand Down Expand Up @@ -894,6 +1050,19 @@ func New(ctx context.Context, log *zap.Logger, cfg *config.Config, errChan chan<
return server, nil
}

func changePort(addrs []string, port uint16) ([]string, error) {
res := slices.Clone(addrs)
for i := range res {
host, _, err := net.SplitHostPort(res[i])
if err != nil {
return nil, fmt.Errorf("[%d] address ('%s') cannot be parsed: %w", i, res[i], err)
}
res[i] = net.JoinHostPort(host, strconv.FormatUint(uint64(port), 10))
}

return res, nil
}

func initTimers(server *Server, cfg *config.Config, paymentProcessor *settlement.Processor) {
basicIncomeTick, stopBasicIncomeFunc := util.SingleAsyncExecutingInstance(func() {
epochN := server.EpochCounter()
Expand Down
14 changes: 14 additions & 0 deletions pkg/innerring/internal/metachain/chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package metachain

import (
metacontracts "github.com/nspcc-dev/neofs-node/pkg/core/metachain"
"github.com/nspcc-dev/neofs-node/pkg/innerring/config"
"github.com/nspcc-dev/neofs-node/pkg/innerring/internal/blockchain"
"go.uber.org/zap"
)

// NewMetaChain returns side chain with redefined/custom native contracts.
// See [contracts.NewCustomNatives] for details.
func NewMetaChain(cfg *config.Consensus, wallet *config.Wallet, errChan chan<- error, log *zap.Logger) (*blockchain.Blockchain, error) {
return blockchain.New(cfg, wallet, errChan, log, metacontracts.NewCustomNatives)
}
Loading
Loading