Compare commits

...

12 commits

Author SHA1 Message Date
Anna Shaleva
551f9ebb31 core: prevent access to uninitialized NotaryAssisted fee
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-07 19:46:37 +03:00
Anna Shaleva
738ef51428 core: move NotaryAssisted attribute out of P2PSigExtensions
Move it under Domovoi hardfork.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-07 19:46:07 +03:00
Anna Shaleva
bc8ca6dc06 config: reschedule NeoFS mainnet hardforks
Ref. #3465.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-07 15:28:39 +03:00
Anna Shaleva
56a4160f21 core: prevent access to inactive Notary contract
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-05 18:42:19 +03:00
Anna Shaleva
9856bb7d34 *: move Notary contract out of P2PSigExtensions
A part of #3464.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-05 18:42:10 +03:00
Anna Shaleva
632334180f *: get rid of (*Blockchain).GetNotaryContractScriptHash
Use `nativehashes.Notary instead of
(*Blockchain).GetNotaryContractScriptHash.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-05 18:38:56 +03:00
Anna Shaleva
c4a324db8f 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 <shaleva.ann@nspcc.ru>
2024-06-05 17:30:10 +03:00
Anna Shaleva
49e51a46ff 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 <shaleva.ann@nspcc.ru>
2024-06-04 20:48:12 +03:00
Anna Shaleva
1720222a32 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 <shaleva.ann@nspcc.ru>
2024-06-04 18:45:10 +03:00
Anna Shaleva
261816d7a5 interop: improve error message on native cache initialization error
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-04 18:41:56 +03:00
Anna Shaleva
71d504765f native: fix error message on native cache initialization
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-04 18:38:28 +03:00
Anna Shaleva
e0825e7665 core: introduce D hardfork
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-06-04 13:54:15 +03:00
30 changed files with 374 additions and 155 deletions

View file

@ -9,6 +9,7 @@ import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/testcli" "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/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
@ -237,7 +238,7 @@ func TestNEP17Transfer(t *testing.T) {
e.CheckAwaitableTxPersisted(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)), "]") "[", testcli.ValidatorAddr, strconv.Itoa(int(validTil)), "]")
t.Run("with data", func(t *testing.T) { t.Run("with data", func(t *testing.T) {

View file

@ -25,8 +25,9 @@ ProtocolConfiguration:
P2PSigExtensions: true P2PSigExtensions: true
Hardforks: Hardforks:
Aspidochelone: 3000000 Aspidochelone: 3000000
Basilisk: 4500000 Basilisk: 3500000
Cockatrice: 5800000 Cockatrice: 3500000
Domovoi: 3500000
ApplicationConfiguration: ApplicationConfiguration:
SkipBlockVerification: false SkipBlockVerification: false

View file

@ -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. | | 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. | | 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.<br>This option is valid only if `P2PSigExtensions` are enabled. | Not supported by the C# node, thus may affect heterogeneous networks functionality. | | P2PNotaryRequestPayloadPoolSize | `int` | `1000` | Size of the node's P2P Notary request payloads memory pool where P2P Notary requests are stored before main or fallback transaction is completed and added to the chain.<br>This option is valid only if `P2PSigExtensions` are enabled. | Not supported by the C# node, thus may affect heterogeneous networks functionality. |
| P2PSigExtensions | `bool` | `false` | Enables following additional Notary service related logic:<br> Transaction attribute `NotaryAssisted`<br> Network payload of the `P2PNotaryRequest` type<br> Native `Notary` contract<br> Notary node module | Not supported by the C# node, thus may affect heterogeneous networks functionality. | | P2PSigExtensions | `bool` | `false` | Enables following additional Notary service related logic:<br>• Network payload of the `P2PNotaryRequest` type<br>• Notary node module | Not supported by the C# node, thus may affect heterogeneous networks functionality. |
| P2PStateExchangeExtensions | `bool` | `false` | Enables the following P2P MPT state data exchange logic: <br>`StateSyncInterval` protocol setting <br>• P2P commands `GetMPTDataCMD` and `MPTDataCMD` | Not supported by the C# node, thus may affect heterogeneous networks functionality. Can be supported either on MPT-complete node (`KeepOnlyLatestState`=`false`) or on light GC-enabled node (`RemoveUntraceableBlocks=true`) in which case `KeepOnlyLatestState` setting doesn't change the behavior, an appropriate set of MPTs is always stored (see `RemoveUntraceableBlocks`). | | P2PStateExchangeExtensions | `bool` | `false` | Enables the following P2P MPT state data exchange logic: <br>`StateSyncInterval` protocol setting <br>• P2P commands `GetMPTDataCMD` and `MPTDataCMD` | Not supported by the C# node, thus may affect heterogeneous networks functionality. Can be supported either on MPT-complete node (`KeepOnlyLatestState`=`false`) or on light GC-enabled node (`RemoveUntraceableBlocks=true`) in which case `KeepOnlyLatestState` setting doesn't change the behavior, an appropriate set of MPTs is always stored (see `RemoveUntraceableBlocks`). |
| ReservedAttributes | `bool` | `false` | Allows to have reserved attributes range for experimental or private purposes. | | 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. | | SeedList | `[]string` | [] | List of initial nodes addresses used to establish connectivity. |

View file

@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"testing" "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"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
@ -81,6 +82,11 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) {
t.Fatal("P2PSigExtensions should be enabled to init basic chain") t.Fatal("P2PSigExtensions should be enabled to init basic chain")
} }
const notaryDepositHeight uint32 = 8
domovoiH, ok := e.Chain.GetConfig().Hardforks[config.HFDomovoi.String()]
require.Truef(t, ok, "%s hardfork should be enabled since basic chain uses Notary contract", config.HFDomovoi.String())
require.LessOrEqualf(t, domovoiH, notaryDepositHeight-1, "%s hardfork should be enabled starting from height %d, got: %d", config.HFDomovoi.String(), notaryDepositHeight-1, domovoiH)
var ( var (
// examplesPrefix is a prefix of the example smart-contracts. // examplesPrefix is a prefix of the example smart-contracts.
examplesPrefix = filepath.Join(rootpath, "examples") 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. // 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)}) transferTxH = gasPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, notaryHash, 10_0000_0000, []any{priv0ScriptHash, int64(e.Chain.BlockHeight() + 1000)})
t.Logf("notaryDepositTxPriv0: %v", transferTxH.StringLE()) 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. // Block #9: designate new Notary node.
ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json")) ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json"))

View file

@ -24,19 +24,18 @@ import (
type FakeChain struct { type FakeChain struct {
config.Blockchain config.Blockchain
*mempool.Pool *mempool.Pool
blocksCh []chan *block.Block blocksCh []chan *block.Block
Blockheight atomic.Uint32 Blockheight atomic.Uint32
PoolTxF func(*transaction.Transaction) error PoolTxF func(*transaction.Transaction) error
poolTxWithData func(*transaction.Transaction, any, *mempool.Pool) error poolTxWithData func(*transaction.Transaction, any, *mempool.Pool) error
blocks map[util.Uint256]*block.Block blocks map[util.Uint256]*block.Block
hdrHashes map[uint32]util.Uint256 hdrHashes map[uint32]util.Uint256
txs map[util.Uint256]*transaction.Transaction txs map[util.Uint256]*transaction.Transaction
VerifyWitnessF func() (int64, error) VerifyWitnessF func() (int64, error)
MaxVerificationGAS int64 MaxVerificationGAS int64
NotaryContractScriptHash util.Uint160 NotaryDepositExpiration uint32
NotaryDepositExpiration uint32 PostBlock []func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block)
PostBlock []func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block) UtilityTokenBalance *big.Int
UtilityTokenBalance *big.Int
} }
// FakeStateSync implements the StateSync interface. // FakeStateSync implements the StateSync interface.
@ -111,14 +110,6 @@ func (chain *FakeChain) GetNotaryDepositExpiration(acc util.Uint160) uint32 {
panic("TODO") 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. // GetNotaryBalance implements the Blockchainer interface.
func (chain *FakeChain) GetNotaryBalance(acc util.Uint160) *big.Int { func (chain *FakeChain) GetNotaryBalance(acc util.Uint160) *big.Int {
panic("TODO") panic("TODO")

View file

@ -27,6 +27,9 @@ const (
// https://github.com/neo-project/neo/pull/2925) and #3362 (ported from // https://github.com/neo-project/neo/pull/2925) and #3362 (ported from
// https://github.com/neo-project/neo/pull/3154). // https://github.com/neo-project/neo/pull/3154).
HFCockatrice // Cockatrice HFCockatrice // Cockatrice
// HFDomovoi represents hard-fork introduced in #3476 (ported from
// https://github.com/neo-project/neo/pull/3290).
HFDomovoi // Domovoi
// hfLast denotes the end of hardforks enum. Consider adding new hardforks // hfLast denotes the end of hardforks enum. Consider adding new hardforks
// before hfLast. // before hfLast.
hfLast hfLast

View file

@ -12,13 +12,15 @@ func _() {
_ = x[HFAspidochelone-1] _ = x[HFAspidochelone-1]
_ = x[HFBasilisk-2] _ = x[HFBasilisk-2]
_ = x[HFCockatrice-4] _ = x[HFCockatrice-4]
_ = x[hfLast-8] _ = x[HFDomovoi-8]
_ = x[hfLast-16]
} }
const ( const (
_Hardfork_name_0 = "DefaultAspidocheloneBasilisk" _Hardfork_name_0 = "DefaultAspidocheloneBasilisk"
_Hardfork_name_1 = "Cockatrice" _Hardfork_name_1 = "Cockatrice"
_Hardfork_name_2 = "hfLast" _Hardfork_name_2 = "Domovoi"
_Hardfork_name_3 = "hfLast"
) )
var ( var (
@ -33,6 +35,8 @@ func (i Hardfork) String() string {
return _Hardfork_name_1 return _Hardfork_name_1
case i == 8: case i == 8:
return _Hardfork_name_2 return _Hardfork_name_2
case i == 16:
return _Hardfork_name_3
default: default:
return "Hardfork(" + strconv.FormatInt(int64(i), 10) + ")" return "Hardfork(" + strconv.FormatInt(int64(i), 10) + ")"
} }

View file

@ -22,6 +22,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "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/mpt"
"github.com/nspcc-dev/neo-go/pkg/core/native" "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/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/stateroot" "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 hfs := bc.config.Hardforks
if hf != nil { if hf != nil {
start, ok := hfs[hf.String()] start, ok := hfs[hf.String()]
if !ok || start < blockHeight { if !ok || start > blockHeight {
return false 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. // 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 { 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) return bc.contracts.Notary.BalanceOf(bc.dao, acc)
} }
// GetNotaryServiceFeePerKey returns a NotaryAssisted transaction attribute fee // GetNotaryServiceFeePerKey returns a NotaryAssisted transaction attribute fee
// per key which is a reward per notary request key for designated notary nodes. // 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 { func (bc *Blockchain) GetNotaryServiceFeePerKey() int64 {
if !bc.isHardforkEnabled(&transaction.NotaryAssistedActivation, bc.BlockHeight()) {
return 0
}
return bc.contracts.Policy.GetAttributeFeeInternal(bc.dao, transaction.NotaryAssistedT) 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. // 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 { 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) 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()) return fmt.Errorf("%w: conflicting transaction %s is already on chain", ErrInvalidAttribute, conflicts.Hash.StringLE())
} }
case transaction.NotaryAssistedT: case transaction.NotaryAssistedT:
if !bc.config.P2PSigExtensions { if !bc.isHardforkEnabled(&transaction.NotaryAssistedActivation, bc.BlockHeight()) {
return fmt.Errorf("%w: NotaryAssisted attribute was found, but P2PSigExtensions are disabled", ErrInvalidAttribute) 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) return fmt.Errorf("%w: NotaryAssisted attribute was found, but transaction is not signed by the Notary native contract", ErrInvalidAttribute)
} }
default: default:
@ -3093,9 +3098,6 @@ func (bc *Blockchain) GetMaxVerificationGAS() int64 {
// GetMaxNotValidBeforeDelta returns maximum NotValidBeforeDelta Notary limit. // GetMaxNotValidBeforeDelta returns maximum NotValidBeforeDelta Notary limit.
func (bc *Blockchain) GetMaxNotValidBeforeDelta() (uint32, error) { 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()) { if !bc.isHardforkEnabled(bc.contracts.Notary.ActiveIn(), bc.BlockHeight()) {
return 0, fmt.Errorf("native Notary is active starting from %s", bc.contracts.Notary.ActiveIn().String()) return 0, fmt.Errorf("native Notary is active starting from %s", bc.contracts.Notary.ActiveIn().String())
} }

View file

@ -370,6 +370,7 @@ func TestNewBlockchain_InitHardforks(t *testing.T) {
config.HFAspidochelone.String(): 0, config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0, config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): 0, config.HFCockatrice.String(): 0,
config.HFDomovoi.String(): 0,
}, bc.GetConfig().Hardforks) }, bc.GetConfig().Hardforks)
}) })
t.Run("empty set", func(t *testing.T) { t.Run("empty set", func(t *testing.T) {
@ -400,13 +401,14 @@ func TestNewBlockchain_InitHardforks(t *testing.T) {
}) })
t.Run("all present", func(t *testing.T) { t.Run("all present", func(t *testing.T) {
bc := newTestChainWithCustomCfg(t, func(c *config.Config) { 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} c.ProtocolConfiguration.Hardforks = map[string]uint32{config.HFAspidochelone.String(): 5, config.HFBasilisk.String(): 10, config.HFCockatrice.String(): 15, config.HFDomovoi.String(): 20}
require.NoError(t, c.ProtocolConfiguration.Validate()) require.NoError(t, c.ProtocolConfiguration.Validate())
}) })
require.Equal(t, map[string]uint32{ require.Equal(t, map[string]uint32{
config.HFAspidochelone.String(): 5, config.HFAspidochelone.String(): 5,
config.HFBasilisk.String(): 10, config.HFBasilisk.String(): 10,
config.HFCockatrice.String(): 15, config.HFCockatrice.String(): 15,
config.HFDomovoi.String(): 20,
}, bc.GetConfig().Hardforks) }, bc.GetConfig().Hardforks)
}) })
} }

View file

@ -271,7 +271,7 @@ func TestBlockchain_StartFromExistingDB(t *testing.T) {
_, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache) _, _, _, err = chain.NewMultiWithCustomConfigAndStoreNoCheck(t, customConfig, cache)
require.Error(t, err) require.Error(t, err)
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native %s: version mismatch for the latest hardfork Cockatrice (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 Domovoi (stored contract state differs from autogenerated one)", nativenames.CryptoLib)), err)
}) })
t.Run("good", func(t *testing.T) { t.Run("good", func(t *testing.T) {
@ -2057,7 +2057,12 @@ func TestBlockchain_VerifyTx(t *testing.T) {
} }
t.Run("Disabled", func(t *testing.T) { t.Run("Disabled", func(t *testing.T) {
bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 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 c.ReservedAttributes = false
}) })
eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad) 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) eBad.SignTx(t, tx, 1_0000_0000, eBad.Committee)
err := bcBad.VerifyTx(tx) err := bcBad.VerifyTx(tx)
require.Error(t, err) 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 Domovoi is not active yet"))
}) })
t.Run("Enabled, insufficient network fee", func(t *testing.T) { t.Run("Enabled, insufficient network fee", func(t *testing.T) {
tx := getNotaryAssistedTx(e, 1, 0) tx := getNotaryAssistedTx(e, 1, 0)
@ -2572,7 +2577,7 @@ func TestBlockchain_GenesisTransactionExtension(t *testing.T) {
// in the right order. // in the right order.
func TestNativenames(t *testing.T) { func TestNativenames(t *testing.T) {
bc, _ := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) { bc, _ := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{} cfg.Hardforks = nil // default (all hardforks enabled) behaviour.
cfg.P2PSigExtensions = true cfg.P2PSigExtensions = true
}) })
natives := bc.GetNatives() natives := bc.GetNatives()

View file

@ -247,7 +247,7 @@ func (c *ContractMD) HFSpecificContractMD(hf *config.Hardfork) *HFSpecificContra
} }
md, ok := c.mdCache[key] md, ok := c.mdCache[key]
if !ok { 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 { if md == nil {
panic(fmt.Errorf("native contract descriptor cache is nil: contract %s, hardfork %s", c.Hash.StringLE(), key)) panic(fmt.Errorf("native contract descriptor cache is nil: contract %s, hardfork %s", c.Hash.StringLE(), key))

View file

@ -72,9 +72,9 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts {
cs.Ledger = ledger cs.Ledger = ledger
cs.Contracts = append(cs.Contracts, ledger) cs.Contracts = append(cs.Contracts, ledger)
gas := newGAS(int64(cfg.InitialGASSupply), cfg.P2PSigExtensions) gas := newGAS(int64(cfg.InitialGASSupply))
neo := newNEO(cfg) neo := newNEO(cfg)
policy := newPolicy(cfg.P2PSigExtensions) policy := newPolicy()
neo.GAS = gas neo.GAS = gas
neo.Policy = policy neo.Policy = policy
gas.NEO = neo gas.NEO = neo
@ -100,15 +100,13 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts {
cs.Oracle = oracle cs.Oracle = oracle
cs.Contracts = append(cs.Contracts, oracle) cs.Contracts = append(cs.Contracts, oracle)
if cfg.P2PSigExtensions { notary := newNotary()
notary := newNotary() notary.GAS = gas
notary.GAS = gas notary.NEO = neo
notary.NEO = neo notary.Desig = desig
notary.Desig = desig notary.Policy = policy
notary.Policy = policy cs.Notary = notary
cs.Notary = notary cs.Contracts = append(cs.Contracts, notary)
cs.Contracts = append(cs.Contracts, notary)
}
return cs return cs
} }

View file

@ -610,8 +610,6 @@ func (m *Management) OnPersist(ic *interop.Context) error {
for _, hf := range config.Hardforks { for _, hf := range config.Hardforks {
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) { if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
isUpdate = true 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 // 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). // (if there are multiple hardforks with the same enabling height).
break break
@ -626,6 +624,10 @@ func (m *Management) OnPersist(ic *interop.Context) error {
currentActiveHFs = append(currentActiveHFs, hf) currentActiveHFs = append(currentActiveHFs, hf)
} }
} }
// activeIn is not included into the activeHFs list.
if activeIn != nil && activeIn.Cmp(latestHF) > 0 {
latestHF = *activeIn
}
if !(isDeploy || isUpdate) { if !(isDeploy || isUpdate) {
continue continue
} }
@ -668,7 +670,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
// The rest of activating hardforks also require initialization. // The rest of activating hardforks also require initialization.
for _, hf := range currentActiveHFs { for _, hf := range currentActiveHFs {
if err := native.Initialize(ic, &hf, hfSpecificMD); err != nil { 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)
} }
} }

View file

@ -17,7 +17,7 @@ import (
func TestDeployGetUpdateDestroyContract(t *testing.T) { func TestDeployGetUpdateDestroyContract(t *testing.T) {
mgmt := newManagement() mgmt := newManagement()
mgmt.Policy = newPolicy(false) mgmt.Policy = newPolicy()
d := dao.NewSimple(storage.NewMemoryStore(), false) d := dao.NewSimple(storage.NewMemoryStore(), false)
ic := &interop.Context{DAO: d} ic := &interop.Context{DAO: d}
err := mgmt.Initialize(ic, nil, nil) err := mgmt.Initialize(ic, nil, nil)
@ -95,7 +95,7 @@ func TestManagement_Initialize(t *testing.T) {
func TestManagement_GetNEP17Contracts(t *testing.T) { func TestManagement_GetNEP17Contracts(t *testing.T) {
mgmt := newManagement() mgmt := newManagement()
mgmt.Policy = newPolicy(false) mgmt.Policy = newPolicy()
d := dao.NewSimple(storage.NewMemoryStore(), false) d := dao.NewSimple(storage.NewMemoryStore(), false)
err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil) err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil)
require.NoError(t, err) require.NoError(t, err)

View file

@ -22,8 +22,7 @@ type GAS struct {
NEO *NEO NEO *NEO
Policy *Policy Policy *Policy
initialSupply int64 initialSupply int64
p2pSigExtensionsEnabled bool
} }
const gasContractID = -6 const gasContractID = -6
@ -32,10 +31,9 @@ const gasContractID = -6
const GASFactor = NEOTotalSupply const GASFactor = NEOTotalSupply
// newGAS returns GAS native contract. // newGAS returns GAS native contract.
func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS { func newGAS(init int64) *GAS {
g := &GAS{ g := &GAS{
initialSupply: init, initialSupply: init,
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
} }
defer g.BuildHFSpecificMD(g.ActiveIn()) defer g.BuildHFSpecificMD(g.ActiveIn())
@ -122,14 +120,12 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
var netFee int64 var netFee int64
for _, tx := range ic.Block.Transactions { for _, tx := range ic.Block.Transactions {
netFee += tx.NetworkFee netFee += tx.NetworkFee
if g.p2pSigExtensionsEnabled { // Reward for NotaryAssisted attribute will be minted to designated notary nodes
// Reward for NotaryAssisted attribute will be minted to designated notary nodes // by Notary contract.
// by Notary contract. attrs := tx.GetAttributes(transaction.NotaryAssistedT)
attrs := tx.GetAttributes(transaction.NotaryAssistedT) if len(attrs) != 0 {
if len(attrs) != 0 { na := attrs[0].Value.(*transaction.NotaryAssisted)
na := attrs[0].Value.(*transaction.NotaryAssisted) netFee -= (int64(na.NKeys) + 1) * g.Policy.GetAttributeFeeInternal(ic.DAO, transaction.NotaryAssistedT)
netFee -= (int64(na.NKeys) + 1) * g.Policy.GetAttributeFeeInternal(ic.DAO, transaction.NotaryAssistedT)
}
} }
} }
g.mint(ic, primary, big.NewInt(int64(netFee)), false) g.mint(ic, primary, big.NewInt(int64(netFee)), false)

View file

@ -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.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.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.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) // cockatriceCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
// under assumption that hardforks from Aspidochelone to Cockatrice (included) are enabled. // 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.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}`, 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}`,
} }
// domovoiCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
// under assumption that hardforks from Aspidochelone to Domovoi (included) are enabled.
domovoiCSS = 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() { func init() {
@ -63,6 +68,11 @@ func init() {
cockatriceCSS[k] = v cockatriceCSS[k] = v
} }
} }
for k, v := range cockatriceCSS {
if _, ok := domovoiCSS[k]; !ok {
domovoiCSS[k] = v
}
}
} }
func newManagementClient(t *testing.T) *neotest.ContractInvoker { func newManagementClient(t *testing.T) *neotest.ContractInvoker {
@ -91,6 +101,10 @@ func TestManagement_GenesisNativeState(t *testing.T) {
h := state.CreateNativeContractHash(name) h := state.CreateNativeContractHash(name)
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) { c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
si := stack[0] 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{} var cs = &state.Contract{}
require.NoError(t, cs.FromStackItem(si), name) require.NoError(t, cs.FromStackItem(si), name)
jBytes, err := ojson.Marshal(cs) jBytes, err := ojson.Marshal(cs)
@ -113,6 +127,7 @@ func TestManagement_GenesisNativeState(t *testing.T) {
config.HFAspidochelone.String(): 100500, config.HFAspidochelone.String(): 100500,
config.HFBasilisk.String(): 100500, config.HFBasilisk.String(): 100500,
config.HFCockatrice.String(): 100500, config.HFCockatrice.String(): 100500,
config.HFDomovoi.String(): 100500,
} }
cfg.P2PSigExtensions = true cfg.P2PSigExtensions = true
}) })
@ -148,15 +163,31 @@ func TestManagement_GenesisNativeState(t *testing.T) {
}) })
check(t, mgmt, cockatriceCSS) check(t, mgmt, cockatriceCSS)
}) })
t.Run("Domovoi 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.HFDomovoi.String(): 0,
}
cfg.P2PSigExtensions = true
})
check(t, mgmt, domovoiCSS)
})
} }
func TestManagement_NativeDeployUpdateNotifications(t *testing.T) { func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
const cockatriceHeight = 3 const (
cockatriceHeight = 3
domovoiHeight = 5
)
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) { mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{ cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0, config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0, config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): cockatriceHeight, config.HFCockatrice.String(): cockatriceHeight,
config.HFDomovoi.String(): domovoiHeight,
} }
cfg.P2PSigExtensions = true cfg.P2PSigExtensions = true
}) })
@ -169,6 +200,8 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
var expected []state.NotificationEvent var expected []state.NotificationEvent
for _, name := range nativenames.All { for _, name := range nativenames.All {
switch name { switch name {
case nativenames.Notary:
continue
case nativenames.Gas: case nativenames.Gas:
expected = append(expected, state.NotificationEvent{ expected = append(expected, state.NotificationEvent{
ScriptHash: nativehashes.GasToken, ScriptHash: nativehashes.GasToken,
@ -200,7 +233,7 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
} }
require.Equal(t, expected, aer[0].Events) 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] cockatriceBlock := mgmt.GenerateNewBlocks(t, cockatriceHeight)[cockatriceHeight-1]
aer, err = mgmt.Chain.GetAppExecResults(cockatriceBlock.Hash(), trigger.OnPersist) aer, err = mgmt.Chain.GetAppExecResults(cockatriceBlock.Hash(), trigger.OnPersist)
require.NoError(t, err) require.NoError(t, err)
@ -216,15 +249,42 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
}) })
} }
require.Equal(t, expected, aer[0].Events) require.Equal(t, expected, aer[0].Events)
// Generate some blocks and check Deploy notifications for Domovoi hardfork.
mgmt.GenerateNewBlocks(t, domovoiHeight-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) { func TestManagement_NativeUpdate(t *testing.T) {
const cockatriceHeight = 3 const (
cockatriceHeight = 3
domovoiHeight = 6
)
c := newCustomManagementClient(t, func(cfg *config.Blockchain) { c := newCustomManagementClient(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{ cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0, config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0, config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): cockatriceHeight, config.HFCockatrice.String(): cockatriceHeight,
config.HFDomovoi.String(): domovoiHeight,
} }
cfg.P2PSigExtensions = true cfg.P2PSigExtensions = true
}) })
@ -235,7 +295,12 @@ func TestManagement_NativeUpdate(t *testing.T) {
for _, name := range nativenames.All { for _, name := range nativenames.All {
h := state.CreateNativeContractHash(name) h := state.CreateNativeContractHash(name)
cs := c.Chain.GetContractState(h) 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) jBytes, err := ojson.Marshal(cs)
require.NoError(t, err, name) require.NoError(t, err, name)
require.Equal(t, defaultCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", 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 { for _, name := range nativenames.All {
h := state.CreateNativeContractHash(name) h := state.CreateNativeContractHash(name)
cs := c.Chain.GetContractState(h) 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 { if name == nativenames.Neo || name == nativenames.CryptoLib {
// A tiny hack to reuse cockatriceCSS map in the check below. // A tiny hack to reuse cockatriceCSS map in the check below.
require.Equal(t, uint16(1), cs.UpdateCounter, name) 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.NoError(t, err, name)
require.Equal(t, cockatriceCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name)) require.Equal(t, cockatriceCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
} }
// Add some blocks up to the Domovoi enabling height and check the natives state.
for i := c.Chain.BlockHeight(); i < domovoiHeight-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 domovoiCSS 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, domovoiCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
}
}
} }
func TestManagement_NativeUpdate_Call(t *testing.T) { func TestManagement_NativeUpdate_Call(t *testing.T) {
@ -292,12 +391,16 @@ func TestManagement_NativeUpdate_Call(t *testing.T) {
// different block heights depending on hardfork settings. This test is located here since it // different block heights depending on hardfork settings. This test is located here since it
// depends on defaultCSS and cockatriceCSS. // depends on defaultCSS and cockatriceCSS.
func TestBlockchain_GetNatives(t *testing.T) { func TestBlockchain_GetNatives(t *testing.T) {
const cockatriceHeight = 3 const (
cockatriceHeight = 3
domovoiHeight = 6
)
bc, acc := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) { bc, acc := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{ cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0, config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0, config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): cockatriceHeight, config.HFCockatrice.String(): cockatriceHeight,
config.HFDomovoi.String(): domovoiHeight,
} }
cfg.P2PSigExtensions = true cfg.P2PSigExtensions = true
}) })
@ -305,7 +408,7 @@ func TestBlockchain_GetNatives(t *testing.T) {
// Check genesis-based native contract states. // Check genesis-based native contract states.
natives := bc.GetNatives() 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 { for _, cs := range natives {
csFull := state.Contract{ csFull := state.Contract{
ContractBase: cs.ContractBase, ContractBase: cs.ContractBase,
@ -316,10 +419,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)) 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) e.GenerateNewBlocks(t, cockatriceHeight)
natives = bc.GetNatives() 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 { for _, cs := range natives {
csFull := state.Contract{ csFull := state.Contract{
ContractBase: cs.ContractBase, ContractBase: cs.ContractBase,
@ -329,6 +432,20 @@ func TestBlockchain_GetNatives(t *testing.T) {
require.NoError(t, err, cs.Manifest.Name) 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)) require.Equal(t, cockatriceCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name))
} }
// Check native state after Domovoi.
e.GenerateNewBlocks(t, domovoiHeight-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 domovoiCSS.
}
jBytes, err := ojson.Marshal(csFull)
require.NoError(t, err, cs.Manifest.Name)
require.Equal(t, domovoiCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name))
}
} }
func TestManagement_ContractCache(t *testing.T) { func TestManagement_ContractCache(t *testing.T) {

View file

@ -52,7 +52,10 @@ const (
defaultMaxNotValidBeforeDelta = 140 // 20 rounds for 7 validators, a little more than half an hour defaultMaxNotValidBeforeDelta = 140 // 20 rounds for 7 validators, a little more than half an hour
) )
var maxNotValidBeforeDeltaKey = []byte{10} var (
maxNotValidBeforeDeltaKey = []byte{10}
activeIn = config.HFDomovoi
)
var ( var (
_ interop.Contract = (*Notary)(nil) _ interop.Contract = (*Notary)(nil)
@ -200,7 +203,7 @@ func (n *Notary) PostPersist(ic *interop.Context) error {
// ActiveIn implements the Contract interface. // ActiveIn implements the Contract interface.
func (n *Notary) ActiveIn() *config.Hardfork { func (n *Notary) ActiveIn() *config.Hardfork {
return nil return &activeIn
} }
// onPayment records the deposited amount as belonging to "from" address with a lock // onPayment records the deposited amount as belonging to "from" address with a lock

View file

@ -63,9 +63,6 @@ var (
type Policy struct { type Policy struct {
interop.ContractMD interop.ContractMD
NEO *NEO NEO *NEO
// p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant.
p2pSigExtensionsEnabled bool
} }
type PolicyCache struct { type PolicyCache struct {
@ -100,11 +97,8 @@ func copyPolicyCache(src, dst *PolicyCache) {
} }
// newPolicy returns Policy native contract. // newPolicy returns Policy native contract.
func newPolicy(p2pSigExtensionsEnabled bool) *Policy { func newPolicy() *Policy {
p := &Policy{ p := &Policy{ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID)}
ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID),
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
}
defer p.BuildHFSpecificMD(p.ActiveIn()) defer p.BuildHFSpecificMD(p.ActiveIn())
desc := newDescriptor("getFeePerByte", smartcontract.IntegerType) desc := newDescriptor("getFeePerByte", smartcontract.IntegerType)
@ -136,13 +130,24 @@ func newPolicy(p2pSigExtensionsEnabled bool) *Policy {
desc = newDescriptor("getAttributeFee", smartcontract.IntegerType, desc = newDescriptor("getAttributeFee", smartcontract.IntegerType,
manifest.NewParameter("attributeType", 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) p.AddMethod(md, desc)
desc = newDescriptor("setAttributeFee", smartcontract.VoidType, desc = newDescriptor("setAttributeFee", smartcontract.VoidType,
manifest.NewParameter("attributeType", smartcontract.IntegerType), manifest.NewParameter("attributeType", smartcontract.IntegerType),
manifest.NewParameter("value", 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) p.AddMethod(md, desc)
desc = newDescriptor("setFeePerByte", smartcontract.VoidType, 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. // Initialize initializes Policy native contract and implements the Contract interface.
func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
if hf != p.ActiveIn() { 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)
setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte) cache := &PolicyCache{
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor) execFeeFactor: defaultExecFeeFactor,
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice) feePerByte: defaultFeePerByte,
maxVerificationGas: defaultMaxVerificationGas,
cache := &PolicyCache{ storagePrice: DefaultStoragePrice,
execFeeFactor: defaultExecFeeFactor, attributeFee: map[transaction.AttrType]uint32{},
feePerByte: defaultFeePerByte, blockedAccounts: make([]util.Uint160, 0),
maxVerificationGas: defaultMaxVerificationGas, }
storagePrice: DefaultStoragePrice, ic.DAO.SetCache(p.ID, cache)
attributeFee: map[transaction.AttrType]uint32{},
blockedAccounts: make([]util.Uint160, 0),
} }
if p.p2pSigExtensionsEnabled { if hf != nil && *hf == transaction.NotaryAssistedActivation {
setIntWithKey(p.ID, ic.DAO, []byte{attributeFeePrefix, byte(transaction.NotaryAssistedT)}, defaultNotaryAssistedFee) setIntWithKey(p.ID, ic.DAO, []byte{attributeFeePrefix, byte(transaction.NotaryAssistedT)}, defaultNotaryAssistedFee)
cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
cache.attributeFee[transaction.NotaryAssistedT] = defaultNotaryAssistedFee cache.attributeFee[transaction.NotaryAssistedT] = defaultNotaryAssistedFee
} }
ic.DAO.SetCache(p.ID, cache)
return nil return nil
} }
@ -363,9 +368,18 @@ func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) sta
return stackitem.Null{} 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])) 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)) panic(fmt.Errorf("invalid attribute type: %d", t))
} }
return stackitem.NewBigInteger(big.NewInt(p.GetAttributeFeeInternal(ic.DAO, 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) 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])) t := transaction.AttrType(toUint8(args[0]))
value := toUint32(args[1]) 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)) panic(fmt.Errorf("invalid attribute type: %d", t))
} }
if value > maxAttributeFee { if value > maxAttributeFee {

View file

@ -4,9 +4,12 @@ import (
"fmt" "fmt"
"testing" "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/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native" "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/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"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain" "github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -65,3 +68,51 @@ func TestPolicy_BlockedAccounts(t *testing.T) {
require.Equal(t, expectedErr, err.Error()) require.Equal(t, expectedErr, err.Error())
}) })
} }
func TestPolicy_GetNotaryFeePerKey(t *testing.T) {
const domovoiHeight = 4
bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) {
c.Hardforks = map[string]uint32{
config.HFDomovoi.String(): domovoiHeight,
}
})
e := neotest.NewExecutor(t, bc, acc, acc)
p := e.CommitteeInvoker(nativehashes.PolicyContract)
// Invoke before Domovoi should fail.
p.InvokeFail(t, "invalid attribute type: 34", "getAttributeFee", int64(transaction.NotaryAssistedT))
for e.Chain.BlockHeight() < domovoiHeight-1 {
e.AddNewBlock(t)
}
// Invoke at Domovoi should return the default value.
p.Invoke(t, 1000_0000, "getAttributeFee", int64(transaction.NotaryAssistedT))
// Invoke after Domovoi should return the default value.
p.Invoke(t, 1000_0000, "getAttributeFee", int64(transaction.NotaryAssistedT))
}
func TestPolicy_SetNotaryFeePerKey(t *testing.T) {
const domovoiHeight = 4
bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) {
c.Hardforks = map[string]uint32{
config.HFDomovoi.String(): domovoiHeight,
}
})
e := neotest.NewExecutor(t, bc, acc, acc)
p := e.CommitteeInvoker(nativehashes.PolicyContract)
// Invoke before Domovoi should fail.
p.InvokeFail(t, "invalid attribute type: 34", "setAttributeFee", int64(transaction.NotaryAssistedT), 500_0000)
for e.Chain.BlockHeight() < domovoiHeight-1 {
e.AddNewBlock(t)
}
// Invoke at Domovoi should return the default value.
p.Invoke(t, nil, "setAttributeFee", int64(transaction.NotaryAssistedT), 500_0000)
// Invoke after Domovoi should return the default value.
p.Invoke(t, nil, "setAttributeFee", int64(transaction.NotaryAssistedT), 510_0000)
}

View file

@ -1,9 +1,14 @@
package transaction package transaction
import ( import (
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
) )
// NotaryAssistedActivation stores the hardfork of NotaryAssisted transaction attribute
// activation.
var NotaryAssistedActivation = config.HFDomovoi
// NotaryAssisted represents attribute for notary service transactions. // NotaryAssisted represents attribute for notary service transactions.
type NotaryAssisted struct { type NotaryAssisted struct {
NKeys uint8 `json:"nkeys"` NKeys uint8 `json:"nkeys"`

View file

@ -21,6 +21,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "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/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/core/mpt" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
@ -65,7 +66,6 @@ type (
GetMaxVerificationGAS() int64 GetMaxVerificationGAS() int64
GetMemPool() *mempool.Pool GetMemPool() *mempool.Pool
GetNotaryBalance(acc util.Uint160) *big.Int GetNotaryBalance(acc util.Uint160) *big.Int
GetNotaryContractScriptHash() util.Uint160
GetNotaryDepositExpiration(acc util.Uint160) uint32 GetNotaryDepositExpiration(acc util.Uint160) uint32
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
HasBlock(util.Uint256) bool HasBlock(util.Uint256) bool
@ -1228,7 +1228,7 @@ func (s *Server) verifyNotaryRequest(_ *transaction.Transaction, data any) error
if _, err := s.chain.VerifyWitness(payer, r, &r.Witness, s.chain.GetMaxVerificationGAS()); err != nil { if _, err := s.chain.VerifyWitness(payer, r, &r.Witness, s.chain.GetMaxVerificationGAS()); err != nil {
return fmt.Errorf("bad P2PNotaryRequest payload witness: %w", err) return fmt.Errorf("bad P2PNotaryRequest payload witness: %w", err)
} }
notaryHash := s.chain.GetNotaryContractScriptHash() notaryHash := nativehashes.Notary
if r.FallbackTransaction.Sender() != notaryHash { 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())) return fmt.Errorf("P2PNotary contract should be a sender of the fallback transaction, got %s", address.Uint160ToString(r.FallbackTransaction.Sender()))
} }

View file

@ -19,6 +19,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block" "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/interop"
"github.com/nspcc-dev/neo-go/pkg/core/mpt" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/network/capability" "github.com/nspcc-dev/neo-go/pkg/network/capability"
@ -1048,7 +1049,6 @@ func TestMemPool(t *testing.T) {
func TestVerifyNotaryRequest(t *testing.T) { func TestVerifyNotaryRequest(t *testing.T) {
bc := fakechain.NewFakeChain() bc := fakechain.NewFakeChain()
bc.MaxVerificationGAS = 10 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) s, err := newServerFromConstructors(ServerConfig{Addresses: []config.AnnounceableAddress{{Address: ":0"}}}, bc, new(fakechain.FakeStateSync), zaptest.NewLogger(t), newFakeTransp, newTestDiscovery)
require.NoError(t, err) require.NoError(t, err)
newNotaryRequest := func() *payload.P2PNotaryRequest { newNotaryRequest := func() *payload.P2PNotaryRequest {
@ -1059,7 +1059,7 @@ func TestVerifyNotaryRequest(t *testing.T) {
}, },
FallbackTransaction: &transaction.Transaction{ FallbackTransaction: &transaction.Transaction{
ValidUntilBlock: 321, ValidUntilBlock: 321,
Signers: []transaction.Signer{{Account: bc.NotaryContractScriptHash}, {Account: random.Uint160()}}, Signers: []transaction.Signer{{Account: nativehashes.Notary}, {Account: random.Uint160()}},
}, },
Witness: transaction.Witness{}, Witness: transaction.Witness{},
} }
@ -1080,7 +1080,7 @@ func TestVerifyNotaryRequest(t *testing.T) {
t.Run("bad main sender", func(t *testing.T) { t.Run("bad main sender", func(t *testing.T) {
bc.VerifyWitnessF = func() (int64, error) { return 0, nil } bc.VerifyWitnessF = func() (int64, error) { return 0, nil }
r := newNotaryRequest() 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)) require.Error(t, s.verifyNotaryRequest(nil, r))
}) })

View file

@ -14,6 +14,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core" "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/block"
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "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/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "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.ValidUntilBlock = bc.BlockHeight() + 2*nvbDiffFallback
fallback.Signers = []transaction.Signer{ fallback.Signers = []transaction.Signer{
{ {
Account: bc.GetNotaryContractScriptHash(), Account: nativehashes.Notary,
Scopes: transaction.None, Scopes: transaction.None,
}, },
{ {
@ -240,7 +241,7 @@ func TestNotary(t *testing.T) {
verificationScripts = append(verificationScripts, script) verificationScripts = append(verificationScripts, script)
} }
signers[len(signers)-1] = transaction.Signer{ signers[len(signers)-1] = transaction.Signer{
Account: bc.GetNotaryContractScriptHash(), Account: nativehashes.Notary,
Scopes: transaction.None, Scopes: transaction.None,
} }
mainTx.Signers = signers mainTx.Signers = signers
@ -752,9 +753,9 @@ func TestNotary(t *testing.T) {
requester1, _ := wallet.NewAccount() requester1, _ := wallet.NewAccount()
requester2, _ := wallet.NewAccount() requester2, _ := wallet.NewAccount()
amount := int64(100_0000_0000) 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)) 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)) 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 // create request for 2 standard signatures => main tx should be completed after the second request is added to the pool

View file

@ -14,6 +14,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block" "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/mempool"
"github.com/nspcc-dev/neo-go/pkg/core/mempoolevent" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "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/crypto/keys"
@ -31,7 +32,6 @@ type (
Ledger interface { Ledger interface {
BlockHeight() uint32 BlockHeight() uint32
GetMaxVerificationGAS() int64 GetMaxVerificationGAS() int64
GetNotaryContractScriptHash() util.Uint160
SubscribeForBlocks(ch chan *block.Block) SubscribeForBlocks(ch chan *block.Block)
UnsubscribeFromBlocks(ch chan *block.Block) UnsubscribeFromBlocks(ch chan *block.Block)
VerifyWitness(util.Uint160, hash.Hashable, *transaction.Witness, int64) (int64, error) 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{}, VerificationScript: []byte{},
} }
for i, signer := range tx.Signers { for i, signer := range tx.Signers {
if signer.Account == n.Config.Chain.GetNotaryContractScriptHash() { if signer.Account == nativehashes.Notary {
tx.Scripts[i] = notaryWitness tx.Scripts[i] = notaryWitness
break break
} }
@ -510,7 +510,7 @@ func (n *Notary) verifyIncompleteWitnesses(tx *transaction.Transaction, nKeysExp
if len(tx.Signers) < 2 { if len(tx.Signers) < 2 {
return nil, errors.New("transaction should have at least 2 signers") 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") return nil, fmt.Errorf("P2PNotary contract should be a signer of the transaction")
} }
result := make([]witnessInfo, len(tx.Signers)) result := make([]witnessInfo, len(tx.Signers))

View file

@ -7,11 +7,11 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "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/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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
@ -48,8 +48,7 @@ func TestWallet(t *testing.T) {
func TestVerifyIncompleteRequest(t *testing.T) { func TestVerifyIncompleteRequest(t *testing.T) {
bc := fakechain.NewFakeChain() bc := fakechain.NewFakeChain()
notaryContractHash := util.Uint160{1, 2, 3} notaryContractHash := nativehashes.Notary
bc.NotaryContractScriptHash = notaryContractHash
_, ntr, _ := getTestNotary(t, bc, "./testdata/notary1.json", "one") _, 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 sig := append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...) // we're not interested in signature correctness
acc1, _ := keys.NewPrivateKey() acc1, _ := keys.NewPrivateKey()

View file

@ -465,6 +465,10 @@ func TestClientNEOContract(t *testing.T) {
func TestClientNotary(t *testing.T) { func TestClientNotary(t *testing.T) {
chain, _, httpSrv := initServerWithInMemoryChain(t) chain, _, httpSrv := initServerWithInMemoryChain(t)
// Domovoi should be enabled since this test uses Notary contract.
_, ok := chain.GetConfig().Hardforks[config.HFDomovoi.String()]
require.True(t, ok)
c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(c.Close) t.Cleanup(c.Close)
@ -2446,7 +2450,10 @@ func TestClient_GetVersion_Hardforks(t *testing.T) {
v, err := c.GetVersion() v, err := c.GetVersion()
require.NoError(t, err) require.NoError(t, err)
expected := map[config.Hardfork]uint32{ expected := map[config.Hardfork]uint32{
config.HFAspidochelone: 25, config.HFAspidochelone: 0,
config.HFBasilisk: 0,
config.HFCockatrice: 0,
config.HFDomovoi: 0,
} }
require.InDeltaMapValues(t, expected, v.Protocol.Hardforks, 0) require.InDeltaMapValues(t, expected, v.Protocol.Hardforks, 0)
} }

View file

@ -88,7 +88,6 @@ type (
GetNativeContractScriptHash(string) (util.Uint160, error) GetNativeContractScriptHash(string) (util.Uint160, error)
GetNatives() []state.Contract GetNatives() []state.Contract
GetNextBlockValidators() ([]*keys.PublicKey, error) GetNextBlockValidators() ([]*keys.PublicKey, error)
GetNotaryContractScriptHash() util.Uint160
GetStateModule() core.StateRoot GetStateModule() core.StateRoot
GetStorageItem(id int32, key []byte) state.StorageItem GetStorageItem(id int32, key []byte) state.StorageItem
GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, nextBlockHeight uint32) (*interop.Context, error) GetTestHistoricVM(t trigger.Type, tx *transaction.Transaction, nextBlockHeight uint32) (*interop.Context, error)

View file

@ -54,6 +54,7 @@ func getUnitTestChain(t testing.TB, enableOracle bool, enableNotary bool, disabl
Password: "one", 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) { func getUnitTestChainWithCustomConfig(t testing.TB, enableOracle bool, enableNotary bool, customCfg func(configuration *config.Config)) (*core.Blockchain, OracleHandler, config.Config, *zap.Logger) {

View file

@ -31,6 +31,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block" "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/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "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/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper" "github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
@ -74,22 +75,22 @@ type rpcTestCase struct {
} }
const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4" const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4"
const testContractHash = "565cff9508ebc75aadd7fe59f38dac610ab6093c" const testContractHash = "449fe8fbd4523072f5e3a4dfa17a494c119d4c08"
const deploymentTxHash = "a14390941cc3a1d87393eff720a722e9cd350bd6ed233c5fe2001326c80eb68e" const deploymentTxHash = "af170742f0f8a2bc064bdbdb2faa2b517e3df833d4d047da8a946c0b8d581b06"
const ( const (
verifyContractHash = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c" verifyContractHash = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c"
verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A=" verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A="
verifyWithArgsContractHash = "4dc916254efd2947c93b11207e8ffc0bb56161c5" verifyWithArgsContractHash = "6261b3bf753bdc3d24c1327a23fd891e1c8a7ccd"
nnsContractHash = "892429fcd47c30f8451781acc627e8b20e0d64f3" nnsContractHash = "ebe47d5143bb8726b87b02efb5cd98e21174fd38"
nnsToken1ID = "6e656f2e636f6d" nnsToken1ID = "6e656f2e636f6d"
nfsoContractHash = "730ebe719ab8e3b69d11dafc95cdb9bf409db179" nfsoContractHash = "2f5c1826bb4da1c764a8871427e4044cf3e82dbd"
nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486" nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486"
storageContractHash = "ebc0c16a76c808cd4dde6bcc063f09e45e331ec7" storageContractHash = "ebc0c16a76c808cd4dde6bcc063f09e45e331ec7"
faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60" faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60"
faultedTxBlock uint32 = 23 faultedTxBlock uint32 = 23
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA" invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
block20StateRootLE = "858c873539d6d24a70f2be13f9dafc61aef2b63c2aa16bb440676de6e44e3cf1" block20StateRootLE = "b49a35fd3a749fc2f7f4e5fe1f288ef2b6188416f65fe5b691892e8209092082"
) )
var ( var (
@ -1381,7 +1382,7 @@ var rpcTestCases = map[string][]rpcTestCase{
script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
return &result.Invoke{ return &result.Invoke{
State: "HALT", State: "HALT",
GasConsumed: 31922970, GasConsumed: 31922730,
Script: script, Script: script,
Stack: []stackitem.Item{stackitem.Make(true)}, Stack: []stackitem.Item{stackitem.Make(true)},
Notifications: []state.NotificationEvent{{ Notifications: []state.NotificationEvent{{
@ -1411,7 +1412,7 @@ var rpcTestCases = map[string][]rpcTestCase{
chg := []dboper.Operation{{ chg := []dboper.Operation{{
State: "Changed", State: "Changed",
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb}, 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", 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}, 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", 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}, 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. // Can be returned in any order.
assert.ElementsMatch(t, chg, res.Diagnostics.Changes) assert.ElementsMatch(t, chg, res.Diagnostics.Changes)
@ -1439,7 +1440,7 @@ var rpcTestCases = map[string][]rpcTestCase{
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib) cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
return &result.Invoke{ return &result.Invoke{
State: "HALT", State: "HALT",
GasConsumed: 13970250, GasConsumed: 13969530,
Script: script, Script: script,
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")}, Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
Notifications: []state.NotificationEvent{}, Notifications: []state.NotificationEvent{},
@ -1532,7 +1533,7 @@ var rpcTestCases = map[string][]rpcTestCase{
script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52) script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
return &result.Invoke{ return &result.Invoke{
State: "HALT", State: "HALT",
GasConsumed: 31922970, GasConsumed: 31922730,
Script: script, Script: script,
Stack: []stackitem.Item{stackitem.Make(true)}, Stack: []stackitem.Item{stackitem.Make(true)},
Notifications: []state.NotificationEvent{{ Notifications: []state.NotificationEvent{{
@ -1558,7 +1559,7 @@ var rpcTestCases = map[string][]rpcTestCase{
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib) cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
return &result.Invoke{ return &result.Invoke{
State: "HALT", State: "HALT",
GasConsumed: 13970250, GasConsumed: 13969530,
Script: script, Script: script,
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")}, Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
Notifications: []state.NotificationEvent{}, 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.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}},
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}}, {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{ Scripts: []transaction.Witness{
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}}, {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) { t.Run("contract-based verification with parameters", func(t *testing.T) {
verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash) verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash)
require.NoError(t, err) 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) { t.Run("contract-based verification with invocation script", func(t *testing.T) {
verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash) 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.Int(invocWriter.BinWriter, 5)
emit.String(invocWriter.BinWriter, "") emit.String(invocWriter.BinWriter, "")
invocScript := invocWriter.Bytes() 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) { t.Run("execution limit, ok", func(t *testing.T) {
// 1_4000_0000 GAS with the default 1.5 allowed by Policy // 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(), Asset: e.chain.UtilityTokenHash(),
Amount: "37106285100", Amount: "37106870550",
LastUpdated: 23, LastUpdated: 23,
Decimals: 8, Decimals: 8,
Name: "GasToken", Name: "GasToken",
@ -3924,7 +3925,7 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int
{ {
Timestamp: blockDepositGAS.Timestamp, Timestamp: blockDepositGAS.Timestamp,
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Address: address.Uint160ToString(e.chain.GetNotaryContractScriptHash()), Address: address.Uint160ToString(nativehashes.Notary),
Amount: "1000000000", Amount: "1000000000",
Index: 8, Index: 8,
NotifyIndex: 0, NotifyIndex: 0,

Binary file not shown.