Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some Inner Ring UX improvements #2673

Merged
merged 5 commits into from
Jan 18, 2024
Merged
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
4 changes: 2 additions & 2 deletions config/example/ir.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ morph:
dial_timeout: 5s # Timeout for RPC client connection to sidechain
reconnections_number: 5 # number of reconnection attempts
reconnections_delay: 5s # time delay b/w reconnection attempts
endpoints: # List of websocket RPC endpoints in sidechain
endpoints: # List of websocket RPC endpoints in sidechain. May be omitted if 'consensus' is configured
- wss://sidechain1.fs.neo.org:30333/ws
- wss://sidechain2.fs.neo.org:30333/ws
validators: # List of hex-encoded 33-byte public keys of sidechain validators to vote for at application startup; can be omitted if equals `consensus.committee`
- 0283120f4c8c1fc1d792af5063d2def9da5fddc90bc1384de7fcfdda33c3860170
consensus: # Local consensus launch mode activated only when 'endpoint.client' is unset.
consensus: # Local consensus launch mode activated only when 'endpoints' is unset.
magic: 15405 # Network magic. Must be unsigned integer in range [1:4294967295]
committee: # Hex-encoded public keys of the initial committee
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
Expand Down
49 changes: 31 additions & 18 deletions pkg/innerring/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import (
"go.uber.org/zap"
)

// Various configuration paths.
const (
cfgPathFSChain = "morph"
cfgPathFSChainRPCEndpoints = cfgPathFSChain + ".endpoints"
cfgPathFSChainLocalConsensus = cfgPathFSChain + ".consensus"
cfgPathFSChainValidators = cfgPathFSChain + ".validators"
)

// checks whether Inner Ring app is configured to initialize underlying NeoFS
// Sidechain or await for a background deployment. Returns an error if
// the configuration format is violated.
Expand All @@ -32,25 +40,30 @@ func isAutoDeploymentMode(cfg *viper.Viper) (bool, error) {
}

// checks if Inner Ring app is configured to be launched in local consensus
// mode.
func isLocalConsensusMode(cfg *viper.Viper) bool {
return !cfg.IsSet("morph.endpoints")
// mode. Returns error if neither NeoFS chain RPC endpoints nor local consensus
// is configured.
func isLocalConsensusMode(cfg *viper.Viper) (bool, error) {
endpointsUnset := !cfg.IsSet(cfgPathFSChainRPCEndpoints)
if endpointsUnset && !cfg.IsSet(cfgPathFSChainLocalConsensus) {
return false, fmt.Errorf("either '%s' or '%s' must be configured",
cfgPathFSChainRPCEndpoints, cfgPathFSChainLocalConsensus)
}

return endpointsUnset, nil
}

func parseBlockchainConfig(v *viper.Viper, _logger *zap.Logger) (c blockchain.Config, err error) {
const rootSection = "morph.consensus"

if !v.IsSet(rootSection) {
return c, fmt.Errorf("missing root section '%s'", rootSection)
if !v.IsSet(cfgPathFSChainLocalConsensus) {
return c, fmt.Errorf("missing root section '%s'", cfgPathFSChainLocalConsensus)
}

_uint, err := parseConfigUint64Range(v, rootSection+".magic", "network magic", 1, math.MaxUint32)
_uint, err := parseConfigUint64Range(v, cfgPathFSChainLocalConsensus+".magic", "network magic", 1, math.MaxUint32)
if err != nil {
return c, err
}
c.NetworkMagic = netmode.Magic(_uint)

const storageSection = rootSection + ".storage"
const storageSection = cfgPathFSChainLocalConsensus + ".storage"
if !v.IsSet(storageSection) {
return c, fmt.Errorf("missing storage section '%s'", storageSection)
}
Expand All @@ -76,39 +89,39 @@ func parseBlockchainConfig(v *viper.Viper, _logger *zap.Logger) (c blockchain.Co
c.Storage = blockchain.InMemory()
}

const committeeKey = rootSection + ".committee"
const committeeKey = cfgPathFSChainLocalConsensus + ".committee"
c.Committee, err = parseConfigPublicKeys(v, committeeKey, "committee members")
if err != nil {
return c, err
} else if len(c.Committee) == 0 {
return c, fmt.Errorf("empty committee members '%s'", committeeKey)
}

c.BlockInterval, err = parseConfigDurationPositive(v, rootSection+".time_per_block", "block interval")
c.BlockInterval, err = parseConfigDurationPositive(v, cfgPathFSChainLocalConsensus+".time_per_block", "block interval")
if err != nil && !errors.Is(err, errMissingConfig) {
return c, err
}

traceableChainLength, err := parseConfigUint64Range(v, rootSection+".max_traceable_blocks", "traceable chain length", 1, math.MaxUint32)
traceableChainLength, err := parseConfigUint64Range(v, cfgPathFSChainLocalConsensus+".max_traceable_blocks", "traceable chain length", 1, math.MaxUint32)
if err != nil && !errors.Is(err, errMissingConfig) {
return c, err
}
c.TraceableChainLength = uint32(traceableChainLength)

c.SeedNodes, err = parseConfigAddressesTCP(v, rootSection+".seed_nodes", "seed nodes")
c.SeedNodes, err = parseConfigAddressesTCP(v, cfgPathFSChainLocalConsensus+".seed_nodes", "seed nodes")
if err != nil && !errors.Is(err, errMissingConfig) {
return c, err
}

const hardForksKey = rootSection + ".hardforks"
const hardForksKey = cfgPathFSChainLocalConsensus + ".hardforks"
if v.IsSet(hardForksKey) {
c.HardForks, err = parseConfigMapUint32(v, hardForksKey, "hard forks", math.MaxUint32)
if err != nil {
return c, err
}
}

const validatorsHistoryKey = rootSection + ".validators_history"
const validatorsHistoryKey = cfgPathFSChainLocalConsensus + ".validators_history"
if v.IsSet(validatorsHistoryKey) {
c.ValidatorsHistory = make(map[uint32]uint32)
committeeSize := uint64(c.Committee.Len())
Expand Down Expand Up @@ -138,7 +151,7 @@ func parseBlockchainConfig(v *viper.Viper, _logger *zap.Logger) (c blockchain.Co
}
}

const rpcSection = rootSection + ".rpc"
const rpcSection = cfgPathFSChainLocalConsensus + ".rpc"
if v.IsSet(rpcSection) {
c.RPC.Addresses, err = parseConfigAddressesTCP(v, rpcSection+".listen", "network addresses to listen insecure Neo RPC on")
if err != nil && !errors.Is(err, errMissingConfig) {
Expand Down Expand Up @@ -168,7 +181,7 @@ func parseBlockchainConfig(v *viper.Viper, _logger *zap.Logger) (c blockchain.Co
}
}

const p2pSection = rootSection + ".p2p"
const p2pSection = cfgPathFSChainLocalConsensus + ".p2p"
if v.IsSet(p2pSection) {
c.P2P.DialTimeout, err = parseConfigDurationPositive(v, p2pSection+".dial_timeout", "P2P dial timeout")
if err != nil && !errors.Is(err, errMissingConfig) {
Expand Down Expand Up @@ -218,7 +231,7 @@ func parseBlockchainConfig(v *viper.Viper, _logger *zap.Logger) (c blockchain.Co
}
}

c.SetRolesInGenesis, err = parseConfigBool(v, rootSection+".set_roles_in_genesis", "flag to set roles for the committee in the genesis block")
c.SetRolesInGenesis, err = parseConfigBool(v, cfgPathFSChainLocalConsensus+".set_roles_in_genesis", "flag to set roles for the committee in the genesis block")
if err != nil && !errors.Is(err, errMissingConfig) {
return c, err
}
Expand Down
80 changes: 68 additions & 12 deletions pkg/innerring/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,17 +345,64 @@ func TestIsLocalConsensusMode(t *testing.T) {
v.SetEnvPrefix("neofs_ir")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

const envKey = "NEOFS_IR_MORPH_ENDPOINTS"

err := os.Unsetenv(envKey)
require.NoError(t, err)

require.True(t, isLocalConsensusMode(v))

err = os.Setenv(envKey, "any string")
require.NoError(t, err)
const envKeyEndpoints = "NEOFS_IR_MORPH_ENDPOINTS"
const envKeyConsensus = "NEOFS_IR_MORPH_CONSENSUS"
var err error

for _, tc := range []struct {
setEndpoints bool
setConsensus bool
expected uint8 // 0:false, 1:true, 2:error
}{
{
setEndpoints: true,
setConsensus: true,
expected: 0,
},
{
setEndpoints: true,
setConsensus: false,
expected: 0,
},
{
setEndpoints: false,
setConsensus: true,
expected: 1,
},
{
setEndpoints: false,
setConsensus: false,
expected: 2,
},
} {
if tc.setEndpoints {
err = os.Setenv(envKeyEndpoints, "any")
} else {
err = os.Unsetenv(envKeyEndpoints)
}
require.NoError(t, err, tc)

require.False(t, isLocalConsensusMode(v))
if tc.setConsensus {
err = os.Setenv(envKeyConsensus, "any")
} else {
err = os.Unsetenv(envKeyConsensus)
}
require.NoError(t, err, tc)

res, err := isLocalConsensusMode(v)
switch tc.expected {
default:
t.Fatalf("unexpected result value %v", tc.expected)
case 0:
require.NoError(t, err, tc)
require.False(t, res, tc)
case 1:
require.NoError(t, err, tc)
require.True(t, res, tc)
case 2:
require.Error(t, err, tc)
}
}
})

t.Run("YAML", func(t *testing.T) {
Expand All @@ -368,11 +415,20 @@ morph:
`))
require.NoError(t, err)

require.False(t, isLocalConsensusMode(v))
res, err := isLocalConsensusMode(v)
require.NoError(t, err)
require.False(t, res)

resetConfig(t, v, "morph.endpoints")

require.True(t, isLocalConsensusMode(v))
_, err = isLocalConsensusMode(v)
require.Error(t, err)

v.Set("morph.consensus", "any")

res, err = isLocalConsensusMode(v)
require.NoError(t, err)
require.True(t, res)
})
}

Expand Down
16 changes: 9 additions & 7 deletions pkg/innerring/innerring.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,11 @@
return nil, err
}

isLocalConsensus := isLocalConsensusMode(cfg)
isLocalConsensus, err := isLocalConsensusMode(cfg)
if err != nil {
return nil, fmt.Errorf("invalid consensus configuration: %w", err)

Check warning on line 400 in pkg/innerring/innerring.go

View check run for this annotation

Codecov / codecov/patch

pkg/innerring/innerring.go#L398-L400

Added lines #L398 - L400 were not covered by tests
}

if isLocalConsensus {
if singleAcc == nil {
return nil, fmt.Errorf("missing account with label '%s' in wallet '%s'", singleAccLabel, walletPass)
Expand Down Expand Up @@ -484,7 +488,7 @@
}
} else {
if len(server.predefinedValidators) == 0 {
return nil, fmt.Errorf("empty '%s' list in config", validatorsConfigKey)
return nil, fmt.Errorf("empty '%s' list in config", cfgPathFSChainValidators)

Check warning on line 491 in pkg/innerring/innerring.go

View check run for this annotation

Codecov / codecov/patch

pkg/innerring/innerring.go#L491

Added line #L491 was not covered by tests
}

// fallback to the pure RPC architecture
Expand Down Expand Up @@ -512,9 +516,9 @@
//
// connection switch/loose callbacks are not needed, so just create
// another one-time client instead of server.createClient
endpoints := cfg.GetStringSlice(morphChain.name + ".endpoints")
endpoints := cfg.GetStringSlice(cfgPathFSChainRPCEndpoints)

Check warning on line 519 in pkg/innerring/innerring.go

View check run for this annotation

Codecov / codecov/patch

pkg/innerring/innerring.go#L519

Added line #L519 was not covered by tests
if len(endpoints) == 0 {
return nil, fmt.Errorf("%s chain client endpoints not provided", morphChain.name)
return nil, fmt.Errorf("configuration of FS chain RPC endpoints '%s' is missing or empty", cfgPathFSChainRPCEndpoints)

Check warning on line 521 in pkg/innerring/innerring.go

View check run for this annotation

Codecov / codecov/patch

pkg/innerring/innerring.go#L521

Added line #L521 was not covered by tests
}

clnt, err = client.New(server.key,
Expand Down Expand Up @@ -1099,10 +1103,8 @@
return client.New(p.key, options...)
}

const validatorsConfigKey = "morph.validators"

func parsePredefinedValidators(cfg *viper.Viper) (keys.PublicKeys, error) {
publicKeyStrings := cfg.GetStringSlice(validatorsConfigKey)
publicKeyStrings := cfg.GetStringSlice(cfgPathFSChainValidators)

Check warning on line 1107 in pkg/innerring/innerring.go

View check run for this annotation

Codecov / codecov/patch

pkg/innerring/innerring.go#L1107

Added line #L1107 was not covered by tests

return ParsePublicKeysFromStrings(publicKeyStrings)
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/morph/client/nns.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,26 @@
func autoSidechainScope(ws *rpcclient.WSClient, conf *cfg) error {
nnsHash, err := nns.InferHash(ws)
if err != nil {
return fmt.Errorf("InferHash: %w", err)
return fmt.Errorf("resolve NNS contract address: %w", err)

Check warning on line 104 in pkg/morph/client/nns.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/client/nns.go#L104

Added line #L104 was not covered by tests
}

var nnsReader = nns.NewReader(invoker.New(ws, nil), nnsHash)

balanceHash, err := nnsReader.ResolveFSContract(nns.NameBalance)
if err != nil {
return fmt.Errorf("resolving balance: %w", err)
return fmt.Errorf("resolve Balance contract address by NNS domain name %q: %w", nns.NameBalance, err)

Check warning on line 111 in pkg/morph/client/nns.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/client/nns.go#L111

Added line #L111 was not covered by tests
}
cntHash, err := nnsReader.ResolveFSContract(nns.NameContainer)
if err != nil {
return fmt.Errorf("resolving container: %w", err)
return fmt.Errorf("resolve Container contract address by NNS domain name %q: %w", nns.NameContainer, err)

Check warning on line 115 in pkg/morph/client/nns.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/client/nns.go#L115

Added line #L115 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"NNS domain name" confuses me. seems like it is about some ".nns"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure i got ur point, pls write message text u suggest

}
netmapHash, err := nnsReader.ResolveFSContract(nns.NameNetmap)
if err != nil {
return fmt.Errorf("resolving netmap: %w", err)
return fmt.Errorf("resolve Netmap contract address by NNS domain name %q: %w", nns.NameNetmap, err)

Check warning on line 119 in pkg/morph/client/nns.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/client/nns.go#L119

Added line #L119 was not covered by tests
}
neofsIDHash, err := nnsReader.ResolveFSContract(nns.NameNeoFSID)
if err != nil {
return fmt.Errorf("resolving neofsid: %w", err)
return fmt.Errorf("resolve NeoFS ID contract address by NNS domain name %q: %w", nns.NameNeoFSID, err)

Check warning on line 123 in pkg/morph/client/nns.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/client/nns.go#L123

Added line #L123 was not covered by tests
}

conf.signer = GetUniversalSignerScope(nnsHash, balanceHash, cntHash, netmapHash, neofsIDHash)
Expand Down
8 changes: 8 additions & 0 deletions pkg/morph/deploy/funds.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@
To: committeeMultiSigAccAddress,
Amount: remGAS,
})

prm.logger.Info("going to transfer all remaining GAS from validator multi-sig account to the committee one",
zap.Stringer("from", validatorMultiSigAccAddress), zap.Stringer("to", committeeMultiSigAccAddress),
zap.Stringer("amount", remGAS))

Check warning on line 179 in pkg/morph/deploy/funds.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/deploy/funds.go#L177-L179

Added lines #L177 - L179 were not covered by tests
}

var script []byte
Expand All @@ -197,6 +201,10 @@
}

script = append(script, tx.Script...)

prm.logger.Info("going to transfer all remaining NEO from validator multi-sig account to the committee one",
zap.Stringer("from", validatorMultiSigAccAddress), zap.Stringer("to", committeeMultiSigAccAddress),
zap.Stringer("amount", remNEO))

Check warning on line 207 in pkg/morph/deploy/funds.go

View check run for this annotation

Codecov / codecov/patch

pkg/morph/deploy/funds.go#L205-L207

Added lines #L205 - L207 were not covered by tests
}

if len(script) == 0 {
Expand Down
Loading