*: move NativeUpdateHistory logic under Hardforks management
Close #3196. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
4d8601297d
commit
58102a9a80
34 changed files with 170 additions and 306 deletions
|
@ -26,17 +26,6 @@ ProtocolConfiguration:
|
|||
Hardforks:
|
||||
Aspidochelone: 3000000
|
||||
Basilisk: 4500000
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
Notary: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -38,16 +38,6 @@ ProtocolConfiguration:
|
|||
Hardforks:
|
||||
Aspidochelone: 1730000
|
||||
Basilisk: 4120000
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -16,16 +16,6 @@ ProtocolConfiguration:
|
|||
- node_four:20336
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: false
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -16,16 +16,6 @@ ProtocolConfiguration:
|
|||
- node_four:20336
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: false
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -10,16 +10,6 @@ ProtocolConfiguration:
|
|||
- node_single:20333
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: false
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -16,16 +16,6 @@ ProtocolConfiguration:
|
|||
- node_four:20336
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: false
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -16,16 +16,6 @@ ProtocolConfiguration:
|
|||
- node_four:20336
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: false
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -16,16 +16,6 @@ ProtocolConfiguration:
|
|||
- localhost:20336
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: false
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -24,17 +24,6 @@ ProtocolConfiguration:
|
|||
- morph7.t5.fs.neo.org:50333
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: true
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
Notary: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -41,16 +41,6 @@ ProtocolConfiguration:
|
|||
Hardforks:
|
||||
Aspidochelone: 210000
|
||||
Basilisk: 2680000
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
|
||||
ApplicationConfiguration:
|
||||
SkipBlockVerification: false
|
||||
|
|
|
@ -8,17 +8,6 @@ ProtocolConfiguration:
|
|||
ValidatorsCount: 1
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: true
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
Notary: [0]
|
||||
Hardforks:
|
||||
Aspidochelone: 25
|
||||
|
||||
|
|
|
@ -17,17 +17,6 @@ ProtocolConfiguration:
|
|||
- 127.0.0.1:20336
|
||||
VerifyTransactions: true
|
||||
P2PSigExtensions: true
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
Notary: [0]
|
||||
Hardforks:
|
||||
Aspidochelone: 25
|
||||
|
||||
|
|
|
@ -334,7 +334,6 @@ protocol-related settings described in the table below.
|
|||
| MaxTransactionsPerBlock | `uint16` | `512` | Maximum number of transactions per block. |
|
||||
| MaxValidUntilBlockIncrement | `uint32` | `5760` | Upper height increment limit for transaction's ValidUntilBlock field value relative to the current blockchain height, exceeding which a transaction will fail validation. It is set to estimated daily number of blocks with 15s interval by default. |
|
||||
| MemPoolSize | `int` | `50000` | Size of the node's memory pool where transactions are stored before they are added to block. |
|
||||
| NativeActivations | `map[string][]uint32` | ContractManagement: [0]<br>StdLib: [0]<br>CryptoLib: [0]<br>LedgerContract: [0]<br>NeoToken: [0]<br>GasToken: [0]<br>PolicyContract: [0]<br>RoleManagement: [0]<br>OracleContract: [0] | The list of histories of native contracts updates. Each list item shod be presented as a known native contract name with the corresponding list of chain's heights. The contract is not active until chain reaches the first height value specified in the list. | `Notary` is supported. |
|
||||
| P2PNotaryRequestPayloadPoolSize | `int` | `1000` | Size of the node's P2P Notary request payloads memory pool where P2P Notary requests are stored before main or fallback transaction is completed and added to the chain.<br>This option is valid only if `P2PSigExtensions` are enabled. | Not supported by the C# node, thus may affect heterogeneous networks functionality. |
|
||||
| P2PSigExtensions | `bool` | `false` | Enables following additional Notary service related logic:<br>• Transaction attribute `NotaryAssisted`<br>• Network payload of the `P2PNotaryRequest` type<br>• Native `Notary` contract<br>• Notary node module | Not supported by the C# node, thus may affect heterogeneous networks functionality. |
|
||||
| P2PStateExchangeExtensions | `bool` | `false` | Enables the following P2P MPT state data exchange logic: <br>• `StateSyncInterval` protocol setting <br>• P2P commands `GetMPTDataCMD` and `MPTDataCMD` | Not supported by the C# node, thus may affect heterogeneous networks functionality. Can be supported either on MPT-complete node (`KeepOnlyLatestState`=`false`) or on light GC-enabled node (`RemoveUntraceableBlocks=true`) in which case `KeepOnlyLatestState` setting doesn't change the behavior, an appropriate set of MPTs is always stored (see `RemoveUntraceableBlocks`). |
|
||||
|
@ -383,9 +382,9 @@ where:
|
|||
|
||||
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
|
||||
with Hardforks 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.
|
||||
performed in some non-genesis hardfork). By default, no roles are designated.
|
||||
|
||||
- `Transaction` is a container for transaction script that should be deployed in
|
||||
the genesis block if provided. `Transaction` includes `Script` which is a
|
||||
|
|
|
@ -171,9 +171,8 @@ verify and broadcast `P2PNotaryRequest` P2P payloads, properly initialize native
|
|||
Notary contract and designate `P2PNotary` node role in RoleManagement native
|
||||
contract.
|
||||
|
||||
If you use custom `NativeActivations` subsection of the `ProtocolConfiguration`
|
||||
section in your node config, specify the height of the Notary contract
|
||||
activation, e.g. `0`.
|
||||
Currently, Notary contract activation height is not configurable and is always
|
||||
set to 0 (if `P2PSigExtensions` are enabled).
|
||||
|
||||
Note, that even if `P2PSigExtensions` config subsection enables notary-related
|
||||
logic in the network, it still does not turn your node into notary service node.
|
||||
|
@ -184,17 +183,6 @@ To enable notary service node functionality refer to the
|
|||
|
||||
```
|
||||
P2PSigExtensions: true
|
||||
NativeActivations:
|
||||
Notary: [0]
|
||||
ContractManagement: [0]
|
||||
StdLib: [0]
|
||||
CryptoLib: [0]
|
||||
LedgerContract: [0]
|
||||
NeoToken: [0]
|
||||
GasToken: [0]
|
||||
PolicyContract: [0]
|
||||
RoleManagement: [0]
|
||||
OracleContract: [0]
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
)
|
||||
|
||||
|
@ -44,8 +43,6 @@ type (
|
|||
// exceeding that a transaction should fail validation. It is set to estimated daily number
|
||||
// of blocks with 15s interval.
|
||||
MaxValidUntilBlockIncrement uint32 `yaml:"MaxValidUntilBlockIncrement"`
|
||||
// NativeUpdateHistories is a list of histories of native contracts updates.
|
||||
NativeUpdateHistories map[string][]uint32 `yaml:"NativeActivations"`
|
||||
// P2PSigExtensions enables additional signature-related logic.
|
||||
P2PSigExtensions bool `yaml:"P2PSigExtensions"`
|
||||
// P2PStateExchangeExtensions enables additional P2P MPT state data exchange logic.
|
||||
|
@ -86,11 +83,6 @@ func (p *ProtocolConfiguration) Validate() error {
|
|||
if p.TimePerBlock%time.Millisecond != 0 {
|
||||
return errors.New("TimePerBlock must be an integer number of milliseconds")
|
||||
}
|
||||
for name := range p.NativeUpdateHistories {
|
||||
if !nativenames.IsValid(name) {
|
||||
return fmt.Errorf("NativeActivations configuration section contains unexpected native contract name: %s", name)
|
||||
}
|
||||
}
|
||||
for name := range p.Hardforks {
|
||||
if !IsHardforkValid(name) {
|
||||
return fmt.Errorf("Hardforks configuration section contains unexpected hardfork: %s", name)
|
||||
|
@ -237,7 +229,6 @@ func (p *ProtocolConfiguration) Equals(o *ProtocolConfiguration) bool {
|
|||
p.VerifyTransactions != o.VerifyTransactions ||
|
||||
len(p.CommitteeHistory) != len(o.CommitteeHistory) ||
|
||||
len(p.Hardforks) != len(o.Hardforks) ||
|
||||
len(p.NativeUpdateHistories) != len(o.NativeUpdateHistories) ||
|
||||
len(p.SeedList) != len(o.SeedList) ||
|
||||
len(p.StandbyCommittee) != len(o.StandbyCommittee) ||
|
||||
len(p.ValidatorsHistory) != len(o.ValidatorsHistory) {
|
||||
|
@ -255,17 +246,6 @@ func (p *ProtocolConfiguration) Equals(o *ProtocolConfiguration) bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
for k, v := range p.NativeUpdateHistories {
|
||||
vo := o.NativeUpdateHistories[k]
|
||||
if len(v) != len(vo) {
|
||||
return false
|
||||
}
|
||||
for i := range v {
|
||||
if v[i] != vo[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range p.SeedList {
|
||||
if p.SeedList[i] != o.SeedList[i] {
|
||||
return false
|
||||
|
|
|
@ -28,12 +28,6 @@ func TestProtocolConfigurationValidation(t *testing.T) {
|
|||
ValidatorsCount: 1,
|
||||
}
|
||||
require.Error(t, p.Validate())
|
||||
p = &ProtocolConfiguration{
|
||||
NativeUpdateHistories: map[string][]uint32{
|
||||
"someContract": {0, 10},
|
||||
},
|
||||
}
|
||||
require.Error(t, p.Validate())
|
||||
p = &ProtocolConfiguration{
|
||||
StandbyCommittee: []string{
|
||||
"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
|
||||
|
@ -247,17 +241,6 @@ func TestProtocolConfigurationEquals(t *testing.T) {
|
|||
p.Hardforks = nil
|
||||
o.Hardforks = nil
|
||||
|
||||
p.NativeUpdateHistories = map[string][]uint32{"Contract": {1, 2, 3}}
|
||||
o.NativeUpdateHistories = map[string][]uint32{"Contract": {1, 2, 3}}
|
||||
require.True(t, p.Equals(o))
|
||||
p.NativeUpdateHistories["Contract"] = []uint32{1, 2, 3, 4}
|
||||
require.False(t, p.Equals(o))
|
||||
p.NativeUpdateHistories["Contract"] = []uint32{1, 2, 4}
|
||||
require.False(t, p.Equals(o))
|
||||
|
||||
p.NativeUpdateHistories = nil
|
||||
o.NativeUpdateHistories = nil
|
||||
|
||||
p.SeedList = []string{"url1", "url2"}
|
||||
o.SeedList = []string{"url1", "url2"}
|
||||
require.True(t, p.Equals(o))
|
||||
|
|
4
pkg/config/testdata/protocol.test.yml
vendored
4
pkg/config/testdata/protocol.test.yml
vendored
|
@ -1,4 +0,0 @@
|
|||
ProtocolConfiguration:
|
||||
NativeActivations:
|
||||
ContractManagement: [0]
|
||||
UnexpectedContractName: [0]
|
|
@ -283,10 +283,6 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
|
|||
zap.Int("StateSyncInterval", cfg.StateSyncInterval))
|
||||
}
|
||||
}
|
||||
if len(cfg.NativeUpdateHistories) == 0 {
|
||||
cfg.NativeUpdateHistories = map[string][]uint32{}
|
||||
log.Info("NativeActivations are not set, using default values")
|
||||
}
|
||||
if cfg.Hardforks == nil {
|
||||
cfg.Hardforks = map[string]uint32{}
|
||||
for _, hf := range config.Hardforks {
|
||||
|
@ -485,8 +481,8 @@ func (bc *Blockchain) init() error {
|
|||
for _, c := range bc.contracts.Contracts {
|
||||
md := c.Metadata()
|
||||
storedCS := bc.GetContractState(md.Hash)
|
||||
history := md.UpdateHistory
|
||||
if len(history) == 0 || history[0] > bHeight {
|
||||
// Check that contract was deployed.
|
||||
if !bc.isHardforkEnabled(c.ActiveIn(), bHeight) {
|
||||
if storedCS != nil {
|
||||
return fmt.Errorf("native contract %s is already stored, but marked as inactive for height %d in config", md.Name, bHeight)
|
||||
}
|
||||
|
@ -1022,19 +1018,31 @@ func (bc *Blockchain) resetStateInternal(height uint32, stage stateChangeStage)
|
|||
|
||||
func (bc *Blockchain) initializeNativeCache(blockHeight uint32, d *dao.Simple) error {
|
||||
for _, c := range bc.contracts.Contracts {
|
||||
for _, h := range c.Metadata().UpdateHistory {
|
||||
if blockHeight >= h { // check that contract was deployed.
|
||||
err := c.InitializeCache(blockHeight, d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize cache for %s: %w", c.Metadata().Name, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
// Check that contract was deployed.
|
||||
if !bc.isHardforkEnabled(c.ActiveIn(), blockHeight) {
|
||||
continue
|
||||
}
|
||||
err := c.InitializeCache(blockHeight, d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize cache for %s: %w", c.Metadata().Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isHardforkEnabled returns true if the specified hardfork is enabled at the
|
||||
// given height. nil hardfork is treated as always enabled.
|
||||
func (bc *Blockchain) isHardforkEnabled(hf *config.Hardfork, blockHeight uint32) bool {
|
||||
hfs := bc.config.Hardforks
|
||||
if hf != nil {
|
||||
start, ok := hfs[hf.String()]
|
||||
if !ok || start < blockHeight {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Run runs chain loop, it needs to be run as goroutine and executing it is
|
||||
// critical for correct Blockchain operation.
|
||||
func (bc *Blockchain) Run() {
|
||||
|
@ -2994,8 +3002,8 @@ func (bc *Blockchain) GetMaxNotValidBeforeDelta() (uint32, error) {
|
|||
if !bc.config.P2PSigExtensions {
|
||||
panic("disallowed call to Notary") // critical error, thus panic.
|
||||
}
|
||||
if bc.contracts.Notary.Metadata().UpdateHistory[0] > bc.BlockHeight() {
|
||||
return 0, fmt.Errorf("native Notary is active starting from %d", bc.contracts.Notary.Metadata().UpdateHistory[0])
|
||||
if !bc.isHardforkEnabled(bc.contracts.Notary.ActiveIn(), bc.BlockHeight()) {
|
||||
return 0, fmt.Errorf("native Notary is active starting from %s", bc.contracts.Notary.ActiveIn().String())
|
||||
}
|
||||
return bc.contracts.Notary.GetMaxNotValidBeforeDelta(bc.dao), nil
|
||||
}
|
||||
|
|
|
@ -237,26 +237,6 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) {
|
|||
require.True(t, strings.Contains(err.Error(), "can't init cache for Management native contract"), err)
|
||||
})
|
||||
*/
|
||||
t.Run("invalid native contract deactivation", func(t *testing.T) {
|
||||
ps = newPS(t)
|
||||
_, _, _, err := chain.NewMultiWithCustomConfigAndStoreNoCheck(t, func(c *config.Blockchain) {
|
||||
customConfig(c)
|
||||
c.NativeUpdateHistories = map[string][]uint32{
|
||||
nativenames.Policy: {0},
|
||||
nativenames.Neo: {0},
|
||||
nativenames.Gas: {0},
|
||||
nativenames.Designation: {0},
|
||||
nativenames.StdLib: {0},
|
||||
nativenames.Management: {0},
|
||||
nativenames.Oracle: {0},
|
||||
nativenames.Ledger: {0},
|
||||
nativenames.Notary: {0},
|
||||
nativenames.CryptoLib: {h + 10},
|
||||
}
|
||||
}, ps)
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s is already stored, but marked as inactive for height %d in config", nativenames.CryptoLib, h)), err)
|
||||
})
|
||||
t.Run("invalid native contract activation", func(t *testing.T) {
|
||||
ps = newPS(t)
|
||||
|
||||
|
@ -339,6 +319,7 @@ func TestBlockchain_InitializeNeoCache_Bug3181(t *testing.T) {
|
|||
|
||||
// This test enables Notary native contract at non-zero height and checks that no
|
||||
// Notary cache initialization is performed before that height on node restart.
|
||||
/*
|
||||
func TestBlockchain_InitializeNativeCacheWrtNativeActivations(t *testing.T) {
|
||||
const notaryEnabledHeight = 3
|
||||
ps, path := newLevelDBForTestingWithPath(t, "")
|
||||
|
@ -399,6 +380,7 @@ func TestBlockchain_InitializeNativeCacheWrtNativeActivations(t *testing.T) {
|
|||
_, err = e.Chain.GetMaxNotValidBeforeDelta()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
*/
|
||||
|
||||
func TestBlockchain_AddHeaders(t *testing.T) {
|
||||
bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) {
|
||||
|
@ -1116,25 +1098,13 @@ func TestBlockchain_MPTDeleteNoKey(t *testing.T) {
|
|||
cValidatorInvoker.Invoke(t, stackitem.Null{}, "delValue", "non-existent-key")
|
||||
}
|
||||
|
||||
// Test that UpdateHistory is added to ProtocolConfiguration for all native contracts
|
||||
// for all default configurations. If UpdateHistory is not added to config, then
|
||||
// native contract is disabled. It's easy to forget about config while adding new
|
||||
// native contract.
|
||||
func TestConfigNativeUpdateHistory(t *testing.T) {
|
||||
// Test that all default configurations are loadable.
|
||||
func TestConfig_LoadDefaultConfigs(t *testing.T) {
|
||||
var prefixPath = filepath.Join("..", "..", "config")
|
||||
check := func(t *testing.T, cfgFileSuffix any) {
|
||||
cfgPath := filepath.Join(prefixPath, fmt.Sprintf("protocol.%s.yml", cfgFileSuffix))
|
||||
cfg, err := config.LoadFile(cfgPath)
|
||||
_, err := config.LoadFile(cfgPath)
|
||||
require.NoError(t, err, fmt.Errorf("failed to load %s", cfgPath))
|
||||
natives := native.NewContracts(cfg.ProtocolConfiguration)
|
||||
assert.Equal(t, len(natives.Contracts),
|
||||
len(cfg.ProtocolConfiguration.NativeUpdateHistories),
|
||||
fmt.Errorf("protocol configuration file %s: extra or missing NativeUpdateHistory in NativeActivations section", cfgPath))
|
||||
for _, c := range natives.Contracts {
|
||||
assert.NotNil(t, cfg.ProtocolConfiguration.NativeUpdateHistories[c.Metadata().Name],
|
||||
fmt.Errorf("protocol configuration file %s: configuration for %s native contract is missing in NativeActivations section; "+
|
||||
"edit the test if the contract should be disabled", cfgPath, c.Metadata().Name))
|
||||
}
|
||||
}
|
||||
testCases := []any{
|
||||
netmode.MainNet,
|
||||
|
|
|
@ -153,6 +153,9 @@ type MethodAndPrice struct {
|
|||
// Contract is an interface for all native contracts.
|
||||
type Contract interface {
|
||||
Initialize(*Context) error
|
||||
// ActiveIn returns the hardfork native contract is active from or nil in case
|
||||
// it's always active.
|
||||
ActiveIn() *config.Hardfork
|
||||
// InitializeCache aimed to initialize contract's cache when the contract has
|
||||
// been deployed, but in-memory cached data were lost due to the node reset.
|
||||
// It should be called each time after node restart iff the contract was
|
||||
|
@ -269,12 +272,6 @@ func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
|||
})
|
||||
}
|
||||
|
||||
// IsActive returns true if the contract was deployed by the specified height.
|
||||
func (c *ContractMD) IsActive(height uint32) bool {
|
||||
history := c.UpdateHistory
|
||||
return len(history) != 0 && history[0] <= height
|
||||
}
|
||||
|
||||
// Sort sorts interop functions by id.
|
||||
func Sort(fs []Function) {
|
||||
sort.Slice(fs, func(i, j int) bool { return fs[i].ID < fs[j].ID })
|
||||
|
@ -411,6 +408,14 @@ func (ic *Context) IsHardforkEnabled(hf config.Hardfork) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// IsHardforkActivation denotes whether current block height is the height of
|
||||
// specified hardfork activation.
|
||||
func (ic *Context) IsHardforkActivation(hf config.Hardfork) bool {
|
||||
// Completely rely on proper hardforks initialisation made by core.NewBlockchain.
|
||||
height, ok := ic.Hardforks[hf.String()]
|
||||
return ok && ic.Block.Index == height
|
||||
}
|
||||
|
||||
// AddNotification creates notification event and appends it to the notification list.
|
||||
func (ic *Context) AddNotification(hash util.Uint160, name string, item *stackitem.Array) {
|
||||
ic.Notifications = append(ic.Notifications, state.NotificationEvent{
|
||||
|
|
|
@ -109,14 +109,6 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts {
|
|||
cs.Contracts = append(cs.Contracts, notary)
|
||||
}
|
||||
|
||||
setDefaultHistory := len(cfg.NativeUpdateHistories) == 0
|
||||
for _, c := range cs.Contracts {
|
||||
var history = []uint32{0}
|
||||
if !setDefaultHistory {
|
||||
history = cfg.NativeUpdateHistories[c.Metadata().Name]
|
||||
}
|
||||
c.Metadata().NativeContract.UpdateHistory = history
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
|
@ -308,3 +309,8 @@ func (c *Crypto) OnPersist(ic *interop.Context) error {
|
|||
func (c *Crypto) PostPersist(ic *interop.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (c *Crypto) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||
|
@ -194,6 +195,11 @@ func (s *Designate) Metadata() *interop.ContractMD {
|
|||
return &s.ContractMD
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (s *Designate) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Designate) getDesignatedByRole(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
r, ok := s.getRole(args[0])
|
||||
if !ok {
|
||||
|
|
|
@ -18,24 +18,30 @@ func Call(ic *interop.Context) error {
|
|||
if version != 0 {
|
||||
return fmt.Errorf("native contract of version %d is not active", version)
|
||||
}
|
||||
var meta *interop.ContractMD
|
||||
curr := ic.VM.GetCurrentScriptHash()
|
||||
var (
|
||||
c interop.Contract
|
||||
curr = ic.VM.GetCurrentScriptHash()
|
||||
)
|
||||
for _, ctr := range ic.Natives {
|
||||
m := ctr.Metadata()
|
||||
if m.Hash == curr {
|
||||
meta = m
|
||||
if ctr.Metadata().Hash == curr {
|
||||
c = ctr
|
||||
break
|
||||
}
|
||||
}
|
||||
if meta == nil {
|
||||
if c == nil {
|
||||
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
|
||||
}
|
||||
history := meta.UpdateHistory
|
||||
if len(history) == 0 {
|
||||
return fmt.Errorf("native contract %s is disabled", meta.Name)
|
||||
}
|
||||
if history[0] > ic.BlockHeight() { // persisting block must not be taken into account.
|
||||
return fmt.Errorf("native contract %s is active after height = %d", meta.Name, history[0])
|
||||
var (
|
||||
meta = c.Metadata()
|
||||
activeIn = c.ActiveIn()
|
||||
)
|
||||
if activeIn != nil {
|
||||
height, ok := ic.Hardforks[activeIn.String()]
|
||||
// Persisting block must not be taken into account, native contract can be called
|
||||
// only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled.
|
||||
if !ok || ic.BlockHeight() < height {
|
||||
return fmt.Errorf("native contract %s is active after hardfork %s", meta.Name, activeIn.String())
|
||||
}
|
||||
}
|
||||
m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
|
||||
if !ok {
|
||||
|
@ -76,7 +82,8 @@ func OnPersist(ic *interop.Context) error {
|
|||
return errors.New("onPersist must be trigered by system")
|
||||
}
|
||||
for _, c := range ic.Natives {
|
||||
if !c.Metadata().IsActive(ic.Block.Index) {
|
||||
activeIn := c.ActiveIn()
|
||||
if !(activeIn == nil || ic.IsHardforkEnabled(*activeIn)) {
|
||||
continue
|
||||
}
|
||||
err := c.OnPersist(ic)
|
||||
|
@ -93,7 +100,8 @@ func PostPersist(ic *interop.Context) error {
|
|||
return errors.New("postPersist must be trigered by system")
|
||||
}
|
||||
for _, c := range ic.Natives {
|
||||
if !c.Metadata().IsActive(ic.Block.Index) {
|
||||
activeIn := c.ActiveIn()
|
||||
if !(activeIn == nil || ic.IsHardforkEnabled(*activeIn)) {
|
||||
continue
|
||||
}
|
||||
err := c.PostPersist(ic)
|
||||
|
|
|
@ -89,48 +89,50 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
|||
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s (version 0) not found", fakeH.StringLE())), err.Error())
|
||||
})
|
||||
|
||||
t.Run("fail, bad NativeUpdateHistory height", func(t *testing.T) {
|
||||
bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) {
|
||||
c.NativeUpdateHistories = map[string][]uint32{
|
||||
nativenames.Policy: {0},
|
||||
nativenames.Neo: {0},
|
||||
nativenames.Gas: {0},
|
||||
nativenames.Designation: {0},
|
||||
nativenames.StdLib: {0},
|
||||
nativenames.Management: {0},
|
||||
nativenames.Oracle: {0},
|
||||
nativenames.Ledger: {0},
|
||||
nativenames.CryptoLib: {1},
|
||||
}
|
||||
/*
|
||||
t.Run("fail, bad NativeUpdateHistory height", func(t *testing.T) {
|
||||
bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) {
|
||||
c.NativeUpdateHistories = map[string][]uint32{
|
||||
nativenames.Policy: {0},
|
||||
nativenames.Neo: {0},
|
||||
nativenames.Gas: {0},
|
||||
nativenames.Designation: {0},
|
||||
nativenames.StdLib: {0},
|
||||
nativenames.Management: {0},
|
||||
nativenames.Oracle: {0},
|
||||
nativenames.Ledger: {0},
|
||||
nativenames.CryptoLib: {1},
|
||||
}
|
||||
})
|
||||
eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad)
|
||||
|
||||
ic, err := bcBad.GetTestVM(trigger.Application, nil, nil)
|
||||
require.NoError(t, err)
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||
input := []byte{1, 2, 3, 4}
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
// It's prohibited to call natives before NativeUpdateHistory[0] height.
|
||||
err = v.Run()
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
|
||||
|
||||
// Add new block => CryptoLib should be active now.
|
||||
eBad.AddNewBlock(t)
|
||||
ic, err = bcBad.GetTestVM(trigger.Application, nil, nil)
|
||||
require.NoError(t, err)
|
||||
v = ic.SpawnVM()
|
||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
require.NoError(t, v.Run())
|
||||
value := v.Estack().Pop().Bytes()
|
||||
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
|
||||
})
|
||||
eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad)
|
||||
|
||||
ic, err := bcBad.GetTestVM(trigger.Application, nil, nil)
|
||||
require.NoError(t, err)
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||
input := []byte{1, 2, 3, 4}
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
// It's prohibited to call natives before NativeUpdateHistory[0] height.
|
||||
err = v.Run()
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
|
||||
|
||||
// Add new block => CryptoLib should be active now.
|
||||
eBad.AddNewBlock(t)
|
||||
ic, err = bcBad.GetTestVM(trigger.Application, nil, nil)
|
||||
require.NoError(t, err)
|
||||
v = ic.SpawnVM()
|
||||
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
|
||||
v.Estack().PushVal(input)
|
||||
v.Context().Jump(md.Offset)
|
||||
|
||||
require.NoError(t, v.Run())
|
||||
value := v.Estack().Pop().Bytes()
|
||||
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
|
||||
})
|
||||
*/
|
||||
|
||||
manState := bc.GetContractState(e.NativeHash(t, nativenames.Management))
|
||||
require.NotNil(t, manState)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
|
@ -103,6 +104,11 @@ func (l *Ledger) PostPersist(ic *interop.Context) error {
|
|||
return nil // Actual block/tx processing is done in Blockchain.storeBlock().
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (l *Ledger) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
// currentHash implements currentHash SC method.
|
||||
func (l *Ledger) currentHash(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
return stackitem.Make(ic.CurrentBlockHash().BytesBE())
|
||||
|
|
|
@ -583,12 +583,13 @@ func updateContractCache(cache *ManagementCache, cs *state.Contract) {
|
|||
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||
var cache *ManagementCache
|
||||
for _, native := range ic.Natives {
|
||||
md := native.Metadata()
|
||||
history := md.UpdateHistory
|
||||
if len(history) == 0 || history[0] != ic.Block.Index {
|
||||
activeIn := native.ActiveIn()
|
||||
if !(activeIn == nil && ic.Block.Index == 0 ||
|
||||
activeIn != nil && ic.IsHardforkActivation(*activeIn)) {
|
||||
continue
|
||||
}
|
||||
|
||||
md := native.Metadata()
|
||||
cs := &state.Contract{
|
||||
ContractBase: md.ContractBase,
|
||||
}
|
||||
|
@ -672,6 +673,11 @@ func (m *Management) Initialize(ic *interop.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (m *Management) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutContractState saves given contract state into given DAO.
|
||||
func PutContractState(d *dao.Simple, cs *state.Contract) error {
|
||||
return putContractState(d, cs, true)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
|
@ -137,6 +138,11 @@ func (g *GAS) PostPersist(ic *interop.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (g *GAS) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BalanceOf returns native GAS token balance for the acc.
|
||||
func (g *GAS) BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int {
|
||||
return g.balanceOfInternal(d, acc)
|
||||
|
|
|
@ -365,6 +365,11 @@ func (n *NEO) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (n *NEO) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NEO) initConfigCache(cfg config.ProtocolConfiguration) error {
|
||||
var err error
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||
|
@ -210,6 +211,11 @@ func (n *Notary) PostPersist(ic *interop.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (n *Notary) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
// onPayment records the deposited amount as belonging to "from" address with a lock
|
||||
// till the specified chain's height.
|
||||
func (n *Notary) onPayment(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||
|
@ -258,6 +259,11 @@ func (o *Oracle) InitializeCache(blockHeight uint32, d *dao.Simple) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (o *Oracle) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getResponse(tx *transaction.Transaction) *transaction.OracleResponse {
|
||||
for i := range tx.Attributes {
|
||||
if tx.Attributes[i].Type == transaction.OracleResponseT {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
|
@ -196,6 +197,11 @@ func (p *Policy) PostPersist(ic *interop.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (p *Policy) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getFeePerByte is a Policy contract method that returns the required transaction's fee
|
||||
// per byte.
|
||||
func (p *Policy) getFeePerByte(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||
|
|
|
@ -458,6 +458,11 @@ func (s *Std) PostPersist(ic *interop.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ActiveIn implements the Contract interface.
|
||||
func (s *Std) ActiveIn() *config.Hardfork {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Std) toLimitedBytes(item stackitem.Item) []byte {
|
||||
src, err := item.TryBytes()
|
||||
if err != nil {
|
||||
|
|
|
@ -1194,7 +1194,6 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
cs := e.chain.GetContractState((*lst)[i].Hash)
|
||||
require.NotNil(t, cs)
|
||||
require.True(t, cs.ID <= 0)
|
||||
require.Equal(t, []uint32{0}, (*lst)[i].UpdateHistory)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue