From 6dee57893e2748b5202910603022468d694025d1 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 4 Jun 2024 13:25:21 +0300 Subject: [PATCH 01/11] core: introduce Echidna hardfork Signed-off-by: Anna Shaleva --- pkg/config/hardfork.go | 3 +++ pkg/config/hardfork_string.go | 8 ++++++-- pkg/core/blockchain_core_test.go | 4 +++- pkg/core/blockchain_neotest_test.go | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) 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_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..892a585282 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) { From 03c29e068e67a5052e6ead7c412c014c2c437c8e Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 4 Jun 2024 18:38:28 +0300 Subject: [PATCH 02/11] native: fix error message on native cache initialization Signed-off-by: Anna Shaleva --- pkg/core/native/management.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 2633f6f8a0..7dd710b047 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -668,7 +668,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) } } From 99fc248520e81dfbe7a0d6dd890812ba3645724a Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 4 Jun 2024 18:41:56 +0300 Subject: [PATCH 03/11] interop: improve error message on native cache initialization error Signed-off-by: Anna Shaleva --- pkg/core/interop/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)) From cc960d5a95149698ca4557f151b6009787763bcb Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 4 Jun 2024 18:43:44 +0300 Subject: [PATCH 04/11] native: fix native deploy process It doesn't work for contracts enabled starting from non-nil hardfork: ``` --- FAIL: TestStateroot_GetLatestStateHeight (0.00s) logger.go:146: 2024-06-04T17:08:35.263+0300 INFO initial gas supply is not set or wrong, setting default value {"InitialGASSupply": "52000000"} logger.go:146: 2024-06-04T17:08:35.263+0300 INFO mempool size is not set or wrong, setting default value {"MemPoolSize": 50000} logger.go:146: 2024-06-04T17:08:35.263+0300 INFO P2PNotaryRequestPayloadPool size is not set or wrong, setting default value {"P2PNotaryRequestPayloadPoolSize": 1000} logger.go:146: 2024-06-04T17:08:35.263+0300 INFO MaxBlockSize is not set or wrong, setting default value {"MaxBlockSize": 262144} logger.go:146: 2024-06-04T17:08:35.263+0300 INFO MaxBlockSystemFee is not set or wrong, setting default value {"MaxBlockSystemFee": 900000000000} logger.go:146: 2024-06-04T17:08:35.263+0300 INFO MaxTransactionsPerBlock is not set or wrong, using default value {"MaxTransactionsPerBlock": 512} logger.go:146: 2024-06-04T17:08:35.263+0300 INFO MaxValidUntilBlockIncrement is not set or wrong, using default value {"MaxValidUntilBlockIncrement": 86400} logger.go:146: 2024-06-04T17:08:35.263+0300 INFO Hardforks are not set, using default value logger.go:146: 2024-06-04T17:08:35.266+0300 INFO no storage version found! creating genesis block chain.go:227: Error Trace: /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/neotest/chain/chain.go:227 /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/neotest/chain/chain.go:217 /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/services/stateroot/service_test.go:319 Error: Received unexpected error: onPersist failed: VM has failed: at instruction 0 (SYSCALL): native contract descriptor cache is not initialized: contract c1e14f19c3e60d0b9244d06dd7ba9b113135ec3b, hardfork Default Test: TestStateroot_GetLatestStateHeight FAIL coverage: 28.6% of statements ``` It happens because ActiveIn hardfork wasn't taken into account during `latestHF` computation. This commit also removes the reusage of `activeIn` variable in deploy procedure, it's misleading and not necessary startign from #3444. Signed-off-by: Anna Shaleva --- pkg/core/native/management.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 7dd710b047..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 } From b2ac71e578b2aca028f5a47490b674b27b224fc9 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 4 Jun 2024 20:48:12 +0300 Subject: [PATCH 05/11] core: fix bug in (bc *Blockchain).isHardforkEnabled This code was never invoked since we had no native contract enabled starting from some hardfork, Notary is the first one. And luckily, we have plenty of tests that fail due to this bug. Signed-off-by: Anna Shaleva --- pkg/core/blockchain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 93c98577e8..9c6c876731 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1070,7 +1070,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 } } From 4eadc2c1b0ebd129672d1281ca920b9bec737288 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 5 Jun 2024 17:30:10 +0300 Subject: [PATCH 06/11] core: move Notary contract under Domovoi hardfork Close #3464. Adjust tests, enable all hardforks for RPC server tests starting from genesis. Signed-off-by: Anna Shaleva --- internal/basicchain/basic.go | 7 + pkg/core/blockchain_neotest_test.go | 2 +- .../native/native_test/management_test.go | 135 ++++++++++++++++-- pkg/core/native/notary.go | 7 +- pkg/services/rpcsrv/client_test.go | 10 +- pkg/services/rpcsrv/server_helper_test.go | 1 + pkg/services/rpcsrv/server_test.go | 30 ++-- pkg/services/rpcsrv/testdata/testblocks.acc | Bin 35787 -> 35697 bytes 8 files changed, 161 insertions(+), 31 deletions(-) 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/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index 892a585282..e6dabb5b41 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -2572,7 +2572,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/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go index 9870e81f01..9adc7e5c5b 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,11 @@ 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}`, + } ) func init() { @@ -63,6 +67,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 +100,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 +126,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 +162,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 +199,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 +232,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 +248,35 @@ 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: "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 +287,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 +304,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 +383,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 +402,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 +413,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 +426,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/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_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..1081019982 100644 --- a/pkg/services/rpcsrv/server_test.go +++ b/pkg/services/rpcsrv/server_test.go @@ -74,22 +74,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 +1381,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 +1411,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 +1423,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 +1439,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 +1532,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 +1558,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{}, @@ -3260,7 +3260,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 +3270,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 +3575,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc any) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "37106285100", + Amount: "37106870550", LastUpdated: 23, Decimals: 8, Name: "GasToken", diff --git a/pkg/services/rpcsrv/testdata/testblocks.acc b/pkg/services/rpcsrv/testdata/testblocks.acc index 173d18a8ce8bfe5999e3e205b0cb30e1e021a1eb..9f97e713f7859859236b26fafc285a6f037adaca 100644 GIT binary patch delta 10410 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 JczZqXe*kwkPgei{ From de83594afbb20af0bd1ff9530a0a616b9eedb86e Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 5 Jun 2024 18:21:27 +0300 Subject: [PATCH 07/11] *: get rid of (*Blockchain).GetNotaryContractScriptHash Use `nativehashes.Notary instead of (*Blockchain).GetNotaryContractScriptHash. Signed-off-by: Anna Shaleva --- cli/nep_test/nep17_test.go | 3 ++- internal/fakechain/fakechain.go | 33 +++++++++++------------------- pkg/core/blockchain.go | 11 ++-------- pkg/network/server.go | 4 ++-- pkg/network/server_test.go | 6 +++--- pkg/services/notary/core_test.go | 9 ++++---- pkg/services/notary/notary.go | 6 +++--- pkg/services/notary/notary_test.go | 5 ++--- pkg/services/rpcsrv/server.go | 1 - pkg/services/rpcsrv/server_test.go | 5 +++-- 10 files changed, 34 insertions(+), 49 deletions(-) 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/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/core/blockchain.go b/pkg/core/blockchain.go index 9c6c876731..da40bc2d54 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" @@ -2114,14 +2115,6 @@ 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 - } - return util.Uint160{} -} - // GetNotaryDepositExpiration returns Notary deposit expiration height for the specified account. func (bc *Blockchain) GetNotaryDepositExpiration(acc util.Uint160) uint32 { return bc.contracts.Notary.ExpirationOf(bc.dao, acc) @@ -2698,7 +2691,7 @@ func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transact if !bc.config.P2PSigExtensions { return fmt.Errorf("%w: NotaryAssisted attribute was found, but P2PSigExtensions are disabled", ErrInvalidAttribute) } - 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: 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/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_test.go b/pkg/services/rpcsrv/server_test.go index 1081019982..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" @@ -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{}}, }, @@ -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, From 571a8f5a9a0cd718807b270c844d0b5bdaf1c4ea Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 5 Jun 2024 18:26:14 +0300 Subject: [PATCH 08/11] *: move Notary contract out of P2PSigExtensions A part of #3464. Signed-off-by: Anna Shaleva --- docs/node-configuration.md | 2 +- pkg/core/blockchain.go | 3 --- pkg/core/native/contract.go | 16 +++++++--------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/docs/node-configuration.md b/docs/node-configuration.md index 2e635ecef6..9120be64d2 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:
• Transaction attribute `NotaryAssisted`
• 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/pkg/core/blockchain.go b/pkg/core/blockchain.go index da40bc2d54..7d5429f5df 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -3081,9 +3081,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/native/contract.go b/pkg/core/native/contract.go index a65631d3b6..b0bd92b958 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -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 } From b735f2e9243e024377b8ff6de2cbf60f914f20b5 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 5 Jun 2024 18:40:22 +0300 Subject: [PATCH 09/11] core: prevent access to inactive Notary contract Signed-off-by: Anna Shaleva --- pkg/core/blockchain.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 7d5429f5df..c67ee5e430 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -2105,18 +2105,27 @@ 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) } // 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) } From 1c3644f9a5ea273b571084f4c7c1552742506fcf Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 7 Jun 2024 19:20:08 +0300 Subject: [PATCH 10/11] core: move NotaryAssisted attribute out of P2PSigExtensions Move it under Domovoi hardfork. Signed-off-by: Anna Shaleva --- docs/node-configuration.md | 2 +- pkg/core/blockchain.go | 4 +- pkg/core/blockchain_neotest_test.go | 9 +- pkg/core/native/contract.go | 4 +- pkg/core/native/management_test.go | 4 +- pkg/core/native/native_gas.go | 22 ++--- .../native/native_test/management_test.go | 8 ++ pkg/core/native/policy.go | 85 ++++++++++++------- pkg/core/native/policy_test.go | 51 +++++++++++ pkg/core/transaction/notary_assisted.go | 5 ++ 10 files changed, 141 insertions(+), 53 deletions(-) diff --git a/docs/node-configuration.md b/docs/node-configuration.md index 9120be64d2..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
• 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/pkg/core/blockchain.go b/pkg/core/blockchain.go index c67ee5e430..8190ff6555 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -2697,8 +2697,8 @@ 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(nativehashes.Notary) { return fmt.Errorf("%w: NotaryAssisted attribute was found, but transaction is not signed by the Notary native contract", ErrInvalidAttribute) diff --git a/pkg/core/blockchain_neotest_test.go b/pkg/core/blockchain_neotest_test.go index e6dabb5b41..482ec7b95e 100644 --- a/pkg/core/blockchain_neotest_test.go +++ b/pkg/core/blockchain_neotest_test.go @@ -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) diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index b0bd92b958..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 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 9adc7e5c5b..498eb19271 100644 --- a/pkg/core/native/native_test/management_test.go +++ b/pkg/core/native/native_test/management_test.go @@ -58,6 +58,7 @@ var ( // 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}`, } ) @@ -255,6 +256,13 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) { 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", 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"` From 887116560d167654c99411ecebb88d7501d6ebf3 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 7 Jun 2024 19:26:15 +0300 Subject: [PATCH 11/11] core: prevent access to uninitialized NotaryAssisted fee Signed-off-by: Anna Shaleva --- pkg/core/blockchain.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 8190ff6555..8b06235b42 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -2117,6 +2117,9 @@ func (bc *Blockchain) GetNotaryBalance(acc util.Uint160) *big.Int { // 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 { + if !bc.isHardforkEnabled(&transaction.NotaryAssistedActivation, bc.BlockHeight()) { + return 0 + } return bc.contracts.Policy.GetAttributeFeeInternal(bc.dao, transaction.NotaryAssistedT) }