diff --git a/cli/nep_test/nep17_test.go b/cli/nep_test/nep17_test.go index e7aa29393b..ace23346c0 100644 --- a/cli/nep_test/nep17_test.go +++ b/cli/nep_test/nep17_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/internal/testcli" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" @@ -236,7 +237,7 @@ func TestNEP17Transfer(t *testing.T) { e.CheckAwaitableTxPersisted(t) }) - cmd = append(cmd, "--to", address.Uint160ToString(e.Chain.GetNotaryContractScriptHash()), + cmd = append(cmd, "--to", address.Uint160ToString(nativehashes.Notary), "[", testcli.ValidatorAddr, strconv.Itoa(int(validTil)), "]") t.Run("with data", func(t *testing.T) { diff --git a/docs/node-configuration.md b/docs/node-configuration.md index 2e635ecef6..bae794d7d5 100644 --- a/docs/node-configuration.md +++ b/docs/node-configuration.md @@ -341,7 +341,7 @@ protocol-related settings described in the table below. | 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. | | 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.
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:
• Transaction attribute `NotaryAssisted`
• Network payload of the `P2PNotaryRequest` type
• Native `Notary` contract
• Notary node module | Not supported by the C# node, thus may affect heterogeneous networks functionality. | +| P2PSigExtensions | `bool` | `false` | Enables following additional Notary service related logic:
• Network payload of the `P2PNotaryRequest` type
• 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:
• `StateSyncInterval` protocol setting
• 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`). | | ReservedAttributes | `bool` | `false` | Allows to have reserved attributes range for experimental or private purposes. | | SeedList | `[]string` | [] | List of initial nodes addresses used to establish connectivity. | diff --git a/internal/basicchain/basic.go b/internal/basicchain/basic.go index 812012aa0a..888143bc81 100644 --- a/internal/basicchain/basic.go +++ b/internal/basicchain/basic.go @@ -8,6 +8,7 @@ import ( "path/filepath" "testing" + "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" @@ -81,6 +82,11 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) { t.Fatal("P2PSigExtensions should be enabled to init basic chain") } + const notaryDepositHeight uint32 = 8 + echidnaH, ok := e.Chain.GetConfig().Hardforks[config.HFEchidna.String()] + require.Truef(t, ok, "%s hardfork should be enabled since basic chain uses Notary contract", config.HFEchidna.String()) + require.LessOrEqualf(t, echidnaH, notaryDepositHeight-1, "%s hardfork should be enabled starting from height %d, got: %d", config.HFEchidna.String(), notaryDepositHeight-1, echidnaH) + var ( // examplesPrefix is a prefix of the example smart-contracts. examplesPrefix = filepath.Join(rootpath, "examples") @@ -189,6 +195,7 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) { // Block #8: deposit some GAS to notary contract for priv0. transferTxH = gasPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, notaryHash, 10_0000_0000, []any{priv0ScriptHash, int64(e.Chain.BlockHeight() + 1000)}) t.Logf("notaryDepositTxPriv0: %v", transferTxH.StringLE()) + require.Equal(t, uint32(notaryDepositHeight), e.Chain.BlockHeight(), "notaryDepositHeight constant is out of date") // Block #9: designate new Notary node. ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json")) diff --git a/internal/fakechain/fakechain.go b/internal/fakechain/fakechain.go index a2553b61a1..e0d885d68e 100644 --- a/internal/fakechain/fakechain.go +++ b/internal/fakechain/fakechain.go @@ -24,19 +24,18 @@ import ( type FakeChain struct { config.Blockchain *mempool.Pool - blocksCh []chan *block.Block - Blockheight atomic.Uint32 - PoolTxF func(*transaction.Transaction) error - poolTxWithData func(*transaction.Transaction, any, *mempool.Pool) error - blocks map[util.Uint256]*block.Block - hdrHashes map[uint32]util.Uint256 - txs map[util.Uint256]*transaction.Transaction - VerifyWitnessF func() (int64, error) - MaxVerificationGAS int64 - NotaryContractScriptHash util.Uint160 - NotaryDepositExpiration uint32 - PostBlock []func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block) - UtilityTokenBalance *big.Int + blocksCh []chan *block.Block + Blockheight atomic.Uint32 + PoolTxF func(*transaction.Transaction) error + poolTxWithData func(*transaction.Transaction, any, *mempool.Pool) error + blocks map[util.Uint256]*block.Block + hdrHashes map[uint32]util.Uint256 + txs map[util.Uint256]*transaction.Transaction + VerifyWitnessF func() (int64, error) + MaxVerificationGAS int64 + NotaryDepositExpiration uint32 + PostBlock []func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block) + UtilityTokenBalance *big.Int } // FakeStateSync implements the StateSync interface. @@ -111,14 +110,6 @@ func (chain *FakeChain) GetNotaryDepositExpiration(acc util.Uint160) uint32 { panic("TODO") } -// GetNotaryContractScriptHash implements the Blockchainer interface. -func (chain *FakeChain) GetNotaryContractScriptHash() util.Uint160 { - if !chain.NotaryContractScriptHash.Equals(util.Uint160{}) { - return chain.NotaryContractScriptHash - } - panic("TODO") -} - // GetNotaryBalance implements the Blockchainer interface. func (chain *FakeChain) GetNotaryBalance(acc util.Uint160) *big.Int { panic("TODO") diff --git a/pkg/config/hardfork.go b/pkg/config/hardfork.go index 0cb80bc312..e60dd22f0d 100644 --- a/pkg/config/hardfork.go +++ b/pkg/config/hardfork.go @@ -43,6 +43,9 @@ const ( // different ApplicationLogs comparing to the C# node, but the node states // match. See #3485 for details. HFDomovoi // Domovoi + // HFEchidna represents hard-fork introduced in #3476 (ported from + // https://github.com/neo-project/neo/pull/3175). + HFEchidna // Echidna // hfLast denotes the end of hardforks enum. Consider adding new hardforks // before hfLast. hfLast diff --git a/pkg/config/hardfork_string.go b/pkg/config/hardfork_string.go index f45f1b3ca2..834f43075f 100644 --- a/pkg/config/hardfork_string.go +++ b/pkg/config/hardfork_string.go @@ -13,14 +13,16 @@ func _() { _ = x[HFBasilisk-2] _ = x[HFCockatrice-4] _ = x[HFDomovoi-8] - _ = x[hfLast-16] + _ = x[HFEchidna-16] + _ = x[hfLast-32] } const ( _Hardfork_name_0 = "DefaultAspidocheloneBasilisk" _Hardfork_name_1 = "Cockatrice" _Hardfork_name_2 = "Domovoi" - _Hardfork_name_3 = "hfLast" + _Hardfork_name_3 = "Echidna" + _Hardfork_name_4 = "hfLast" ) var ( @@ -37,6 +39,8 @@ func (i Hardfork) String() string { return _Hardfork_name_2 case i == 16: return _Hardfork_name_3 + case i == 32: + return _Hardfork_name_4 default: return "Hardfork(" + strconv.FormatInt(int64(i), 10) + ")" } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 93c98577e8..8b06235b42 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -22,6 +22,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mpt" "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "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/stateroot" @@ -1070,7 +1071,7 @@ func (bc *Blockchain) isHardforkEnabled(hf *config.Hardfork, blockHeight uint32) hfs := bc.config.Hardforks if hf != nil { start, ok := hfs[hf.String()] - if !ok || start < blockHeight { + if !ok || start > blockHeight { return false } } @@ -2104,26 +2105,30 @@ func (bc *Blockchain) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint } // GetNotaryBalance returns Notary deposit amount for the specified account. +// Default value is returned if Notary contract is not yet active. func (bc *Blockchain) GetNotaryBalance(acc util.Uint160) *big.Int { + if !bc.isHardforkEnabled(bc.contracts.Notary.ActiveIn(), bc.BlockHeight()) { + return nil + } return bc.contracts.Notary.BalanceOf(bc.dao, acc) } // GetNotaryServiceFeePerKey returns a NotaryAssisted transaction attribute fee // per key which is a reward per notary request key for designated notary nodes. +// Default value is returned if Notary contract is not yet active. func (bc *Blockchain) GetNotaryServiceFeePerKey() int64 { - return bc.contracts.Policy.GetAttributeFeeInternal(bc.dao, transaction.NotaryAssistedT) -} - -// GetNotaryContractScriptHash returns Notary native contract hash. -func (bc *Blockchain) GetNotaryContractScriptHash() util.Uint160 { - if bc.P2PSigExtensionsEnabled() { - return bc.contracts.Notary.Hash + if !bc.isHardforkEnabled(&transaction.NotaryAssistedActivation, bc.BlockHeight()) { + return 0 } - return util.Uint160{} + return bc.contracts.Policy.GetAttributeFeeInternal(bc.dao, transaction.NotaryAssistedT) } // GetNotaryDepositExpiration returns Notary deposit expiration height for the specified account. +// Default value is returned if Notary contract is not yet active. func (bc *Blockchain) GetNotaryDepositExpiration(acc util.Uint160) uint32 { + if !bc.isHardforkEnabled(bc.contracts.Notary.ActiveIn(), bc.BlockHeight()) { + return 0 + } return bc.contracts.Notary.ExpirationOf(bc.dao, acc) } @@ -2695,10 +2700,10 @@ func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transact return fmt.Errorf("%w: conflicting transaction %s is already on chain", ErrInvalidAttribute, conflicts.Hash.StringLE()) } case transaction.NotaryAssistedT: - if !bc.config.P2PSigExtensions { - return fmt.Errorf("%w: NotaryAssisted attribute was found, but P2PSigExtensions are disabled", ErrInvalidAttribute) + if !bc.isHardforkEnabled(&transaction.NotaryAssistedActivation, bc.BlockHeight()) { + return fmt.Errorf("%w: NotaryAssisted attribute was found, but %s is not active yet", ErrInvalidAttribute, transaction.NotaryAssistedActivation) } - if !tx.HasSigner(bc.contracts.Notary.Hash) { + if !tx.HasSigner(nativehashes.Notary) { return fmt.Errorf("%w: NotaryAssisted attribute was found, but transaction is not signed by the Notary native contract", ErrInvalidAttribute) } default: @@ -3088,9 +3093,6 @@ func (bc *Blockchain) GetMaxVerificationGAS() int64 { // GetMaxNotValidBeforeDelta returns maximum NotValidBeforeDelta Notary limit. func (bc *Blockchain) GetMaxNotValidBeforeDelta() (uint32, error) { - if !bc.config.P2PSigExtensions { - panic("disallowed call to Notary") // critical error, thus panic. - } 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()) } diff --git a/pkg/core/blockchain_core_test.go b/pkg/core/blockchain_core_test.go index 0365338d71..c5f24995fc 100644 --- a/pkg/core/blockchain_core_test.go +++ b/pkg/core/blockchain_core_test.go @@ -371,6 +371,7 @@ func TestNewBlockchain_InitHardforks(t *testing.T) { config.HFBasilisk.String(): 0, config.HFCockatrice.String(): 0, config.HFDomovoi.String(): 0, + config.HFEchidna.String(): 0, }, bc.GetConfig().Hardforks) }) t.Run("empty set", func(t *testing.T) { @@ -401,7 +402,7 @@ func TestNewBlockchain_InitHardforks(t *testing.T) { }) t.Run("all present", func(t *testing.T) { bc := newTestChainWithCustomCfg(t, func(c *config.Config) { - c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFAspidochelone.String(): 5, config.HFBasilisk.String(): 10, config.HFCockatrice.String(): 15, config.HFDomovoi.String(): 20} + c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFAspidochelone.String(): 5, config.HFBasilisk.String(): 10, config.HFCockatrice.String(): 15, config.HFDomovoi.String(): 20, config.HFEchidna.String(): 25} require.NoError(t, c.ProtocolConfiguration.Validate()) }) require.Equal(t, map[string]uint32{ @@ -409,6 +410,7 @@ func TestNewBlockchain_InitHardforks(t *testing.T) { config.HFBasilisk.String(): 10, config.HFCockatrice.String(): 15, config.HFDomovoi.String(): 20, + config.HFEchidna.String(): 25, }, bc.GetConfig().Hardforks) }) } diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index 9f0db26031..482ec7b95e 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -271,7 +271,7 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) { _, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) require.Error(t, err) - require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Domovoi (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err) + require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Echidna (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err) }) t.Run("good", func(t *testing.T) { @@ -2057,7 +2057,12 @@ func TestBlockchain_VerifyTx(t *testing.T) { } t.Run("Disabled", func(t *testing.T) { bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { - c.P2PSigExtensions = false + c.P2PSigExtensions = true + c.Hardforks = map[string]uint32{ + config.HFAspidochelone.String(): 0, + config.HFBasilisk.String(): 0, + config.HFCockatrice.String(): 0, + } c.ReservedAttributes = false }) eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad) @@ -2069,7 +2074,7 @@ func TestBlockchain_VerifyTx(t *testing.T) { eBad.SignTx(t, tx, 1_0000_0000, eBad.Committee) err := bcBad.VerifyTx(tx) require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "invalid attribute: NotaryAssisted attribute was found, but P2PSigExtensions are disabled")) + require.True(t, strings.Contains(err.Error(), "invalid attribute: NotaryAssisted attribute was found, but Echidna is not active yet")) }) t.Run("Enabled, insufficient network fee", func(t *testing.T) { tx := getNotaryAssistedTx(e, 1, 0) @@ -2572,7 +2577,7 @@ func TestBlockchain_GenesisTransactionExtension(t *testing.T) { // in the right order. func TestNativenames(t *testing.T) { bc, _ := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) { - cfg.Hardforks = map[string]uint32{} + cfg.Hardforks = nil // default (all hardforks enabled) behaviour. cfg.P2PSigExtensions = true }) natives := bc.GetNatives() diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 23027e3d9a..67c2ff0edb 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -249,7 +249,7 @@ func (c *ContractMD) HFSpecificContractMD(hf *config.Hardfork) *HFSpecificContra } md, ok := c.mdCache[key] if !ok { - panic(fmt.Errorf("native contract descriptor cache is not initialized: contract %s, hardfork %s", c.Hash.StringLE(), key)) + panic(fmt.Errorf("native contract descriptor cache is not initialized: contract %s (%s), hardfork %s", c.Hash.StringLE(), c.Name, key)) } if md == nil { panic(fmt.Errorf("native contract descriptor cache is nil: contract %s, hardfork %s", c.Hash.StringLE(), key)) diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index a65631d3b6..3949062518 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -72,9 +72,9 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts { cs.Ledger = ledger cs.Contracts = append(cs.Contracts, ledger) - gas := newGAS(int64(cfg.InitialGASSupply), cfg.P2PSigExtensions) + gas := newGAS(int64(cfg.InitialGASSupply)) neo := newNEO(cfg) - policy := newPolicy(cfg.P2PSigExtensions) + policy := newPolicy() neo.GAS = gas neo.Policy = policy gas.NEO = neo @@ -100,15 +100,13 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts { cs.Oracle = oracle cs.Contracts = append(cs.Contracts, oracle) - if cfg.P2PSigExtensions { - notary := newNotary() - notary.GAS = gas - notary.NEO = neo - notary.Desig = desig - notary.Policy = policy - cs.Notary = notary - cs.Contracts = append(cs.Contracts, notary) - } + notary := newNotary() + notary.GAS = gas + notary.NEO = neo + notary.Desig = desig + notary.Policy = policy + cs.Notary = notary + cs.Contracts = append(cs.Contracts, notary) return cs } diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 2633f6f8a0..923240e58c 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -610,8 +610,6 @@ func (m *Management) OnPersist(ic *interop.Context) error { for _, hf := range config.Hardforks { if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) { isUpdate = true - activation := hf // avoid loop variable pointer exporting. - activeIn = &activation // reuse ActiveIn variable for the initialization hardfork. // Break immediately since native Initialize should be called starting from the first hardfork in a raw // (if there are multiple hardforks with the same enabling height). break @@ -626,6 +624,10 @@ func (m *Management) OnPersist(ic *interop.Context) error { currentActiveHFs = append(currentActiveHFs, hf) } } + // activeIn is not included into the activeHFs list. + if activeIn != nil && activeIn.Cmp(latestHF) > 0 { + latestHF = *activeIn + } if !(isDeploy || isUpdate) { continue } @@ -668,7 +670,7 @@ func (m *Management) OnPersist(ic *interop.Context) error { // The rest of activating hardforks also require initialization. for _, hf := range currentActiveHFs { if err := native.Initialize(ic, &hf, hfSpecificMD); err != nil { - return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err) + return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, hf, err) } } diff --git a/pkg/core/native/management_test.go b/pkg/core/native/management_test.go index 73859507b1..df10d4b868 100644 --- a/pkg/core/native/management_test.go +++ b/pkg/core/native/management_test.go @@ -17,7 +17,7 @@ import ( func TestDeployGetUpdateDestroyContract(t *testing.T) { mgmt := newManagement() - mgmt.Policy = newPolicy(false) + mgmt.Policy = newPolicy() d := dao.NewSimple(storage.NewMemoryStore(), false) ic := &interop.Context{DAO: d} err := mgmt.Initialize(ic, nil, nil) @@ -95,7 +95,7 @@ func TestManagement_Initialize(t *testing.T) { func TestManagement_GetNEP17Contracts(t *testing.T) { mgmt := newManagement() - mgmt.Policy = newPolicy(false) + mgmt.Policy = newPolicy() d := dao.NewSimple(storage.NewMemoryStore(), false) err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil) require.NoError(t, err) diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 92f1aed7fb..7cfe34bce1 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -22,8 +22,7 @@ type GAS struct { NEO *NEO Policy *Policy - initialSupply int64 - p2pSigExtensionsEnabled bool + initialSupply int64 } const gasContractID = -6 @@ -32,10 +31,9 @@ const gasContractID = -6 const GASFactor = NEOTotalSupply // newGAS returns GAS native contract. -func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS { +func newGAS(init int64) *GAS { g := &GAS{ - initialSupply: init, - p2pSigExtensionsEnabled: p2pSigExtensionsEnabled, + initialSupply: init, } defer g.BuildHFSpecificMD(g.ActiveIn()) @@ -122,14 +120,12 @@ func (g *GAS) OnPersist(ic *interop.Context) error { var netFee int64 for _, tx := range ic.Block.Transactions { netFee += tx.NetworkFee - if g.p2pSigExtensionsEnabled { - // Reward for NotaryAssisted attribute will be minted to designated notary nodes - // by Notary contract. - attrs := tx.GetAttributes(transaction.NotaryAssistedT) - if len(attrs) != 0 { - na := attrs[0].Value.(*transaction.NotaryAssisted) - netFee -= (int64(na.NKeys) + 1) * g.Policy.GetAttributeFeeInternal(ic.DAO, transaction.NotaryAssistedT) - } + // Reward for NotaryAssisted attribute will be minted to designated notary nodes + // by Notary contract. + attrs := tx.GetAttributes(transaction.NotaryAssistedT) + if len(attrs) != 0 { + na := attrs[0].Value.(*transaction.NotaryAssisted) + netFee -= (int64(na.NKeys) + 1) * g.Policy.GetAttributeFeeInternal(ic.DAO, transaction.NotaryAssistedT) } } g.mint(ic, primary, big.NewInt(int64(netFee)), false) diff --git a/pkg/core/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go index 9870e81f01..498eb19271 100644 --- a/pkg/core/native/native_test/management_test.go +++ b/pkg/core/native/native_test/management_test.go @@ -47,7 +47,6 @@ var ( nativenames.Policy: `{"id":-7,"hash":"0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"PolicyContract","abi":{"methods":[{"name":"blockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false},{"name":"getAttributeFee","offset":7,"parameters":[{"name":"attributeType","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"getExecFeeFactor","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"getFeePerByte","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"getStoragePrice","offset":28,"parameters":[],"returntype":"Integer","safe":true},{"name":"isBlocked","offset":35,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":true},{"name":"setAttributeFee","offset":42,"parameters":[{"name":"attributeType","type":"Integer"},{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setExecFeeFactor","offset":49,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setFeePerByte","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setStoragePrice","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"unblockAccount","offset":70,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`, nativenames.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`, nativenames.Oracle: `{"id":-9,"hash":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"OracleContract","abi":{"methods":[{"name":"finish","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"getPrice","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"request","offset":14,"parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setPrice","offset":21,"parameters":[{"name":"price","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":28,"parameters":[],"returntype":"Boolean","safe":true}],"events":[{"name":"OracleRequest","parameters":[{"name":"Id","type":"Integer"},{"name":"RequestContract","type":"Hash160"},{"name":"Url","type":"String"},{"name":"Filter","type":"String"}]},{"name":"OracleResponse","parameters":[{"name":"Id","type":"Integer"},{"name":"OriginalTx","type":"Hash256"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`, - nativenames.Notary: `{"id":-10,"hash":"0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"Notary","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"expirationOf","offset":7,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"getMaxNotValidBeforeDelta","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"lockDepositUntil","offset":21,"parameters":[{"name":"address","type":"Hash160"},{"name":"till","type":"Integer"}],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"setMaxNotValidBeforeDelta","offset":35,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":42,"parameters":[{"name":"signature","type":"Signature"}],"returntype":"Boolean","safe":true},{"name":"withdraw","offset":49,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`, } // cockatriceCSS holds serialized native contract states built for genesis block (with UpdateCounter 0) // under assumption that hardforks from Aspidochelone to Cockatrice (included) are enabled. @@ -55,6 +54,12 @@ var ( nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"keccak256","offset":42,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":49,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":70,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`, nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1325686241},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":77,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":84,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":91,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":98,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":105,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":112,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":119,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":126,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":133,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`, } + // echidnaCSS holds serialized native contract states built for genesis block (with UpdateCounter 0) + // under assumption that hardforks from Aspidochelone to Echidna (included) are enabled. + echidnaCSS = map[string]string{ + nativenames.Notary: `{"id":-10,"hash":"0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"Notary","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"expirationOf","offset":7,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"getMaxNotValidBeforeDelta","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"lockDepositUntil","offset":21,"parameters":[{"name":"address","type":"Hash160"},{"name":"till","type":"Integer"}],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"setMaxNotValidBeforeDelta","offset":35,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":42,"parameters":[{"name":"signature","type":"Signature"}],"returntype":"Boolean","safe":true},{"name":"withdraw","offset":49,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`, + nativenames.Policy: `{"id":-7,"hash":"0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"PolicyContract","abi":{"methods":[{"name":"blockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false},{"name":"getAttributeFee","offset":7,"parameters":[{"name":"attributeType","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"getExecFeeFactor","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"getFeePerByte","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"getStoragePrice","offset":28,"parameters":[],"returntype":"Integer","safe":true},{"name":"isBlocked","offset":35,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":true},{"name":"setAttributeFee","offset":42,"parameters":[{"name":"attributeType","type":"Integer"},{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setExecFeeFactor","offset":49,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setFeePerByte","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setStoragePrice","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"unblockAccount","offset":70,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`, + } ) func init() { @@ -63,6 +68,11 @@ func init() { cockatriceCSS[k] = v } } + for k, v := range cockatriceCSS { + if _, ok := echidnaCSS[k]; !ok { + echidnaCSS[k] = v + } + } } func newManagementClient(t *testing.T) *neotest.ContractInvoker { @@ -91,6 +101,10 @@ func TestManagement_GenesisNativeState(t *testing.T) { h := state.CreateNativeContractHash(name) c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { si := stack[0] + if _, ok := expected[name]; !ok { + require.Equal(t, stackitem.Null{}, si, fmt.Errorf("contract %s state found", name)) + return + } var cs = &state.Contract{} require.NoError(t, cs.FromStackItem(si), name) jBytes, err := ojson.Marshal(cs) @@ -113,6 +127,7 @@ func TestManagement_GenesisNativeState(t *testing.T) { config.HFAspidochelone.String(): 100500, config.HFBasilisk.String(): 100500, config.HFCockatrice.String(): 100500, + config.HFEchidna.String(): 100500, } cfg.P2PSigExtensions = true }) @@ -148,15 +163,31 @@ func TestManagement_GenesisNativeState(t *testing.T) { }) check(t, mgmt, cockatriceCSS) }) + t.Run("Echidna enabled", func(t *testing.T) { + mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) { + cfg.Hardforks = map[string]uint32{ + config.HFAspidochelone.String(): 0, + config.HFBasilisk.String(): 0, + config.HFCockatrice.String(): 0, + config.HFEchidna.String(): 0, + } + cfg.P2PSigExtensions = true + }) + check(t, mgmt, echidnaCSS) + }) } func TestManagement_NativeDeployUpdateNotifications(t *testing.T) { - const cockatriceHeight = 3 + const ( + cockatriceHeight = 3 + echidnaHeight = 5 + ) mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) { cfg.Hardforks = map[string]uint32{ config.HFAspidochelone.String(): 0, config.HFBasilisk.String(): 0, config.HFCockatrice.String(): cockatriceHeight, + config.HFEchidna.String(): echidnaHeight, } cfg.P2PSigExtensions = true }) @@ -169,6 +200,8 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) { var expected []state.NotificationEvent for _, name := range nativenames.All { switch name { + case nativenames.Notary: + continue case nativenames.Gas: expected = append(expected, state.NotificationEvent{ ScriptHash: nativehashes.GasToken, @@ -200,7 +233,7 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) { } require.Equal(t, expected, aer[0].Events) - // Generate some blocks and check Update notifications. + // Generate some blocks and check Update notifications for Cockatrice hardfork. cockatriceBlock := mgmt.GenerateNewBlocks(t, cockatriceHeight)[cockatriceHeight-1] aer, err = mgmt.Chain.GetAppExecResults(cockatriceBlock.Hash(), trigger.OnPersist) require.NoError(t, err) @@ -216,15 +249,42 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) { }) } require.Equal(t, expected, aer[0].Events) + + // Generate some blocks and check Deploy notifications for Echidna hardfork. + mgmt.GenerateNewBlocks(t, echidnaHeight-int(mgmt.Chain.BlockHeight())) + aer, err = mgmt.Chain.GetAppExecResults(mgmt.Chain.GetHeaderHash(mgmt.Chain.BlockHeight()), trigger.OnPersist) + require.NoError(t, err) + require.Equal(t, 1, len(aer)) + expected = expected[:0] + expected = append(expected, state.NotificationEvent{ + ScriptHash: nativehashes.ContractManagement, + Name: "Update", + Item: stackitem.NewArray([]stackitem.Item{ + stackitem.Make(nativehashes.PolicyContract), + }), + }) + expected = append(expected, state.NotificationEvent{ + ScriptHash: nativehashes.ContractManagement, + Name: "Deploy", + Item: stackitem.NewArray([]stackitem.Item{ + stackitem.Make(nativehashes.Notary), + }), + }) + require.Equal(t, expected, aer[0].Events) } func TestManagement_NativeUpdate(t *testing.T) { - const cockatriceHeight = 3 + const ( + cockatriceHeight = 3 + echidnaHeight = 6 + ) + c := newCustomManagementClient(t, func(cfg *config.Blockchain) { cfg.Hardforks = map[string]uint32{ config.HFAspidochelone.String(): 0, config.HFBasilisk.String(): 0, config.HFCockatrice.String(): cockatriceHeight, + config.HFEchidna.String(): echidnaHeight, } cfg.P2PSigExtensions = true }) @@ -235,7 +295,12 @@ func TestManagement_NativeUpdate(t *testing.T) { for _, name := range nativenames.All { h := state.CreateNativeContractHash(name) cs := c.Chain.GetContractState(h) - require.NotNil(t, cs, name) + if name == nativenames.Notary { + require.Nil(t, cs, name) + continue + } else { + require.NotNil(t, cs, name) + } jBytes, err := ojson.Marshal(cs) require.NoError(t, err, name) require.Equal(t, defaultCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name)) @@ -247,16 +312,50 @@ func TestManagement_NativeUpdate(t *testing.T) { for _, name := range nativenames.All { h := state.CreateNativeContractHash(name) cs := c.Chain.GetContractState(h) - require.NotNil(t, cs, name) + if name == nativenames.Notary { + require.Nil(t, cs, name) + continue + } else { + require.NotNil(t, cs, name) + } + var actual = cs if name == nativenames.Neo || name == nativenames.CryptoLib { // A tiny hack to reuse cockatriceCSS map in the check below. require.Equal(t, uint16(1), cs.UpdateCounter, name) - cs.UpdateCounter-- + cp := *cs + actual = &cp // avoid Management cache corruption. + actual.UpdateCounter-- } - jBytes, err := ojson.Marshal(cs) + jBytes, err := ojson.Marshal(actual) require.NoError(t, err, name) require.Equal(t, cockatriceCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name)) } + + // Add some blocks up to the Echidna enabling height and check the natives state. + for i := c.Chain.BlockHeight(); i < echidnaHeight-1; i++ { + c.AddNewBlock(t) + for _, name := range nativenames.All { + h := state.CreateNativeContractHash(name) + cs := c.Chain.GetContractState(h) + if name == nativenames.Notary { + require.Nil(t, cs, name) + continue + } else { + require.NotNil(t, cs, name) + } + var actual = cs + if name == nativenames.Neo || name == nativenames.CryptoLib { + // A tiny hack to reuse echidnaCSS map in the check below. + require.Equal(t, uint16(1), cs.UpdateCounter, name) + cp := *cs + actual = &cp // avoid Management cache corruption. + actual.UpdateCounter-- + } + jBytes, err := ojson.Marshal(actual) + require.NoError(t, err, name) + require.Equal(t, echidnaCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name)) + } + } } func TestManagement_NativeUpdate_Call(t *testing.T) { @@ -292,12 +391,18 @@ func TestManagement_NativeUpdate_Call(t *testing.T) { // different block heights depending on hardfork settings. This test is located here since it // depends on defaultCSS and cockatriceCSS. func TestBlockchain_GetNatives(t *testing.T) { - const cockatriceHeight = 3 + const ( + cockatriceHeight = 3 + domovoiHeight = 5 + echidnaHeight = 6 + ) bc, acc := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) { cfg.Hardforks = map[string]uint32{ config.HFAspidochelone.String(): 0, config.HFBasilisk.String(): 0, config.HFCockatrice.String(): cockatriceHeight, + config.HFDomovoi.String(): domovoiHeight, + config.HFEchidna.String(): echidnaHeight, } cfg.P2PSigExtensions = true }) @@ -305,7 +410,7 @@ func TestBlockchain_GetNatives(t *testing.T) { // Check genesis-based native contract states. natives := bc.GetNatives() - require.Equal(t, len(nativenames.All), len(natives)) + require.Equal(t, len(nativenames.All)-1, len(natives)) // Notary is deployed starting from D hardfork. for _, cs := range natives { csFull := state.Contract{ ContractBase: cs.ContractBase, @@ -316,10 +421,10 @@ func TestBlockchain_GetNatives(t *testing.T) { require.Equal(t, defaultCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name)) } - // Check native state after update. + // Check native state after Cockatrice. e.GenerateNewBlocks(t, cockatriceHeight) natives = bc.GetNatives() - require.Equal(t, len(nativenames.All), len(natives)) + require.Equal(t, len(nativenames.All)-1, len(natives)) // Notary is deployed starting from D hardfork. for _, cs := range natives { csFull := state.Contract{ ContractBase: cs.ContractBase, @@ -329,6 +434,20 @@ func TestBlockchain_GetNatives(t *testing.T) { require.NoError(t, err, cs.Manifest.Name) require.Equal(t, cockatriceCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name)) } + + // Check native state after Echidna. + e.GenerateNewBlocks(t, echidnaHeight-cockatriceHeight) + natives = bc.GetNatives() + require.Equal(t, len(nativenames.All), len(natives)) + for _, cs := range natives { + csFull := state.Contract{ + ContractBase: cs.ContractBase, + UpdateCounter: 0, // Since we're comparing only state.NativeContract part, set the update counter to 0 to match the echidnaCSS. + } + jBytes, err := ojson.Marshal(csFull) + require.NoError(t, err, cs.Manifest.Name) + require.Equal(t, echidnaCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name)) + } } func TestManagement_ContractCache(t *testing.T) { diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index fa3cab4361..95a78efce0 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -52,7 +52,10 @@ const ( defaultMaxNotValidBeforeDelta = 140 // 20 rounds for 7 validators, a little more than half an hour ) -var maxNotValidBeforeDeltaKey = []byte{10} +var ( + maxNotValidBeforeDeltaKey = []byte{10} + activeIn = config.HFEchidna +) var ( _ interop.Contract = (*Notary)(nil) @@ -200,7 +203,7 @@ func (n *Notary) PostPersist(ic *interop.Context) error { // ActiveIn implements the Contract interface. func (n *Notary) ActiveIn() *config.Hardfork { - return nil + return &activeIn } // onPayment records the deposited amount as belonging to "from" address with a lock diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index 0591fe7b44..a8a823639e 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -63,9 +63,6 @@ var ( type Policy struct { interop.ContractMD NEO *NEO - - // p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant. - p2pSigExtensionsEnabled bool } type PolicyCache struct { @@ -100,11 +97,8 @@ func copyPolicyCache(src, dst *PolicyCache) { } // newPolicy returns Policy native contract. -func newPolicy(p2pSigExtensionsEnabled bool) *Policy { - p := &Policy{ - ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID), - p2pSigExtensionsEnabled: p2pSigExtensionsEnabled, - } +func newPolicy() *Policy { + p := &Policy{ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID)} defer p.BuildHFSpecificMD(p.ActiveIn()) desc := newDescriptor("getFeePerByte", smartcontract.IntegerType) @@ -136,13 +130,24 @@ func newPolicy(p2pSigExtensionsEnabled bool) *Policy { desc = newDescriptor("getAttributeFee", smartcontract.IntegerType, manifest.NewParameter("attributeType", smartcontract.IntegerType)) - md = newMethodAndPrice(p.getAttributeFee, 1<<15, callflag.ReadStates) + md = newMethodAndPrice(p.getAttributeFeeV0, 1<<15, callflag.ReadStates, config.HFDefault, transaction.NotaryAssistedActivation) + p.AddMethod(md, desc) + + desc = newDescriptor("getAttributeFee", smartcontract.IntegerType, + manifest.NewParameter("attributeType", smartcontract.IntegerType)) + md = newMethodAndPrice(p.getAttributeFeeV1, 1<<15, callflag.ReadStates, transaction.NotaryAssistedActivation) p.AddMethod(md, desc) desc = newDescriptor("setAttributeFee", smartcontract.VoidType, manifest.NewParameter("attributeType", smartcontract.IntegerType), manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(p.setAttributeFee, 1<<15, callflag.States) + md = newMethodAndPrice(p.setAttributeFeeV0, 1<<15, callflag.States, config.HFDefault, transaction.NotaryAssistedActivation) + p.AddMethod(md, desc) + + desc = newDescriptor("setAttributeFee", smartcontract.VoidType, + manifest.NewParameter("attributeType", smartcontract.IntegerType), + manifest.NewParameter("value", smartcontract.IntegerType)) + md = newMethodAndPrice(p.setAttributeFeeV1, 1<<15, callflag.States, transaction.NotaryAssistedActivation) p.AddMethod(md, desc) desc = newDescriptor("setFeePerByte", smartcontract.VoidType, @@ -170,27 +175,27 @@ func (p *Policy) Metadata() *interop.ContractMD { // Initialize initializes Policy native contract and implements the Contract interface. func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { - if hf != p.ActiveIn() { - return nil - } - - setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte) - setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor) - setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice) - - cache := &PolicyCache{ - execFeeFactor: defaultExecFeeFactor, - feePerByte: defaultFeePerByte, - maxVerificationGas: defaultMaxVerificationGas, - storagePrice: DefaultStoragePrice, - attributeFee: map[transaction.AttrType]uint32{}, - blockedAccounts: make([]util.Uint160, 0), + if hf == p.ActiveIn() { + setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte) + setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor) + setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice) + + cache := &PolicyCache{ + execFeeFactor: defaultExecFeeFactor, + feePerByte: defaultFeePerByte, + maxVerificationGas: defaultMaxVerificationGas, + storagePrice: DefaultStoragePrice, + attributeFee: map[transaction.AttrType]uint32{}, + blockedAccounts: make([]util.Uint160, 0), + } + ic.DAO.SetCache(p.ID, cache) } - if p.p2pSigExtensionsEnabled { + if hf != nil && *hf == transaction.NotaryAssistedActivation { setIntWithKey(p.ID, ic.DAO, []byte{attributeFeePrefix, byte(transaction.NotaryAssistedT)}, defaultNotaryAssistedFee) + + cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache) cache.attributeFee[transaction.NotaryAssistedT] = defaultNotaryAssistedFee } - ic.DAO.SetCache(p.ID, cache) return nil } @@ -363,9 +368,18 @@ func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) sta return stackitem.Null{} } -func (p *Policy) getAttributeFee(ic *interop.Context, args []stackitem.Item) stackitem.Item { +func (p *Policy) getAttributeFeeV0(ic *interop.Context, args []stackitem.Item) stackitem.Item { + return p.getAttributeFeeGeneric(ic, args, false) +} + +func (p *Policy) getAttributeFeeV1(ic *interop.Context, args []stackitem.Item) stackitem.Item { + return p.getAttributeFeeGeneric(ic, args, true) +} + +func (p *Policy) getAttributeFeeGeneric(ic *interop.Context, args []stackitem.Item, allowNotaryAssisted bool) stackitem.Item { t := transaction.AttrType(toUint8(args[0])) - if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) { + if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) || + (!allowNotaryAssisted && t == transaction.NotaryAssistedT) { panic(fmt.Errorf("invalid attribute type: %d", t)) } return stackitem.NewBigInteger(big.NewInt(p.GetAttributeFeeInternal(ic.DAO, t))) @@ -382,10 +396,19 @@ func (p *Policy) GetAttributeFeeInternal(d *dao.Simple, t transaction.AttrType) return int64(v) } -func (p *Policy) setAttributeFee(ic *interop.Context, args []stackitem.Item) stackitem.Item { +func (p *Policy) setAttributeFeeV0(ic *interop.Context, args []stackitem.Item) stackitem.Item { + return p.setAttributeFeeGeneric(ic, args, false) +} + +func (p *Policy) setAttributeFeeV1(ic *interop.Context, args []stackitem.Item) stackitem.Item { + return p.setAttributeFeeGeneric(ic, args, true) +} + +func (p *Policy) setAttributeFeeGeneric(ic *interop.Context, args []stackitem.Item, allowNotaryAssisted bool) stackitem.Item { t := transaction.AttrType(toUint8(args[0])) value := toUint32(args[1]) - if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) { + if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) || + (!allowNotaryAssisted && t == transaction.NotaryAssistedT) { panic(fmt.Errorf("invalid attribute type: %d", t)) } if value > maxAttributeFee { diff --git a/pkg/core/native/policy_test.go b/pkg/core/native/policy_test.go index 9569dd6b83..a1d95fe568 100644 --- a/pkg/core/native/policy_test.go +++ b/pkg/core/native/policy_test.go @@ -4,9 +4,12 @@ import ( "fmt" "testing" + "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/neotest/chain" "github.com/stretchr/testify/require" @@ -65,3 +68,51 @@ func TestPolicy_BlockedAccounts(t *testing.T) { require.Equal(t, expectedErr, err.Error()) }) } + +func TestPolicy_GetNotaryFeePerKey(t *testing.T) { + const echidnaHeight = 4 + bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { + c.Hardforks = map[string]uint32{ + config.HFEchidna.String(): echidnaHeight, + } + }) + e := neotest.NewExecutor(t, bc, acc, acc) + p := e.CommitteeInvoker(nativehashes.PolicyContract) + + // Invoke before Echidna should fail. + p.InvokeFail(t, "invalid attribute type: 34", "getAttributeFee", int64(transaction.NotaryAssistedT)) + + for e.Chain.BlockHeight() < echidnaHeight-1 { + e.AddNewBlock(t) + } + + // Invoke at Echidna should return the default value. + p.Invoke(t, 1000_0000, "getAttributeFee", int64(transaction.NotaryAssistedT)) + + // Invoke after Echidna should return the default value. + p.Invoke(t, 1000_0000, "getAttributeFee", int64(transaction.NotaryAssistedT)) +} + +func TestPolicy_SetNotaryFeePerKey(t *testing.T) { + const echidnaHeight = 4 + bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) { + c.Hardforks = map[string]uint32{ + config.HFEchidna.String(): echidnaHeight, + } + }) + e := neotest.NewExecutor(t, bc, acc, acc) + p := e.CommitteeInvoker(nativehashes.PolicyContract) + + // Invoke before Echidna should fail. + p.InvokeFail(t, "invalid attribute type: 34", "setAttributeFee", int64(transaction.NotaryAssistedT), 500_0000) + + for e.Chain.BlockHeight() < echidnaHeight-1 { + e.AddNewBlock(t) + } + + // Invoke at Echidna should return the default value. + p.Invoke(t, nil, "setAttributeFee", int64(transaction.NotaryAssistedT), 500_0000) + + // Invoke after Echidna should return the default value. + p.Invoke(t, nil, "setAttributeFee", int64(transaction.NotaryAssistedT), 510_0000) +} diff --git a/pkg/core/transaction/notary_assisted.go b/pkg/core/transaction/notary_assisted.go index 278aaea936..8f857bfd04 100644 --- a/pkg/core/transaction/notary_assisted.go +++ b/pkg/core/transaction/notary_assisted.go @@ -1,9 +1,14 @@ package transaction import ( + "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/io" ) +// NotaryAssistedActivation stores the hardfork of NotaryAssisted transaction attribute +// activation. +var NotaryAssistedActivation = config.HFEchidna + // NotaryAssisted represents attribute for notary service transactions. type NotaryAssisted struct { NKeys uint8 `json:"nkeys"` diff --git a/pkg/network/server.go b/pkg/network/server.go index 34da851b52..de529bcbf6 100644 --- a/pkg/network/server.go +++ b/pkg/network/server.go @@ -21,6 +21,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mempoolevent" "github.com/nspcc-dev/neo-go/pkg/core/mpt" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" @@ -66,7 +67,6 @@ type ( GetMaxVerificationGAS() int64 GetMemPool() *mempool.Pool GetNotaryBalance(acc util.Uint160) *big.Int - GetNotaryContractScriptHash() util.Uint160 GetNotaryDepositExpiration(acc util.Uint160) uint32 GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) HasBlock(util.Uint256) bool @@ -1240,7 +1240,7 @@ func (s *Server) verifyNotaryRequest(_ *transaction.Transaction, data any) error if _, err := s.chain.VerifyWitness(payer, r, &r.Witness, s.chain.GetMaxVerificationGAS()); err != nil { return fmt.Errorf("bad P2PNotaryRequest payload witness: %w", err) } - notaryHash := s.chain.GetNotaryContractScriptHash() + notaryHash := nativehashes.Notary if r.FallbackTransaction.Sender() != notaryHash { return fmt.Errorf("P2PNotary contract should be a sender of the fallback transaction, got %s", address.Uint160ToString(r.FallbackTransaction.Sender())) } diff --git a/pkg/network/server_test.go b/pkg/network/server_test.go index 9d2c6f20bf..b4a9d7ef96 100644 --- a/pkg/network/server_test.go +++ b/pkg/network/server_test.go @@ -19,6 +19,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/mpt" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "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/network/capability" @@ -1048,7 +1049,6 @@ func TestMemPool(t *testing.T) { func TestVerifyNotaryRequest(t *testing.T) { bc := fakechain.NewFakeChain() bc.MaxVerificationGAS = 10 - bc.NotaryContractScriptHash = util.Uint160{1, 2, 3} s, err := newServerFromConstructors(ServerConfig{Addresses: []config.AnnounceableAddress{{Address: ":0"}}}, bc, new(fakechain.FakeStateSync), zaptest.NewLogger(t), newFakeTransp, newTestDiscovery) require.NoError(t, err) newNotaryRequest := func() *payload.P2PNotaryRequest { @@ -1059,7 +1059,7 @@ func TestVerifyNotaryRequest(t *testing.T) { }, FallbackTransaction: &transaction.Transaction{ ValidUntilBlock: 321, - Signers: []transaction.Signer{{Account: bc.NotaryContractScriptHash}, {Account: random.Uint160()}}, + Signers: []transaction.Signer{{Account: nativehashes.Notary}, {Account: random.Uint160()}}, }, Witness: transaction.Witness{}, } @@ -1080,7 +1080,7 @@ func TestVerifyNotaryRequest(t *testing.T) { t.Run("bad main sender", func(t *testing.T) { bc.VerifyWitnessF = func() (int64, error) { return 0, nil } r := newNotaryRequest() - r.MainTransaction.Signers[0] = transaction.Signer{Account: bc.NotaryContractScriptHash} + r.MainTransaction.Signers[0] = transaction.Signer{Account: nativehashes.Notary} require.Error(t, s.verifyNotaryRequest(nil, r)) }) diff --git a/pkg/services/notary/core_test.go b/pkg/services/notary/core_test.go index 57ad225268..314aa07be1 100644 --- a/pkg/services/notary/core_test.go +++ b/pkg/services/notary/core_test.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/mempool" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "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/transaction" @@ -171,7 +172,7 @@ func TestNotary(t *testing.T) { fallback.ValidUntilBlock = bc.BlockHeight() + 2*nvbDiffFallback fallback.Signers = []transaction.Signer{ { - Account: bc.GetNotaryContractScriptHash(), + Account: nativehashes.Notary, Scopes: transaction.None, }, { @@ -240,7 +241,7 @@ func TestNotary(t *testing.T) { verificationScripts = append(verificationScripts, script) } signers[len(signers)-1] = transaction.Signer{ - Account: bc.GetNotaryContractScriptHash(), + Account: nativehashes.Notary, Scopes: transaction.None, } mainTx.Signers = signers @@ -752,9 +753,9 @@ func TestNotary(t *testing.T) { requester1, _ := wallet.NewAccount() requester2, _ := wallet.NewAccount() amount := int64(100_0000_0000) - gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []any{requester1.ScriptHash(), int64(bc.BlockHeight() + 50)}) + gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), nativehashes.Notary, amount, []any{requester1.ScriptHash(), int64(bc.BlockHeight() + 50)}) e.CheckGASBalance(t, notaryHash, big.NewInt(amount)) - gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []any{requester2.ScriptHash(), int64(bc.BlockHeight() + 50)}) + gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), nativehashes.Notary, amount, []any{requester2.ScriptHash(), int64(bc.BlockHeight() + 50)}) e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount)) // create request for 2 standard signatures => main tx should be completed after the second request is added to the pool diff --git a/pkg/services/notary/notary.go b/pkg/services/notary/notary.go index 166890675e..44258d7bb7 100644 --- a/pkg/services/notary/notary.go +++ b/pkg/services/notary/notary.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mempoolevent" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -31,7 +32,6 @@ type ( Ledger interface { BlockHeight() uint32 GetMaxVerificationGAS() int64 - GetNotaryContractScriptHash() util.Uint160 SubscribeForBlocks(ch chan *block.Block) UnsubscribeFromBlocks(ch chan *block.Block) VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) (int64, error) @@ -408,7 +408,7 @@ func (n *Notary) finalize(acc *wallet.Account, tx *transaction.Transaction, h ut VerificationScript: []byte{}, } for i, signer := range tx.Signers { - if signer.Account == n.Config.Chain.GetNotaryContractScriptHash() { + if signer.Account == nativehashes.Notary { tx.Scripts[i] = notaryWitness break } @@ -510,7 +510,7 @@ func (n *Notary) verifyIncompleteWitnesses(tx *transaction.Transaction, nKeysExp if len(tx.Signers) < 2 { return nil, errors.New("transaction should have at least 2 signers") } - if !tx.HasSigner(n.Config.Chain.GetNotaryContractScriptHash()) { + if !tx.HasSigner(nativehashes.Notary) { return nil, fmt.Errorf("P2PNotary contract should be a signer of the transaction") } result := make([]witnessInfo, len(tx.Signers)) diff --git a/pkg/services/notary/notary_test.go b/pkg/services/notary/notary_test.go index 9df86c72fa..afc630f8a7 100644 --- a/pkg/services/notary/notary_test.go +++ b/pkg/services/notary/notary_test.go @@ -7,11 +7,11 @@ import ( "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/mempool" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" @@ -48,8 +48,7 @@ func TestWallet(t *testing.T) { func TestVerifyIncompleteRequest(t *testing.T) { bc := fakechain.NewFakeChain() - notaryContractHash := util.Uint160{1, 2, 3} - bc.NotaryContractScriptHash = notaryContractHash + notaryContractHash := nativehashes.Notary _, ntr, _ := getTestNotary(t, bc, "./testdata/notary1.json", "one") sig := append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...) // we're not interested in signature correctness acc1, _ := keys.NewPrivateKey() diff --git a/pkg/services/rpcsrv/client_test.go b/pkg/services/rpcsrv/client_test.go index 98e874567f..f4e2deebf7 100644 --- a/pkg/services/rpcsrv/client_test.go +++ b/pkg/services/rpcsrv/client_test.go @@ -465,6 +465,10 @@ func TestClientNEOContract(t *testing.T) { func TestClientNotary(t *testing.T) { chain, _, httpSrv := initServerWithInMemoryChain(t) + // Echidna should be enabled since this test uses Notary contract. + _, ok := chain.GetConfig().Hardforks[config.HFEchidna.String()] + require.True(t, ok) + c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) require.NoError(t, err) t.Cleanup(c.Close) @@ -2446,7 +2450,11 @@ func TestClient_GetVersion_Hardforks(t *testing.T) { v, err := c.GetVersion() require.NoError(t, err) expected := map[config.Hardfork]uint32{ - config.HFAspidochelone: 25, + config.HFAspidochelone: 0, + config.HFBasilisk: 0, + config.HFCockatrice: 0, + config.HFDomovoi: 0, + config.HFEchidna: 0, } require.InDeltaMapValues(t, expected, v.Protocol.Hardforks, 0) } diff --git a/pkg/services/rpcsrv/server.go b/pkg/services/rpcsrv/server.go index 755140a365..82c38175f3 100644 --- a/pkg/services/rpcsrv/server.go +++ b/pkg/services/rpcsrv/server.go @@ -88,7 +88,6 @@ type ( GetNativeContractScriptHash(string) (util.Uint160, error) GetNatives() []state.Contract GetNextBlockValidators() ([]*keys.PublicKey, error) - GetNotaryContractScriptHash() util.Uint160 GetStateModule() core.StateRoot GetStorageItem(id int32, key []byte) state.StorageItem GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, nextBlockHeight uint32) (*interop.Context, error) diff --git a/pkg/services/rpcsrv/server_helper_test.go b/pkg/services/rpcsrv/server_helper_test.go index 3595bacd60..c488de8a95 100644 --- a/pkg/services/rpcsrv/server_helper_test.go +++ b/pkg/services/rpcsrv/server_helper_test.go @@ -54,6 +54,7 @@ func getUnitTestChain(t testing.TB, enableOracle bool, enableNotary bool, disabl Password: "one", } } + cfg.ProtocolConfiguration.Hardforks = nil }) } func getUnitTestChainWithCustomConfig(t testing.TB, enableOracle bool, enableNotary bool, customCfg func(configuration *config.Config)) (*core.Blockchain, OracleHandler, config.Config, *zap.Logger) { diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go index e6223d602e..e29be1e2ef 100644 --- a/pkg/services/rpcsrv/server_test.go +++ b/pkg/services/rpcsrv/server_test.go @@ -31,6 +31,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/storage/dboper" @@ -74,22 +75,22 @@ type rpcTestCase struct { } const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4" -const testContractHash = "565cff9508ebc75aadd7fe59f38dac610ab6093c" -const deploymentTxHash = "a14390941cc3a1d87393eff720a722e9cd350bd6ed233c5fe2001326c80eb68e" +const testContractHash = "449fe8fbd4523072f5e3a4dfa17a494c119d4c08" +const deploymentTxHash = "af170742f0f8a2bc064bdbdb2faa2b517e3df833d4d047da8a946c0b8d581b06" const ( verifyContractHash = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c" verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A=" - verifyWithArgsContractHash = "4dc916254efd2947c93b11207e8ffc0bb56161c5" - nnsContractHash = "892429fcd47c30f8451781acc627e8b20e0d64f3" + verifyWithArgsContractHash = "6261b3bf753bdc3d24c1327a23fd891e1c8a7ccd" + nnsContractHash = "ebe47d5143bb8726b87b02efb5cd98e21174fd38" nnsToken1ID = "6e656f2e636f6d" - nfsoContractHash = "730ebe719ab8e3b69d11dafc95cdb9bf409db179" + nfsoContractHash = "2f5c1826bb4da1c764a8871427e4044cf3e82dbd" nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486" storageContractHash = "ebc0c16a76c808cd4dde6bcc063f09e45e331ec7" faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60" faultedTxBlock uint32 = 23 invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA" - block20StateRootLE = "858c873539d6d24a70f2be13f9dafc61aef2b63c2aa16bb440676de6e44e3cf1" + block20StateRootLE = "b49a35fd3a749fc2f7f4e5fe1f288ef2b6188416f65fe5b691892e8209092082" ) var ( @@ -1381,7 +1382,7 @@ var rpcTestCases = map[string][]rpcTestCase{ script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) return &result.Invoke{ State: "HALT", - GasConsumed: 31922970, + GasConsumed: 31922730, Script: script, Stack: []stackitem.Item{stackitem.Make(true)}, Notifications: []state.NotificationEvent{{ @@ -1411,7 +1412,7 @@ var rpcTestCases = map[string][]rpcTestCase{ chg := []dboper.Operation{{ State: "Changed", Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb}, - Value: []byte{0x54, 0xb2, 0xd2, 0xa3, 0x51, 0x79, 0x12}, + Value: []byte{0x06, 0x44, 0xda, 0xa3, 0x51, 0x79, 0x12}, }, { State: "Added", Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb}, @@ -1423,7 +1424,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { State: "Changed", Key: []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2}, - Value: []byte{0x41, 0x01, 0x21, 0x05, 0x0c, 0x76, 0x4f, 0xdf, 0x08}, + Value: []byte{0x41, 0x01, 0x21, 0x05, 0xf6, 0x64, 0x58, 0xdf, 0x08}, }} // Can be returned in any order. assert.ElementsMatch(t, chg, res.Diagnostics.Changes) @@ -1439,7 +1440,7 @@ var rpcTestCases = map[string][]rpcTestCase{ cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib) return &result.Invoke{ State: "HALT", - GasConsumed: 13970250, + GasConsumed: 13969530, Script: script, Stack: []stackitem.Item{stackitem.Make("1.2.3.4")}, Notifications: []state.NotificationEvent{}, @@ -1532,7 +1533,7 @@ var rpcTestCases = map[string][]rpcTestCase{ script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) return &result.Invoke{ State: "HALT", - GasConsumed: 31922970, + GasConsumed: 31922730, Script: script, Stack: []stackitem.Item{stackitem.Make(true)}, Notifications: []state.NotificationEvent{{ @@ -1558,7 +1559,7 @@ var rpcTestCases = map[string][]rpcTestCase{ cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib) return &result.Invoke{ State: "HALT", - GasConsumed: 13970250, + GasConsumed: 13969530, Script: script, Stack: []stackitem.Item{stackitem.Make("1.2.3.4")}, Notifications: []state.NotificationEvent{}, @@ -2495,7 +2496,7 @@ func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, n {Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}}, {Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}}, }, - Signers: []transaction.Signer{{Account: chain.GetNotaryContractScriptHash()}, {Account: sender.GetScriptHash()}}, + Signers: []transaction.Signer{{Account: nativehashes.Notary}, {Account: sender.GetScriptHash()}}, Scripts: []transaction.Witness{ {InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}}, }, @@ -3260,7 +3261,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] t.Run("contract-based verification with parameters", func(t *testing.T) { verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash) require.NoError(t, err) - checkContract(t, verAcc, []byte{}, 244130) // No C# match, but we believe it's OK and it differs from the one above. + checkContract(t, verAcc, []byte{}, 244010) // No C# match, but we believe it's OK and it differs from the one above. }) t.Run("contract-based verification with invocation script", func(t *testing.T) { verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash) @@ -3270,7 +3271,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] emit.Int(invocWriter.BinWriter, 5) emit.String(invocWriter.BinWriter, "") invocScript := invocWriter.Bytes() - checkContract(t, verAcc, invocScript, 146960) // No C# match, but we believe it's OK and it has a specific invocation script overriding anything server-side. + checkContract(t, verAcc, invocScript, 146840) // No C# match, but we believe it's OK and it has a specific invocation script overriding anything server-side. }) t.Run("execution limit, ok", func(t *testing.T) { // 1_4000_0000 GAS with the default 1.5 allowed by Policy @@ -3575,7 +3576,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc any) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "37106285100", + Amount: "37106870550", LastUpdated: 23, Decimals: 8, Name: "GasToken", @@ -3924,7 +3925,7 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int { Timestamp: blockDepositGAS.Timestamp, Asset: e.chain.UtilityTokenHash(), - Address: address.Uint160ToString(e.chain.GetNotaryContractScriptHash()), + Address: address.Uint160ToString(nativehashes.Notary), Amount: "1000000000", Index: 8, NotifyIndex: 0, diff --git a/pkg/services/rpcsrv/testdata/testblocks.acc b/pkg/services/rpcsrv/testdata/testblocks.acc index 173d18a8ce..9f97e713f7 100644 Binary files a/pkg/services/rpcsrv/testdata/testblocks.acc and b/pkg/services/rpcsrv/testdata/testblocks.acc differ