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

View file

@ -25,8 +25,9 @@ ProtocolConfiguration:
P2PSigExtensions: true
Hardforks:
Aspidochelone: 3000000
Basilisk: 4500000
Cockatrice: 5800000
Basilisk: 3500000
Cockatrice: 3500000
Domovoi: 3500000
ApplicationConfiguration:
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. |
| 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. |
| 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`). |
| 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. |

View file

@ -8,6 +8,7 @@ import (
"path/filepath"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
@ -81,6 +82,11 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) {
t.Fatal("P2PSigExtensions should be enabled to init basic chain")
}
const notaryDepositHeight uint32 = 8
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 (
// examplesPrefix is a prefix of the example smart-contracts.
examplesPrefix = filepath.Join(rootpath, "examples")
@ -189,6 +195,7 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) {
// Block #8: deposit some GAS to notary contract for priv0.
transferTxH = gasPriv0Invoker.Invoke(t, true, "transfer", priv0ScriptHash, notaryHash, 10_0000_0000, []any{priv0ScriptHash, int64(e.Chain.BlockHeight() + 1000)})
t.Logf("notaryDepositTxPriv0: %v", transferTxH.StringLE())
require.Equal(t, uint32(notaryDepositHeight), e.Chain.BlockHeight(), "notaryDepositHeight constant is out of date")
// Block #9: designate new Notary node.
ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json"))

View file

@ -33,7 +33,6 @@ type FakeChain struct {
txs map[util.Uint256]*transaction.Transaction
VerifyWitnessF func() (int64, error)
MaxVerificationGAS int64
NotaryContractScriptHash util.Uint160
NotaryDepositExpiration uint32
PostBlock []func(func(*transaction.Transaction, *mempool.Pool, bool) bool, *mempool.Pool, *block.Block)
UtilityTokenBalance *big.Int
@ -111,14 +110,6 @@ func (chain *FakeChain) GetNotaryDepositExpiration(acc util.Uint160) uint32 {
panic("TODO")
}
// GetNotaryContractScriptHash implements the Blockchainer interface.
func (chain *FakeChain) GetNotaryContractScriptHash() util.Uint160 {
if !chain.NotaryContractScriptHash.Equals(util.Uint160{}) {
return chain.NotaryContractScriptHash
}
panic("TODO")
}
// GetNotaryBalance implements the Blockchainer interface.
func (chain *FakeChain) GetNotaryBalance(acc util.Uint160) *big.Int {
panic("TODO")

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/3154).
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
// before hfLast.
hfLast

View file

@ -12,13 +12,15 @@ func _() {
_ = x[HFAspidochelone-1]
_ = x[HFBasilisk-2]
_ = x[HFCockatrice-4]
_ = x[hfLast-8]
_ = x[HFDomovoi-8]
_ = x[hfLast-16]
}
const (
_Hardfork_name_0 = "DefaultAspidocheloneBasilisk"
_Hardfork_name_1 = "Cockatrice"
_Hardfork_name_2 = "hfLast"
_Hardfork_name_2 = "Domovoi"
_Hardfork_name_3 = "hfLast"
)
var (
@ -33,6 +35,8 @@ func (i Hardfork) String() string {
return _Hardfork_name_1
case i == 8:
return _Hardfork_name_2
case i == 16:
return _Hardfork_name_3
default:
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/mpt"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/stateroot"
@ -1070,7 +1071,7 @@ func (bc *Blockchain) isHardforkEnabled(hf *config.Hardfork, blockHeight uint32)
hfs := bc.config.Hardforks
if hf != nil {
start, ok := hfs[hf.String()]
if !ok || start < blockHeight {
if !ok || start > blockHeight {
return false
}
}
@ -2104,26 +2105,30 @@ func (bc *Blockchain) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint
}
// GetNotaryBalance returns Notary deposit amount for the specified account.
// Default value is returned if Notary contract is not yet active.
func (bc *Blockchain) GetNotaryBalance(acc util.Uint160) *big.Int {
if !bc.isHardforkEnabled(bc.contracts.Notary.ActiveIn(), bc.BlockHeight()) {
return nil
}
return bc.contracts.Notary.BalanceOf(bc.dao, acc)
}
// GetNotaryServiceFeePerKey returns a NotaryAssisted transaction attribute fee
// per key which is a reward per notary request key for designated notary nodes.
// Default value is returned if Notary contract is not yet active.
func (bc *Blockchain) GetNotaryServiceFeePerKey() int64 {
if !bc.isHardforkEnabled(&transaction.NotaryAssistedActivation, bc.BlockHeight()) {
return 0
}
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.
// Default value is returned if Notary contract is not yet active.
func (bc *Blockchain) GetNotaryDepositExpiration(acc util.Uint160) uint32 {
if !bc.isHardforkEnabled(bc.contracts.Notary.ActiveIn(), bc.BlockHeight()) {
return 0
}
return bc.contracts.Notary.ExpirationOf(bc.dao, acc)
}
@ -2695,10 +2700,10 @@ func (bc *Blockchain) verifyTxAttributes(d *dao.Simple, tx *transaction.Transact
return fmt.Errorf("%w: conflicting transaction %s is already on chain", ErrInvalidAttribute, conflicts.Hash.StringLE())
}
case transaction.NotaryAssistedT:
if !bc.config.P2PSigExtensions {
return fmt.Errorf("%w: NotaryAssisted attribute was found, but P2PSigExtensions are disabled", ErrInvalidAttribute)
if !bc.isHardforkEnabled(&transaction.NotaryAssistedActivation, bc.BlockHeight()) {
return fmt.Errorf("%w: NotaryAssisted attribute was found, but %s is not active yet", ErrInvalidAttribute, transaction.NotaryAssistedActivation)
}
if !tx.HasSigner(bc.contracts.Notary.Hash) {
if !tx.HasSigner(nativehashes.Notary) {
return fmt.Errorf("%w: NotaryAssisted attribute was found, but transaction is not signed by the Notary native contract", ErrInvalidAttribute)
}
default:
@ -3093,9 +3098,6 @@ func (bc *Blockchain) GetMaxVerificationGAS() int64 {
// GetMaxNotValidBeforeDelta returns maximum NotValidBeforeDelta Notary limit.
func (bc *Blockchain) GetMaxNotValidBeforeDelta() (uint32, error) {
if !bc.config.P2PSigExtensions {
panic("disallowed call to Notary") // critical error, thus panic.
}
if !bc.isHardforkEnabled(bc.contracts.Notary.ActiveIn(), bc.BlockHeight()) {
return 0, fmt.Errorf("native Notary is active starting from %s", bc.contracts.Notary.ActiveIn().String())
}

View file

@ -370,6 +370,7 @@ func TestNewBlockchain_InitHardforks(t *testing.T) {
config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): 0,
config.HFDomovoi.String(): 0,
}, bc.GetConfig().Hardforks)
})
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) {
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.Equal(t, map[string]uint32{
config.HFAspidochelone.String(): 5,
config.HFBasilisk.String(): 10,
config.HFCockatrice.String(): 15,
config.HFDomovoi.String(): 20,
}, bc.GetConfig().Hardforks)
})
}

View file

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

View file

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

View file

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

View file

@ -610,8 +610,6 @@ func (m *Management) OnPersist(ic *interop.Context) error {
for _, hf := range config.Hardforks {
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
isUpdate = true
activation := hf // avoid loop variable pointer exporting.
activeIn = &activation // reuse ActiveIn variable for the initialization hardfork.
// Break immediately since native Initialize should be called starting from the first hardfork in a raw
// (if there are multiple hardforks with the same enabling height).
break
@ -626,6 +624,10 @@ func (m *Management) OnPersist(ic *interop.Context) error {
currentActiveHFs = append(currentActiveHFs, hf)
}
}
// activeIn is not included into the activeHFs list.
if activeIn != nil && activeIn.Cmp(latestHF) > 0 {
latestHF = *activeIn
}
if !(isDeploy || isUpdate) {
continue
}
@ -668,7 +670,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
// The rest of activating hardforks also require initialization.
for _, hf := range currentActiveHFs {
if err := native.Initialize(ic, &hf, hfSpecificMD); err != nil {
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err)
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, hf, err)
}
}

View file

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

View file

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

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.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Oracle: `{"id":-9,"hash":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"OracleContract","abi":{"methods":[{"name":"finish","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"getPrice","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"request","offset":14,"parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"Any"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setPrice","offset":21,"parameters":[{"name":"price","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":28,"parameters":[],"returntype":"Boolean","safe":true}],"events":[{"name":"OracleRequest","parameters":[{"name":"Id","type":"Integer"},{"name":"RequestContract","type":"Hash160"},{"name":"Url","type":"String"},{"name":"Filter","type":"String"}]},{"name":"OracleResponse","parameters":[{"name":"Id","type":"Integer"},{"name":"OriginalTx","type":"Hash256"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Notary: `{"id":-10,"hash":"0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"Notary","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"expirationOf","offset":7,"parameters":[{"name":"addr","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"getMaxNotValidBeforeDelta","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"lockDepositUntil","offset":21,"parameters":[{"name":"address","type":"Hash160"},{"name":"till","type":"Integer"}],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":28,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"setMaxNotValidBeforeDelta","offset":35,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"verify","offset":42,"parameters":[{"name":"signature","type":"Signature"}],"returntype":"Boolean","safe":true},{"name":"withdraw","offset":49,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
}
// cockatriceCSS holds serialized native contract states built for genesis block (with UpdateCounter 0)
// under assumption that hardforks from Aspidochelone to Cockatrice (included) are enabled.
@ -55,6 +54,12 @@ var (
nativenames.CryptoLib: `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1094259016},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"keccak256","offset":42,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":49,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":56,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":70,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
nativenames.Neo: `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1325686241},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"registerCandidate","offset":77,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":84,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":91,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":98,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":105,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":112,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":119,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":126,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":133,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null},"updatecounter":0}`,
}
// 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() {
@ -63,6 +68,11 @@ func init() {
cockatriceCSS[k] = v
}
}
for k, v := range cockatriceCSS {
if _, ok := domovoiCSS[k]; !ok {
domovoiCSS[k] = v
}
}
}
func newManagementClient(t *testing.T) *neotest.ContractInvoker {
@ -91,6 +101,10 @@ func TestManagement_GenesisNativeState(t *testing.T) {
h := state.CreateNativeContractHash(name)
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
si := stack[0]
if _, ok := expected[name]; !ok {
require.Equal(t, stackitem.Null{}, si, fmt.Errorf("contract %s state found", name))
return
}
var cs = &state.Contract{}
require.NoError(t, cs.FromStackItem(si), name)
jBytes, err := ojson.Marshal(cs)
@ -113,6 +127,7 @@ func TestManagement_GenesisNativeState(t *testing.T) {
config.HFAspidochelone.String(): 100500,
config.HFBasilisk.String(): 100500,
config.HFCockatrice.String(): 100500,
config.HFDomovoi.String(): 100500,
}
cfg.P2PSigExtensions = true
})
@ -148,15 +163,31 @@ func TestManagement_GenesisNativeState(t *testing.T) {
})
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) {
const cockatriceHeight = 3
const (
cockatriceHeight = 3
domovoiHeight = 5
)
mgmt := newCustomManagementClient(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): cockatriceHeight,
config.HFDomovoi.String(): domovoiHeight,
}
cfg.P2PSigExtensions = true
})
@ -169,6 +200,8 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
var expected []state.NotificationEvent
for _, name := range nativenames.All {
switch name {
case nativenames.Notary:
continue
case nativenames.Gas:
expected = append(expected, state.NotificationEvent{
ScriptHash: nativehashes.GasToken,
@ -200,7 +233,7 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
}
require.Equal(t, expected, aer[0].Events)
// Generate some blocks and check Update notifications.
// Generate some blocks and check Update notifications for Cockatrice hardfork.
cockatriceBlock := mgmt.GenerateNewBlocks(t, cockatriceHeight)[cockatriceHeight-1]
aer, err = mgmt.Chain.GetAppExecResults(cockatriceBlock.Hash(), trigger.OnPersist)
require.NoError(t, err)
@ -216,15 +249,42 @@ func TestManagement_NativeDeployUpdateNotifications(t *testing.T) {
})
}
require.Equal(t, expected, aer[0].Events)
// Generate some blocks and check Deploy notifications for 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) {
const cockatriceHeight = 3
const (
cockatriceHeight = 3
domovoiHeight = 6
)
c := newCustomManagementClient(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): cockatriceHeight,
config.HFDomovoi.String(): domovoiHeight,
}
cfg.P2PSigExtensions = true
})
@ -235,7 +295,12 @@ func TestManagement_NativeUpdate(t *testing.T) {
for _, name := range nativenames.All {
h := state.CreateNativeContractHash(name)
cs := c.Chain.GetContractState(h)
if name == nativenames.Notary {
require.Nil(t, cs, name)
continue
} else {
require.NotNil(t, cs, name)
}
jBytes, err := ojson.Marshal(cs)
require.NoError(t, err, name)
require.Equal(t, defaultCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
@ -247,16 +312,50 @@ func TestManagement_NativeUpdate(t *testing.T) {
for _, name := range nativenames.All {
h := state.CreateNativeContractHash(name)
cs := c.Chain.GetContractState(h)
if name == nativenames.Notary {
require.Nil(t, cs, name)
continue
} else {
require.NotNil(t, cs, name)
}
var actual = cs
if name == nativenames.Neo || name == nativenames.CryptoLib {
// A tiny hack to reuse cockatriceCSS map in the check below.
require.Equal(t, uint16(1), cs.UpdateCounter, name)
cs.UpdateCounter--
cp := *cs
actual = &cp // avoid Management cache corruption.
actual.UpdateCounter--
}
jBytes, err := ojson.Marshal(cs)
jBytes, err := ojson.Marshal(actual)
require.NoError(t, err, name)
require.Equal(t, cockatriceCSS[name], string(jBytes), fmt.Errorf("contract %s state mismatch", name))
}
// Add some blocks up to the 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) {
@ -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
// depends on defaultCSS and cockatriceCSS.
func TestBlockchain_GetNatives(t *testing.T) {
const cockatriceHeight = 3
const (
cockatriceHeight = 3
domovoiHeight = 6
)
bc, acc := chain.NewSingleWithCustomConfig(t, func(cfg *config.Blockchain) {
cfg.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): 0,
config.HFBasilisk.String(): 0,
config.HFCockatrice.String(): cockatriceHeight,
config.HFDomovoi.String(): domovoiHeight,
}
cfg.P2PSigExtensions = true
})
@ -305,7 +408,7 @@ func TestBlockchain_GetNatives(t *testing.T) {
// Check genesis-based native contract states.
natives := bc.GetNatives()
require.Equal(t, len(nativenames.All), len(natives))
require.Equal(t, len(nativenames.All)-1, len(natives)) // Notary is deployed starting from D hardfork.
for _, cs := range natives {
csFull := state.Contract{
ContractBase: cs.ContractBase,
@ -316,10 +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))
}
// Check native state after update.
// Check native state after Cockatrice.
e.GenerateNewBlocks(t, cockatriceHeight)
natives = bc.GetNatives()
require.Equal(t, len(nativenames.All), len(natives))
require.Equal(t, len(nativenames.All)-1, len(natives)) // Notary is deployed starting from D hardfork.
for _, cs := range natives {
csFull := state.Contract{
ContractBase: cs.ContractBase,
@ -329,6 +432,20 @@ func TestBlockchain_GetNatives(t *testing.T) {
require.NoError(t, err, cs.Manifest.Name)
require.Equal(t, cockatriceCSS[cs.Manifest.Name], string(jBytes), fmt.Errorf("contract %s state mismatch", cs.Manifest.Name))
}
// Check native state after 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) {

View file

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

View file

@ -63,9 +63,6 @@ var (
type Policy struct {
interop.ContractMD
NEO *NEO
// p2pSigExtensionsEnabled defines whether the P2P signature extensions logic is relevant.
p2pSigExtensionsEnabled bool
}
type PolicyCache struct {
@ -100,11 +97,8 @@ func copyPolicyCache(src, dst *PolicyCache) {
}
// newPolicy returns Policy native contract.
func newPolicy(p2pSigExtensionsEnabled bool) *Policy {
p := &Policy{
ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID),
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
}
func newPolicy() *Policy {
p := &Policy{ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID)}
defer p.BuildHFSpecificMD(p.ActiveIn())
desc := newDescriptor("getFeePerByte", smartcontract.IntegerType)
@ -136,13 +130,24 @@ func newPolicy(p2pSigExtensionsEnabled bool) *Policy {
desc = newDescriptor("getAttributeFee", smartcontract.IntegerType,
manifest.NewParameter("attributeType", smartcontract.IntegerType))
md = newMethodAndPrice(p.getAttributeFee, 1<<15, callflag.ReadStates)
md = newMethodAndPrice(p.getAttributeFeeV0, 1<<15, callflag.ReadStates, config.HFDefault, transaction.NotaryAssistedActivation)
p.AddMethod(md, desc)
desc = newDescriptor("getAttributeFee", smartcontract.IntegerType,
manifest.NewParameter("attributeType", smartcontract.IntegerType))
md = newMethodAndPrice(p.getAttributeFeeV1, 1<<15, callflag.ReadStates, transaction.NotaryAssistedActivation)
p.AddMethod(md, desc)
desc = newDescriptor("setAttributeFee", smartcontract.VoidType,
manifest.NewParameter("attributeType", smartcontract.IntegerType),
manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(p.setAttributeFee, 1<<15, callflag.States)
md = newMethodAndPrice(p.setAttributeFeeV0, 1<<15, callflag.States, config.HFDefault, transaction.NotaryAssistedActivation)
p.AddMethod(md, desc)
desc = newDescriptor("setAttributeFee", smartcontract.VoidType,
manifest.NewParameter("attributeType", smartcontract.IntegerType),
manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(p.setAttributeFeeV1, 1<<15, callflag.States, transaction.NotaryAssistedActivation)
p.AddMethod(md, desc)
desc = newDescriptor("setFeePerByte", smartcontract.VoidType,
@ -170,10 +175,7 @@ func (p *Policy) Metadata() *interop.ContractMD {
// Initialize initializes Policy native contract and implements the Contract interface.
func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
if hf != p.ActiveIn() {
return nil
}
if hf == p.ActiveIn() {
setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte)
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
@ -186,11 +188,14 @@ func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *int
attributeFee: map[transaction.AttrType]uint32{},
blockedAccounts: make([]util.Uint160, 0),
}
if p.p2pSigExtensionsEnabled {
ic.DAO.SetCache(p.ID, cache)
}
if hf != nil && *hf == transaction.NotaryAssistedActivation {
setIntWithKey(p.ID, ic.DAO, []byte{attributeFeePrefix, byte(transaction.NotaryAssistedT)}, defaultNotaryAssistedFee)
cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
cache.attributeFee[transaction.NotaryAssistedT] = defaultNotaryAssistedFee
}
ic.DAO.SetCache(p.ID, cache)
return nil
}
@ -363,9 +368,18 @@ func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) sta
return stackitem.Null{}
}
func (p *Policy) getAttributeFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
func (p *Policy) getAttributeFeeV0(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return p.getAttributeFeeGeneric(ic, args, false)
}
func (p *Policy) getAttributeFeeV1(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return p.getAttributeFeeGeneric(ic, args, true)
}
func (p *Policy) getAttributeFeeGeneric(ic *interop.Context, args []stackitem.Item, allowNotaryAssisted bool) stackitem.Item {
t := transaction.AttrType(toUint8(args[0]))
if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) {
if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) ||
(!allowNotaryAssisted && t == transaction.NotaryAssistedT) {
panic(fmt.Errorf("invalid attribute type: %d", t))
}
return stackitem.NewBigInteger(big.NewInt(p.GetAttributeFeeInternal(ic.DAO, t)))
@ -382,10 +396,19 @@ func (p *Policy) GetAttributeFeeInternal(d *dao.Simple, t transaction.AttrType)
return int64(v)
}
func (p *Policy) setAttributeFee(ic *interop.Context, args []stackitem.Item) stackitem.Item {
func (p *Policy) setAttributeFeeV0(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return p.setAttributeFeeGeneric(ic, args, false)
}
func (p *Policy) setAttributeFeeV1(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return p.setAttributeFeeGeneric(ic, args, true)
}
func (p *Policy) setAttributeFeeGeneric(ic *interop.Context, args []stackitem.Item, allowNotaryAssisted bool) stackitem.Item {
t := transaction.AttrType(toUint8(args[0]))
value := toUint32(args[1])
if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) {
if !transaction.IsValidAttrType(ic.Chain.GetConfig().ReservedAttributes, t) ||
(!allowNotaryAssisted && t == transaction.NotaryAssistedT) {
panic(fmt.Errorf("invalid attribute type: %d", t))
}
if value > maxAttributeFee {

View file

@ -4,9 +4,12 @@ import (
"fmt"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/stretchr/testify/require"
@ -65,3 +68,51 @@ func TestPolicy_BlockedAccounts(t *testing.T) {
require.Equal(t, expectedErr, err.Error())
})
}
func TestPolicy_GetNotaryFeePerKey(t *testing.T) {
const 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
import (
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// NotaryAssistedActivation stores the hardfork of NotaryAssisted transaction attribute
// activation.
var NotaryAssistedActivation = config.HFDomovoi
// NotaryAssisted represents attribute for notary service transactions.
type NotaryAssisted struct {
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/mempoolevent"
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
@ -65,7 +66,6 @@ type (
GetMaxVerificationGAS() int64
GetMemPool() *mempool.Pool
GetNotaryBalance(acc util.Uint160) *big.Int
GetNotaryContractScriptHash() util.Uint160
GetNotaryDepositExpiration(acc util.Uint160) uint32
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
HasBlock(util.Uint256) bool
@ -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 {
return fmt.Errorf("bad P2PNotaryRequest payload witness: %w", err)
}
notaryHash := s.chain.GetNotaryContractScriptHash()
notaryHash := nativehashes.Notary
if r.FallbackTransaction.Sender() != notaryHash {
return fmt.Errorf("P2PNotary contract should be a sender of the fallback transaction, got %s", address.Uint160ToString(r.FallbackTransaction.Sender()))
}

View file

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

View file

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

View file

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

View file

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

View file

@ -465,6 +465,10 @@ func TestClientNEOContract(t *testing.T) {
func TestClientNotary(t *testing.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{})
require.NoError(t, err)
t.Cleanup(c.Close)
@ -2446,7 +2450,10 @@ func TestClient_GetVersion_Hardforks(t *testing.T) {
v, err := c.GetVersion()
require.NoError(t, err)
expected := map[config.Hardfork]uint32{
config.HFAspidochelone: 25,
config.HFAspidochelone: 0,
config.HFBasilisk: 0,
config.HFCockatrice: 0,
config.HFDomovoi: 0,
}
require.InDeltaMapValues(t, expected, v.Protocol.Hardforks, 0)
}

View file

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

View file

@ -54,6 +54,7 @@ func getUnitTestChain(t testing.TB, enableOracle bool, enableNotary bool, disabl
Password: "one",
}
}
cfg.ProtocolConfiguration.Hardforks = nil
})
}
func getUnitTestChainWithCustomConfig(t testing.TB, enableOracle bool, enableNotary bool, customCfg func(configuration *config.Config)) (*core.Blockchain, OracleHandler, config.Config, *zap.Logger) {

View file

@ -31,6 +31,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
@ -74,22 +75,22 @@ type rpcTestCase struct {
}
const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4"
const testContractHash = "565cff9508ebc75aadd7fe59f38dac610ab6093c"
const deploymentTxHash = "a14390941cc3a1d87393eff720a722e9cd350bd6ed233c5fe2001326c80eb68e"
const testContractHash = "449fe8fbd4523072f5e3a4dfa17a494c119d4c08"
const deploymentTxHash = "af170742f0f8a2bc064bdbdb2faa2b517e3df833d4d047da8a946c0b8d581b06"
const (
verifyContractHash = "06ed5314c2e4cb103029a60b86d46afa2fb8f67c"
verifyContractAVM = "VwIAQS1RCDBwDBTunqIsJ+NL0BSPxBCOCPdOj1BIskrZMCQE2zBxaBPOStkoJATbKGlK2SgkBNsol0A="
verifyWithArgsContractHash = "4dc916254efd2947c93b11207e8ffc0bb56161c5"
nnsContractHash = "892429fcd47c30f8451781acc627e8b20e0d64f3"
verifyWithArgsContractHash = "6261b3bf753bdc3d24c1327a23fd891e1c8a7ccd"
nnsContractHash = "ebe47d5143bb8726b87b02efb5cd98e21174fd38"
nnsToken1ID = "6e656f2e636f6d"
nfsoContractHash = "730ebe719ab8e3b69d11dafc95cdb9bf409db179"
nfsoContractHash = "2f5c1826bb4da1c764a8871427e4044cf3e82dbd"
nfsoToken1ID = "7e244ffd6aa85fb1579d2ed22e9b761ab62e3486"
storageContractHash = "ebc0c16a76c808cd4dde6bcc063f09e45e331ec7"
faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60"
faultedTxBlock uint32 = 23
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
block20StateRootLE = "858c873539d6d24a70f2be13f9dafc61aef2b63c2aa16bb440676de6e44e3cf1"
block20StateRootLE = "b49a35fd3a749fc2f7f4e5fe1f288ef2b6188416f65fe5b691892e8209092082"
)
var (
@ -1381,7 +1382,7 @@ var rpcTestCases = map[string][]rpcTestCase{
script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
return &result.Invoke{
State: "HALT",
GasConsumed: 31922970,
GasConsumed: 31922730,
Script: script,
Stack: []stackitem.Item{stackitem.Make(true)},
Notifications: []state.NotificationEvent{{
@ -1411,7 +1412,7 @@ var rpcTestCases = map[string][]rpcTestCase{
chg := []dboper.Operation{{
State: "Changed",
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0xb},
Value: []byte{0x54, 0xb2, 0xd2, 0xa3, 0x51, 0x79, 0x12},
Value: []byte{0x06, 0x44, 0xda, 0xa3, 0x51, 0x79, 0x12},
}, {
State: "Added",
Key: []byte{0xfb, 0xff, 0xff, 0xff, 0x14, 0xd6, 0x24, 0x87, 0x12, 0xff, 0x97, 0x22, 0x80, 0xa0, 0xae, 0xf5, 0x24, 0x1c, 0x96, 0x4d, 0x63, 0x78, 0x29, 0xcd, 0xb},
@ -1423,7 +1424,7 @@ var rpcTestCases = map[string][]rpcTestCase{
}, {
State: "Changed",
Key: []byte{0xfa, 0xff, 0xff, 0xff, 0x14, 0xee, 0x9e, 0xa2, 0x2c, 0x27, 0xe3, 0x4b, 0xd0, 0x14, 0x8f, 0xc4, 0x10, 0x8e, 0x8, 0xf7, 0x4e, 0x8f, 0x50, 0x48, 0xb2},
Value: []byte{0x41, 0x01, 0x21, 0x05, 0x0c, 0x76, 0x4f, 0xdf, 0x08},
Value: []byte{0x41, 0x01, 0x21, 0x05, 0xf6, 0x64, 0x58, 0xdf, 0x08},
}}
// Can be returned in any order.
assert.ElementsMatch(t, chg, res.Diagnostics.Changes)
@ -1439,7 +1440,7 @@ var rpcTestCases = map[string][]rpcTestCase{
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
return &result.Invoke{
State: "HALT",
GasConsumed: 13970250,
GasConsumed: 13969530,
Script: script,
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
Notifications: []state.NotificationEvent{},
@ -1532,7 +1533,7 @@ var rpcTestCases = map[string][]rpcTestCase{
script = append(script, 0x41, 0x62, 0x7d, 0x5b, 0x52)
return &result.Invoke{
State: "HALT",
GasConsumed: 31922970,
GasConsumed: 31922730,
Script: script,
Stack: []stackitem.Item{stackitem.Make(true)},
Notifications: []state.NotificationEvent{{
@ -1558,7 +1559,7 @@ var rpcTestCases = map[string][]rpcTestCase{
cryptoHash, _ := e.chain.GetNativeContractScriptHash(nativenames.CryptoLib)
return &result.Invoke{
State: "HALT",
GasConsumed: 13970250,
GasConsumed: 13969530,
Script: script,
Stack: []stackitem.Item{stackitem.Make("1.2.3.4")},
Notifications: []state.NotificationEvent{},
@ -2495,7 +2496,7 @@ func createValidNotaryRequest(chain *core.Blockchain, sender *keys.PrivateKey, n
{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: mainTx.Hash()}},
{Type: transaction.NotaryAssistedT, Value: &transaction.NotaryAssisted{NKeys: 0}},
},
Signers: []transaction.Signer{{Account: chain.GetNotaryContractScriptHash()}, {Account: sender.GetScriptHash()}},
Signers: []transaction.Signer{{Account: nativehashes.Notary}, {Account: sender.GetScriptHash()}},
Scripts: []transaction.Witness{
{InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), VerificationScript: []byte{}},
},
@ -3260,7 +3261,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
t.Run("contract-based verification with parameters", func(t *testing.T) {
verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash)
require.NoError(t, err)
checkContract(t, verAcc, []byte{}, 244130) // No C# match, but we believe it's OK and it differs from the one above.
checkContract(t, verAcc, []byte{}, 244010) // No C# match, but we believe it's OK and it differs from the one above.
})
t.Run("contract-based verification with invocation script", func(t *testing.T) {
verAcc, err := util.Uint160DecodeStringLE(verifyWithArgsContractHash)
@ -3270,7 +3271,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
emit.Int(invocWriter.BinWriter, 5)
emit.String(invocWriter.BinWriter, "")
invocScript := invocWriter.Bytes()
checkContract(t, verAcc, invocScript, 146960) // No C# match, but we believe it's OK and it has a specific invocation script overriding anything server-side.
checkContract(t, verAcc, invocScript, 146840) // No C# match, but we believe it's OK and it has a specific invocation script overriding anything server-side.
})
t.Run("execution limit, ok", func(t *testing.T) {
// 1_4000_0000 GAS with the default 1.5 allowed by Policy
@ -3575,7 +3576,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc any) {
},
{
Asset: e.chain.UtilityTokenHash(),
Amount: "37106285100",
Amount: "37106870550",
LastUpdated: 23,
Decimals: 8,
Name: "GasToken",
@ -3924,7 +3925,7 @@ func checkNep17TransfersAux(t *testing.T, e *executor, acc any, sent, rcvd []int
{
Timestamp: blockDepositGAS.Timestamp,
Asset: e.chain.UtilityTokenHash(),
Address: address.Uint160ToString(e.chain.GetNotaryContractScriptHash()),
Address: address.Uint160ToString(nativehashes.Notary),
Amount: "1000000000",
Index: 8,
NotifyIndex: 0,

Binary file not shown.