forked from TrueCloudLab/neoneo-go
*: introduce Genesis protocol configuration
This section contains genesis-related settings including genesis-related or natives-related extensions. Currently it includes the set of node roles that may be designated duing the native Designation contract initialisation. Close #3156. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
1815bc8a32
commit
065bd3f0be
11 changed files with 294 additions and 6 deletions
|
@ -325,6 +325,7 @@ protocol-related settings described in the table below.
|
||||||
| Section | Type | Default value | Description | Notes |
|
| Section | Type | Default value | Description | Notes |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
|
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
|
||||||
|
| Genesis | [Genesis](#Genesis-Configuration) | none | The set of genesis block settings including NeoGo-specific protocol extensions that should be enabled at the genesis block or during native contracts initialisation. |
|
||||||
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. It also includes [#3085](https://github.com/nspcc-dev/neo-go/pull/3085) (ported from the [reference](https://github.com/neo-project/neo/pull/2810)) that enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. |
|
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM. It also includes [#3085](https://github.com/nspcc-dev/neo-go/pull/3085) (ported from the [reference](https://github.com/neo-project/neo/pull/2810)) that enables strict check for notifications emitted by a contract to precisely match the events specified in the contract manifest. |
|
||||||
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
|
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
|
||||||
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |
|
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |
|
||||||
|
@ -346,3 +347,39 @@ protocol-related settings described in the table below.
|
||||||
| ValidatorsCount | `uint32` | `0` | Number of validators set for the whole network lifetime, can't be set if `ValidatorsHistory` setting is used. |
|
| ValidatorsCount | `uint32` | `0` | Number of validators set for the whole network lifetime, can't be set if `ValidatorsHistory` setting is used. |
|
||||||
| ValidatorsHistory | map[uint32]uint32 | none | Number of consensus nodes to use after given height (see `CommitteeHistory` also). Heights where the change occurs must be divisible by the number of committee members at that height. Can't be used with `ValidatorsCount` not equal to zero. |
|
| ValidatorsHistory | map[uint32]uint32 | none | Number of consensus nodes to use after given height (see `CommitteeHistory` also). Heights where the change occurs must be divisible by the number of committee members at that height. Can't be used with `ValidatorsCount` not equal to zero. |
|
||||||
| VerifyTransactions | `bool` | `false` | Denotes whether to verify transactions in the received blocks. |
|
| VerifyTransactions | `bool` | `false` | Denotes whether to verify transactions in the received blocks. |
|
||||||
|
|
||||||
|
### Genesis Configuration
|
||||||
|
|
||||||
|
`Genesis` subsection of protocol configuration section contains a set of settings
|
||||||
|
specific for genesis block including NeoGo node extensions that should be enabled
|
||||||
|
during genesis block persist or at the moment of native contracts initialisation.
|
||||||
|
`Genesis` has the following structure:
|
||||||
|
```
|
||||||
|
Genesis:
|
||||||
|
Roles:
|
||||||
|
NeoFSAlphabet:
|
||||||
|
- 033238fa63bd08115ebf442d4af897eea2f6866e4c2001cd1f6e7656acdd91a5d3
|
||||||
|
- 03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c
|
||||||
|
- 02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e
|
||||||
|
- 03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050
|
||||||
|
Oracle:
|
||||||
|
- 03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0
|
||||||
|
- 0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30
|
||||||
|
```
|
||||||
|
where:
|
||||||
|
- `Roles` is a map from node roles that should be set at the moment of native
|
||||||
|
RoleManagement contract initialisation to the list of hex-encoded public keys
|
||||||
|
corresponding to this role. The set of valid roles includes:
|
||||||
|
- `StateValidator`
|
||||||
|
- `Oracle`
|
||||||
|
- `NeoFSAlphabet`
|
||||||
|
- `P2PNotary`
|
||||||
|
|
||||||
|
Roles designation order follows the enumeration above. Designation
|
||||||
|
notifications will be emitted after each configured role designation.
|
||||||
|
|
||||||
|
Note that Roles is a NeoGo extension that isn't supported by the NeoC# node and
|
||||||
|
must be disabled on the public Neo N3 networks. Roles extension is compatible
|
||||||
|
with NativeUpdateHistory setting, which means that specified roles will be set
|
||||||
|
only during native RoleManagement contract initialisation (which may be
|
||||||
|
performed in some non-genesis block). By default, no roles are designated.
|
||||||
|
|
51
pkg/config/genesis_extensions.go
Normal file
51
pkg/config/genesis_extensions.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Genesis represents a set of genesis block settings including the extensions
|
||||||
|
// enabled in the genesis block or during native contracts initialization.
|
||||||
|
type Genesis struct {
|
||||||
|
// Roles contains the set of roles that should be designated during native
|
||||||
|
// Designation contract initialization. It is NeoGo extension and must be
|
||||||
|
// disabled on the public Neo N3 networks.
|
||||||
|
Roles map[noderoles.Role]keys.PublicKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
// genesisAux is an auxiliary structure for Genesis YAML marshalling.
|
||||||
|
type genesisAux struct {
|
||||||
|
Roles map[string]keys.PublicKeys `yaml:"Roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML implements the YAML marshaler interface.
|
||||||
|
func (e Genesis) MarshalYAML() (any, error) {
|
||||||
|
var aux genesisAux
|
||||||
|
aux.Roles = make(map[string]keys.PublicKeys, len(e.Roles))
|
||||||
|
for r, ks := range e.Roles {
|
||||||
|
aux.Roles[r.String()] = ks
|
||||||
|
}
|
||||||
|
return aux, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the YAML unmarshaler interface.
|
||||||
|
func (e *Genesis) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var aux genesisAux
|
||||||
|
if err := unmarshal(&aux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Roles = make(map[noderoles.Role]keys.PublicKeys)
|
||||||
|
for s, ks := range aux.Roles {
|
||||||
|
r, ok := noderoles.FromString(s)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown node role: %s", s)
|
||||||
|
}
|
||||||
|
e.Roles[r] = ks
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -16,6 +16,10 @@ type (
|
||||||
ProtocolConfiguration struct {
|
ProtocolConfiguration struct {
|
||||||
// CommitteeHistory stores committee size change history (height: size).
|
// CommitteeHistory stores committee size change history (height: size).
|
||||||
CommitteeHistory map[uint32]uint32 `yaml:"CommitteeHistory"`
|
CommitteeHistory map[uint32]uint32 `yaml:"CommitteeHistory"`
|
||||||
|
// Genesis stores genesis-related settings including a set of NeoGo
|
||||||
|
// extensions that should be included into genesis block or be enabled
|
||||||
|
// at the moment of native contracts initialization.
|
||||||
|
Genesis Genesis `yaml:"Genesis"`
|
||||||
|
|
||||||
Magic netmode.Magic `yaml:"Magic"`
|
Magic netmode.Magic `yaml:"Magic"`
|
||||||
MemPoolSize int `yaml:"MemPoolSize"`
|
MemPoolSize int `yaml:"MemPoolSize"`
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProtocolConfigurationValidation(t *testing.T) {
|
func TestProtocolConfigurationValidation(t *testing.T) {
|
||||||
|
@ -275,3 +281,65 @@ func TestProtocolConfigurationEquals(t *testing.T) {
|
||||||
p.ValidatorsHistory = map[uint32]uint32{112: 0}
|
p.ValidatorsHistory = map[uint32]uint32{112: 0}
|
||||||
require.False(t, p.Equals(o))
|
require.False(t, p.Equals(o))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenesisExtensionsMarshalYAML(t *testing.T) {
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pub := pk.PublicKey()
|
||||||
|
|
||||||
|
t.Run("MarshalUnmarshalYAML", func(t *testing.T) {
|
||||||
|
g := &Genesis{
|
||||||
|
Roles: map[noderoles.Role]keys.PublicKeys{
|
||||||
|
noderoles.NeoFSAlphabet: {pub},
|
||||||
|
noderoles.P2PNotary: {pub},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testserdes.MarshalUnmarshalYAML(t, g, new(Genesis))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unmarshal config", func(t *testing.T) {
|
||||||
|
t.Run("good", func(t *testing.T) {
|
||||||
|
pubStr := hex.EncodeToString(pub.Bytes())
|
||||||
|
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
||||||
|
Genesis:
|
||||||
|
Roles:
|
||||||
|
NeoFSAlphabet:
|
||||||
|
- %s
|
||||||
|
- %s
|
||||||
|
Oracle:
|
||||||
|
- %s
|
||||||
|
- %s`, pubStr, pubStr, pubStr, pubStr)
|
||||||
|
cfg := new(Config)
|
||||||
|
require.NoError(t, yaml.Unmarshal([]byte(cfgYml), cfg))
|
||||||
|
require.Equal(t, 2, len(cfg.ProtocolConfiguration.Genesis.Roles))
|
||||||
|
require.Equal(t, keys.PublicKeys{pub, pub}, cfg.ProtocolConfiguration.Genesis.Roles[noderoles.NeoFSAlphabet])
|
||||||
|
require.Equal(t, keys.PublicKeys{pub, pub}, cfg.ProtocolConfiguration.Genesis.Roles[noderoles.Oracle])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unknown role", func(t *testing.T) {
|
||||||
|
pubStr := hex.EncodeToString(pub.Bytes())
|
||||||
|
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
||||||
|
Genesis:
|
||||||
|
Roles:
|
||||||
|
BadRole:
|
||||||
|
- %s`, pubStr)
|
||||||
|
cfg := new(Config)
|
||||||
|
err := yaml.Unmarshal([]byte(cfgYml), cfg)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "unknown node role: BadRole")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("last role", func(t *testing.T) {
|
||||||
|
pubStr := hex.EncodeToString(pub.Bytes())
|
||||||
|
cfgYml := fmt.Sprintf(`ProtocolConfiguration:
|
||||||
|
Genesis:
|
||||||
|
Roles:
|
||||||
|
last:
|
||||||
|
- %s`, pubStr)
|
||||||
|
cfg := new(Config)
|
||||||
|
err := yaml.Unmarshal([]byte(cfgYml), cfg)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "unknown node role: last")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts {
|
||||||
cs.Policy = policy
|
cs.Policy = policy
|
||||||
cs.Contracts = append(cs.Contracts, neo, gas, policy)
|
cs.Contracts = append(cs.Contracts, neo, gas, policy)
|
||||||
|
|
||||||
desig := newDesignate(cfg.P2PSigExtensions)
|
desig := newDesignate(cfg.P2PSigExtensions, cfg.Genesis.Roles)
|
||||||
desig.NEO = neo
|
desig.NEO = neo
|
||||||
cs.Designate = desig
|
cs.Designate = desig
|
||||||
cs.Contracts = append(cs.Contracts, desig)
|
cs.Contracts = append(cs.Contracts, desig)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
@ -32,6 +33,9 @@ type Designate struct {
|
||||||
|
|
||||||
// p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant.
|
// p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant.
|
||||||
p2pSigExtensionsEnabled bool
|
p2pSigExtensionsEnabled bool
|
||||||
|
// initialNodeRoles defines a set of node roles that should be defined at the contract
|
||||||
|
// deployment (initialization).
|
||||||
|
initialNodeRoles map[noderoles.Role]keys.PublicKeys
|
||||||
|
|
||||||
OracleService atomic.Value
|
OracleService atomic.Value
|
||||||
// NotaryService represents a Notary node module.
|
// NotaryService represents a Notary node module.
|
||||||
|
@ -97,9 +101,10 @@ func (s *Designate) isValidRole(r noderoles.Role) bool {
|
||||||
r == noderoles.NeoFSAlphabet || (s.p2pSigExtensionsEnabled && r == noderoles.P2PNotary)
|
r == noderoles.NeoFSAlphabet || (s.p2pSigExtensionsEnabled && r == noderoles.P2PNotary)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.Role]keys.PublicKeys) *Designate {
|
||||||
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
||||||
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
||||||
|
s.initialNodeRoles = initialNodeRoles
|
||||||
defer s.UpdateHash()
|
defer s.UpdateHash()
|
||||||
|
|
||||||
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
||||||
|
@ -127,6 +132,19 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
||||||
func (s *Designate) Initialize(ic *interop.Context) error {
|
func (s *Designate) Initialize(ic *interop.Context) error {
|
||||||
cache := &DesignationCache{}
|
cache := &DesignationCache{}
|
||||||
ic.DAO.SetCache(s.ID, cache)
|
ic.DAO.SetCache(s.ID, cache)
|
||||||
|
|
||||||
|
if len(s.initialNodeRoles) != 0 {
|
||||||
|
for _, r := range noderoles.Roles {
|
||||||
|
pubs, ok := s.initialNodeRoles[r]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := s.DesignateAsRole(ic, r, pubs)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize Designation role data for role %s: %w", r, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,10 +373,14 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs
|
||||||
if !s.isValidRole(r) {
|
if !s.isValidRole(r) {
|
||||||
return ErrInvalidRole
|
return ErrInvalidRole
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ic.Trigger != trigger.OnPersist {
|
||||||
h := s.NEO.GetCommitteeAddress(ic.DAO)
|
h := s.NEO.GetCommitteeAddress(ic.DAO)
|
||||||
if ok, err := runtime.CheckHashedWitness(ic, h); err != nil || !ok {
|
if ok, err := runtime.CheckHashedWitness(ic, h); err != nil || !ok {
|
||||||
return ErrInvalidWitness
|
return ErrInvalidWitness
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ic.Block == nil {
|
if ic.Block == nil {
|
||||||
return ErrNoBlock
|
return ErrNoBlock
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
package native_test
|
package native_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"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/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -137,3 +140,23 @@ func TestDesignate_Cache(t *testing.T) {
|
||||||
require.Nil(t, updatedNodes)
|
require.Nil(t, updatedNodes)
|
||||||
require.False(t, updateCalled)
|
require.False(t, updateCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDesignate_GenesisRolesExtension(t *testing.T) {
|
||||||
|
pk1, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pk2, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pubs := keys.PublicKeys{pk1.PublicKey(), pk2.PublicKey()}
|
||||||
|
|
||||||
|
bc, acc := chain.NewSingleWithCustomConfig(t, func(blockchain *config.Blockchain) {
|
||||||
|
blockchain.Genesis.Roles = map[noderoles.Role]keys.PublicKeys{
|
||||||
|
noderoles.StateValidator: pubs,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
e := neotest.NewExecutor(t, bc, acc, acc)
|
||||||
|
c := e.CommitteeInvoker(e.NativeHash(t, nativenames.Designation))
|
||||||
|
|
||||||
|
// Check designated node in a separate block.
|
||||||
|
sort.Sort(pubs)
|
||||||
|
checkNodeRoles(t, c, true, noderoles.StateValidator, e.Chain.BlockHeight()+1, pubs)
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ const (
|
||||||
last
|
last
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Roles is a set of all available roles sorted by values.
|
||||||
|
var Roles []Role
|
||||||
|
|
||||||
// roles is a map of valid Role string representation to its type.
|
// roles is a map of valid Role string representation to its type.
|
||||||
var roles map[string]Role
|
var roles map[string]Role
|
||||||
|
|
||||||
|
@ -24,6 +27,7 @@ func init() {
|
||||||
roles = make(map[string]Role)
|
roles = make(map[string]Role)
|
||||||
for i := StateValidator; i < last; i = i << 1 {
|
for i := StateValidator; i < last; i = i << 1 {
|
||||||
roles[i.String()] = i
|
roles[i.String()] = i
|
||||||
|
Roles = append(Roles, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ func TestDesignate_DesignateAsRole(t *testing.T) {
|
||||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||||
bl := block.New(bc.config.StateRootInHeader)
|
bl := block.New(bc.config.StateRootInHeader)
|
||||||
bl.Index = bc.BlockHeight() + 1
|
bl.Index = bc.BlockHeight() + 1
|
||||||
ic := bc.newInteropContext(trigger.OnPersist, bc.dao, bl, tx)
|
ic := bc.newInteropContext(trigger.Application, bc.dao, bl, tx)
|
||||||
ic.SpawnVM()
|
ic.SpawnVM()
|
||||||
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
||||||
|
|
||||||
|
|
|
@ -390,3 +390,23 @@ func (p *PublicKey) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalYAML implements the YAML marshaler interface.
|
||||||
|
func (p *PublicKey) MarshalYAML() (any, error) {
|
||||||
|
return hex.EncodeToString(p.Bytes()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the YAML unmarshaler interface.
|
||||||
|
func (p *PublicKey) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var s string
|
||||||
|
err := unmarshal(&s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to decode public key from hex bytes: %w", err)
|
||||||
|
}
|
||||||
|
return p.DecodeBytes(b)
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEncodeDecodeInfinity(t *testing.T) {
|
func TestEncodeDecodeInfinity(t *testing.T) {
|
||||||
|
@ -246,3 +247,61 @@ func BenchmarkPublicDecodeBytes(t *testing.B) {
|
||||||
require.NoError(t, k.DecodeBytes(keyBytes))
|
require.NoError(t, k.DecodeBytes(keyBytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMarshallYAML(t *testing.T) {
|
||||||
|
str := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
|
||||||
|
pubKey, err := NewPublicKeyFromString(str)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bytes, err := yaml.Marshal(&pubKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []byte(str + "\n") // YAML marshaller adds new line in the end which is expected.
|
||||||
|
require.Equal(t, expected, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshallYAML(t *testing.T) {
|
||||||
|
str := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
|
||||||
|
expected, err := NewPublicKeyFromString(str)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
actual := &PublicKey{}
|
||||||
|
err = yaml.Unmarshal([]byte(str), actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshallYAMLBadCompresed(t *testing.T) {
|
||||||
|
str := `"02ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"`
|
||||||
|
actual := &PublicKey{}
|
||||||
|
err := yaml.Unmarshal([]byte(str), actual)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "error computing Y for compressed point")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshallYAMLNotAHex(t *testing.T) {
|
||||||
|
str := `"04Tb17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"`
|
||||||
|
actual := &PublicKey{}
|
||||||
|
err := yaml.Unmarshal([]byte(str), actual)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "failed to decode public key from hex bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshallYAMLUncompressed(t *testing.T) {
|
||||||
|
str := "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
|
||||||
|
expected, err := NewPublicKeyFromString(str)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
actual := &PublicKey{}
|
||||||
|
err = yaml.Unmarshal([]byte(str), actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalYAML(t *testing.T) {
|
||||||
|
str := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
|
||||||
|
expected, err := NewPublicKeyFromString(str)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testserdes.MarshalUnmarshalYAML(t, expected, new(PublicKey))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue