diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 5183a5a2d..b7548d6d2 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -293,7 +293,7 @@ func TestVerifyTx(t *testing.T) { require.Equal(t, 1, len(aer)) require.Equal(t, aer[0].VMState, vm.HaltState) - res, err := invokeContractMethod(bc, 100000000, bc.contracts.Policy.Hash, "blockAccount", accs[1].PrivateKey().GetScriptHash().BytesBE()) + res, err := invokeContractMethodGeneric(bc, 100000000, bc.contracts.Policy.Hash, "blockAccount", true, accs[1].PrivateKey().GetScriptHash().BytesBE()) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(true)) diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index efccd7c3a..ad76f3fa8 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -63,6 +63,7 @@ func NewContracts(p2pSigExtensionsEnabled bool) *Contracts { neo := newNEO() neo.GAS = gas gas.NEO = neo + mgmt.NEO = neo cs.GAS = gas cs.NEO = neo @@ -70,6 +71,7 @@ func NewContracts(p2pSigExtensionsEnabled bool) *Contracts { cs.Contracts = append(cs.Contracts, gas) policy := newPolicy() + policy.NEO = neo cs.Policy = policy cs.Contracts = append(cs.Contracts, policy) @@ -88,6 +90,7 @@ func NewContracts(p2pSigExtensionsEnabled bool) *Contracts { if p2pSigExtensionsEnabled { notary := newNotary() notary.GAS = gas + notary.NEO = neo notary.Desig = desig cs.Notary = notary cs.Contracts = append(cs.Contracts, notary) diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 691b38029..1face8fc9 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -27,6 +27,7 @@ import ( // Management is contract-managing native contract. type Management struct { interop.ContractMD + NEO *NEO mtx sync.RWMutex contracts map[util.Uint160]*state.Contract @@ -367,14 +368,10 @@ func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackit if value < 0 { panic(fmt.Errorf("MinimumDeploymentFee cannot be negative")) } - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } - if !ok { + if !m.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } - err = setUint32WithKey(m.ContractID, ic.DAO, keyMinimumDeploymentFee, value) + err := setUint32WithKey(m.ContractID, ic.DAO, keyMinimumDeploymentFee, value) if err != nil { panic(err) } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 303be0c24..43ebcc201 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -462,6 +462,14 @@ func (n *NEO) GetCommitteeAddress() util.Uint160 { return n.committeeHash.Load().(util.Uint160) } +func (n *NEO) checkCommittee(ic *interop.Context) bool { + ok, err := runtime.CheckHashedWitness(ic, n.GetCommitteeAddress()) + if err != nil { + panic(err) + } + return ok +} + func (n *NEO) setGASPerBlock(ic *interop.Context, args []stackitem.Item) stackitem.Item { gas := toBigInt(args[0]) ok, err := n.SetGASPerBlock(ic, ic.Block.Index+1, gas) diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index 7d3fa974f..62510423d 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -27,6 +27,7 @@ import ( type Notary struct { interop.ContractMD GAS *GAS + NEO *NEO Desig *Designate lock sync.RWMutex @@ -387,16 +388,12 @@ func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem if value > transaction.MaxValidUntilBlockIncrement/2 || value < uint32(ic.Chain.GetConfig().ValidatorsCount) { panic(fmt.Errorf("MaxNotValidBeforeDelta cannot be more than %d or less than %d", transaction.MaxValidUntilBlockIncrement/2, ic.Chain.GetConfig().ValidatorsCount)) } - ok, err := checkValidators(ic) - if err != nil { - panic(fmt.Errorf("failed to check committee signature: %w", err)) - } - if !ok { + if !n.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } n.lock.Lock() defer n.lock.Unlock() - err = setUint32WithKey(n.ContractID, ic.DAO, maxNotValidBeforeDeltaKey, value) + err := setUint32WithKey(n.ContractID, ic.DAO, maxNotValidBeforeDeltaKey, value) if err != nil { panic(fmt.Errorf("failed to put value into the storage: %w", err)) } diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index 4950ae8dc..98fc26c1d 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -62,6 +62,7 @@ var ( // Policy represents Policy native contract. type Policy struct { interop.ContractMD + NEO *NEO lock sync.RWMutex // isValid defies whether cached values were changed during the current // consensus iteration. If false, these values will be updated after @@ -307,15 +308,12 @@ func (p *Policy) setExecFeeFactor(ic *interop.Context, args []stackitem.Item) st if value <= 0 || maxExecFeeFactor < value { panic(fmt.Errorf("ExecFeeFactor must be between 0 and %d", maxExecFeeFactor)) } - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } else if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } p.lock.Lock() defer p.lock.Unlock() - err = setUint32WithKey(p.ContractID, ic.DAO, execFeeFactorKey, uint32(value)) + err := setUint32WithKey(p.ContractID, ic.DAO, execFeeFactorKey, uint32(value)) if err != nil { panic(err) } @@ -366,15 +364,12 @@ func (p *Policy) setStoragePrice(ic *interop.Context, args []stackitem.Item) sta if value <= 0 || maxStoragePrice < value { panic(fmt.Errorf("StoragePrice must be between 0 and %d", maxStoragePrice)) } - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } else if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } p.lock.Lock() defer p.lock.Unlock() - err = setUint32WithKey(p.ContractID, ic.DAO, storagePriceKey, uint32(value)) + err := setUint32WithKey(p.ContractID, ic.DAO, storagePriceKey, uint32(value)) if err != nil { panic(err) } @@ -389,16 +384,12 @@ func (p *Policy) setMaxTransactionsPerBlock(ic *interop.Context, args []stackite if value > block.MaxTransactionsPerBlock { panic(fmt.Errorf("MaxTransactionsPerBlock cannot exceed the maximum allowed transactions per block = %d", block.MaxTransactionsPerBlock)) } - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } - if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } p.lock.Lock() defer p.lock.Unlock() - err = setUint32WithKey(p.ContractID, ic.DAO, maxTransactionsPerBlockKey, value) + err := setUint32WithKey(p.ContractID, ic.DAO, maxTransactionsPerBlockKey, value) if err != nil { panic(err) } @@ -412,16 +403,12 @@ func (p *Policy) setMaxBlockSize(ic *interop.Context, args []stackitem.Item) sta if value > payload.MaxSize { panic(fmt.Errorf("MaxBlockSize cannot be more than the maximum payload size = %d", payload.MaxSize)) } - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } - if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } p.lock.Lock() defer p.lock.Unlock() - err = setUint32WithKey(p.ContractID, ic.DAO, maxBlockSizeKey, value) + err := setUint32WithKey(p.ContractID, ic.DAO, maxBlockSizeKey, value) if err != nil { panic(err) } @@ -435,16 +422,12 @@ func (p *Policy) setFeePerByte(ic *interop.Context, args []stackitem.Item) stack if value < 0 || value > maxFeePerByte { panic(fmt.Errorf("FeePerByte shouldn't be negative or greater than %d", maxFeePerByte)) } - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } - if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } p.lock.Lock() defer p.lock.Unlock() - err = setInt64WithKey(p.ContractID, ic.DAO, feePerByteKey, value) + err := setInt64WithKey(p.ContractID, ic.DAO, feePerByteKey, value) if err != nil { panic(err) } @@ -458,16 +441,12 @@ func (p *Policy) setMaxBlockSystemFee(ic *interop.Context, args []stackitem.Item if value <= minBlockSystemFee { panic(fmt.Errorf("MaxBlockSystemFee cannot be less then %d", minBlockSystemFee)) } - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } - if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } p.lock.Lock() defer p.lock.Unlock() - err = setInt64WithKey(p.ContractID, ic.DAO, maxBlockSystemFeeKey, value) + err := setInt64WithKey(p.ContractID, ic.DAO, maxBlockSystemFeeKey, value) if err != nil { panic(err) } @@ -478,11 +457,7 @@ func (p *Policy) setMaxBlockSystemFee(ic *interop.Context, args []stackitem.Item // blockAccount is Policy contract method and adds given account hash to the list // of blocked accounts. func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } - if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } hash := toUint160(args[0]) @@ -492,7 +467,7 @@ func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stacki key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...) p.lock.Lock() defer p.lock.Unlock() - err = ic.DAO.PutStorageItem(p.ContractID, key, &state.StorageItem{ + err := ic.DAO.PutStorageItem(p.ContractID, key, &state.StorageItem{ Value: []byte{0x01}, }) if err != nil { @@ -505,11 +480,7 @@ func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stacki // unblockAccount is Policy contract method and removes given account hash from // the list of blocked accounts. func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stackitem.Item { - ok, err := checkValidators(ic) - if err != nil { - panic(err) - } - if !ok { + if !p.NEO.checkCommittee(ic) { return stackitem.NewBool(false) } hash := toUint160(args[0]) @@ -519,7 +490,7 @@ func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stac key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...) p.lock.Lock() defer p.lock.Unlock() - err = ic.DAO.DeleteStorageItem(p.ContractID, key) + err := ic.DAO.DeleteStorageItem(p.ContractID, key) if err != nil { panic(err) } diff --git a/pkg/core/native/util.go b/pkg/core/native/util.go index d70d88799..fcd74c08b 100644 --- a/pkg/core/native/util.go +++ b/pkg/core/native/util.go @@ -4,8 +4,6 @@ import ( "encoding/binary" "github.com/nspcc-dev/neo-go/pkg/core/dao" - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/io" @@ -65,14 +63,6 @@ func setUint32WithKey(id int32, dao dao.DAO, key []byte, value uint32) error { return dao.PutStorageItem(id, key, si) } -func checkValidators(ic *interop.Context) (bool, error) { - prevBlock, err := ic.Chain.GetBlock(ic.Block.PrevHash) - if err != nil { - return false, err - } - return runtime.CheckHashedWitness(ic, prevBlock.NextConsensus) -} - // makeUint160Key creates a key from account script hash. func makeUint160Key(prefix byte, h util.Uint160) []byte { k := make([]byte, util.Uint160Size+1) diff --git a/pkg/core/native_notary_test.go b/pkg/core/native_notary_test.go index 75f6fd3e9..bb287851c 100644 --- a/pkg/core/native_notary_test.go +++ b/pkg/core/native_notary_test.go @@ -30,6 +30,8 @@ func TestNotaryContractPipeline(t *testing.T) { gasHash := chain.contracts.GAS.Hash depositLock := 100 + transferFundsToCommittee(t, chain) + // check Notary contract has no GAS on the account checkBalanceOf(t, chain, notaryHash, 0) @@ -329,6 +331,8 @@ func TestMaxNotValidBeforeDelta(t *testing.T) { defer chain.Close() notaryHash := chain.contracts.Notary.Hash + transferFundsToCommittee(t, chain) + t.Run("get, internal method", func(t *testing.T) { n := chain.contracts.Notary.GetMaxNotValidBeforeDelta(chain.dao) require.Equal(t, 140, int(n)) @@ -342,7 +346,7 @@ func TestMaxNotValidBeforeDelta(t *testing.T) { }) t.Run("set", func(t *testing.T) { - res, err := invokeContractMethod(chain, 100000000, notaryHash, "setMaxNotValidBeforeDelta", bigint.ToBytes(big.NewInt(150))) + res, err := invokeContractMethodGeneric(chain, 100000000, notaryHash, "setMaxNotValidBeforeDelta", true, bigint.ToBytes(big.NewInt(150))) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(true)) n := chain.contracts.Notary.GetMaxNotValidBeforeDelta(chain.dao) @@ -350,13 +354,13 @@ func TestMaxNotValidBeforeDelta(t *testing.T) { }) t.Run("set, too big value", func(t *testing.T) { - res, err := invokeContractMethod(chain, 100000000, notaryHash, "setMaxNotValidBeforeDelta", bigint.ToBytes(big.NewInt(transaction.MaxValidUntilBlockIncrement/2+1))) + res, err := invokeContractMethodGeneric(chain, 100000000, notaryHash, "setMaxNotValidBeforeDelta", true, bigint.ToBytes(big.NewInt(transaction.MaxValidUntilBlockIncrement/2+1))) require.NoError(t, err) checkFAULTState(t, res) }) t.Run("set, too small value", func(t *testing.T) { - res, err := invokeContractMethod(chain, 100000000, notaryHash, "setMaxNotValidBeforeDelta", bigint.ToBytes(big.NewInt(int64(chain.GetConfig().ValidatorsCount-1)))) + res, err := invokeContractMethodGeneric(chain, 100000000, notaryHash, "setMaxNotValidBeforeDelta", true, bigint.ToBytes(big.NewInt(int64(chain.GetConfig().ValidatorsCount-1)))) require.NoError(t, err) checkFAULTState(t, res) }) diff --git a/pkg/core/native_policy_test.go b/pkg/core/native_policy_test.go index 6e0684665..244eccc5c 100644 --- a/pkg/core/native_policy_test.go +++ b/pkg/core/native_policy_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/internal/random" + "github.com/nspcc-dev/neo-go/internal/testchain" "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/native" @@ -14,10 +15,16 @@ import ( "github.com/stretchr/testify/require" ) +func transferFundsToCommittee(t *testing.T, chain *Blockchain) { + transferTokenFromMultisigAccount(t, chain, testchain.CommitteeScriptHash(), + chain.contracts.GAS.Hash, 100_00000000) +} + func testGetSet(t *testing.T, chain *Blockchain, hash util.Uint160, name string, defaultValue, minValue, maxValue int64) { getName := "get" + name setName := "set" + name + transferFundsToCommittee(t, chain) t.Run("set, not signed by committee", func(t *testing.T) { signer, err := wallet.NewAccount() require.NoError(t, err) @@ -33,14 +40,14 @@ func testGetSet(t *testing.T, chain *Blockchain, hash util.Uint160, name string, }) t.Run("set, too small value", func(t *testing.T) { - res, err := invokeContractMethod(chain, 100000000, hash, setName, minValue-1) + res, err := invokeContractMethodGeneric(chain, 100000000, hash, setName, true, minValue-1) require.NoError(t, err) checkFAULTState(t, res) }) if maxValue != 0 { t.Run("set, too large value", func(t *testing.T) { - res, err := invokeContractMethod(chain, 100000000, hash, setName, maxValue+1) + res, err := invokeContractMethodGeneric(chain, 100000000, hash, setName, true, maxValue+1) require.NoError(t, err) checkFAULTState(t, res) }) @@ -48,7 +55,7 @@ func testGetSet(t *testing.T, chain *Blockchain, hash util.Uint160, name string, t.Run("set, success", func(t *testing.T) { // Set and get in the same block. - txSet, err := prepareContractMethodInvoke(chain, 100000000, hash, setName, defaultValue+1) + txSet, err := prepareContractMethodInvokeGeneric(chain, 100000000, hash, setName, true, defaultValue+1) require.NoError(t, err) txGet1, err := prepareContractMethodInvoke(chain, 100000000, hash, getName) require.NoError(t, err) @@ -144,6 +151,9 @@ func TestBlockedAccounts(t *testing.T) { account := util.Uint160{1, 2, 3} policyHash := chain.contracts.Policy.Metadata().Hash + transferTokenFromMultisigAccount(t, chain, testchain.CommitteeScriptHash(), + chain.contracts.GAS.Hash, 100_00000000) + t.Run("isBlocked, internal method", func(t *testing.T) { isBlocked := chain.contracts.Policy.IsBlockedInternal(chain.dao, random.Uint160()) require.Equal(t, false, isBlocked) @@ -157,7 +167,7 @@ func TestBlockedAccounts(t *testing.T) { }) t.Run("block-unblock account", func(t *testing.T) { - res, err := invokeContractMethod(chain, 100000000, policyHash, "blockAccount", account.BytesBE()) + res, err := invokeContractMethodGeneric(chain, 100000000, policyHash, "blockAccount", true, account.BytesBE()) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(true)) @@ -165,7 +175,7 @@ func TestBlockedAccounts(t *testing.T) { require.Equal(t, isBlocked, true) require.NoError(t, chain.persist()) - res, err = invokeContractMethod(chain, 100000000, policyHash, "unblockAccount", account.BytesBE()) + res, err = invokeContractMethodGeneric(chain, 100000000, policyHash, "unblockAccount", true, account.BytesBE()) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(true)) @@ -176,25 +186,25 @@ func TestBlockedAccounts(t *testing.T) { t.Run("double-block", func(t *testing.T) { // block - res, err := invokeContractMethod(chain, 100000000, policyHash, "blockAccount", account.BytesBE()) + res, err := invokeContractMethodGeneric(chain, 100000000, policyHash, "blockAccount", true, account.BytesBE()) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(true)) require.NoError(t, chain.persist()) // double-block should fail - res, err = invokeContractMethod(chain, 100000000, policyHash, "blockAccount", account.BytesBE()) + res, err = invokeContractMethodGeneric(chain, 100000000, policyHash, "blockAccount", true, account.BytesBE()) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(false)) require.NoError(t, chain.persist()) // unblock - res, err = invokeContractMethod(chain, 100000000, policyHash, "unblockAccount", account.BytesBE()) + res, err = invokeContractMethodGeneric(chain, 100000000, policyHash, "unblockAccount", true, account.BytesBE()) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(true)) require.NoError(t, chain.persist()) // unblock the same account should fail as we don't have it blocked - res, err = invokeContractMethod(chain, 100000000, policyHash, "unblockAccount", account.BytesBE()) + res, err = invokeContractMethodGeneric(chain, 100000000, policyHash, "unblockAccount", true, account.BytesBE()) require.NoError(t, err) checkResult(t, res, stackitem.NewBool(false)) require.NoError(t, chain.persist())