From bf3415e2bc7c240bf42ef104a55679528b4e22db Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 27 Apr 2022 22:57:10 +0300 Subject: [PATCH 1/2] emit: introduce CheckSig function Which allows to create verification scripts without keys.PublicKey which is convenient in some cases where we already have serialized key and don't want to waste time decompressing it. --- pkg/crypto/keys/publickey.go | 4 +--- pkg/vm/emit/emit.go | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 56c3f4a48..b1e3748c2 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -12,7 +12,6 @@ import ( "github.com/btcsuite/btcd/btcec" lru "github.com/hashicorp/golang-lru" - "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" @@ -319,8 +318,7 @@ func (p *PublicKey) GetVerificationScript() []byte { buf.WriteB(0xAC) // CHECKSIG return buf.Bytes() } - emit.Bytes(buf.BinWriter, b) - emit.Syscall(buf.BinWriter, interopnames.SystemCryptoCheckSig) + emit.CheckSig(buf.BinWriter, b) return buf.Bytes() } diff --git a/pkg/vm/emit/emit.go b/pkg/vm/emit/emit.go index bff6807fd..1a8450f3f 100644 --- a/pkg/vm/emit/emit.go +++ b/pkg/vm/emit/emit.go @@ -219,6 +219,14 @@ func AppCall(w *io.BinWriter, scriptHash util.Uint160, operation string, f callf AppCallNoArgs(w, scriptHash, operation, f) } +// CheckSig emits a single-key verification script using given []bytes as a key. +// It does not check for key correctness, so you can get an invalid script if the +// data passed is not really a public key. +func CheckSig(w *io.BinWriter, key []byte) { + Bytes(w, key) + Syscall(w, interopnames.SystemCryptoCheckSig) +} + func isInstructionJmp(op opcode.Opcode) bool { return opcode.JMP <= op && op <= opcode.CALLL || op == opcode.ENDTRYL } From a8607e43b1fad4e618396ce477071963bd209209 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 27 Apr 2022 22:58:52 +0300 Subject: [PATCH 2/2] native: check candidates againt Policy blocked list Follow neo-project/neo#2695 and neo-project/neo#2707. --- pkg/core/native/contract.go | 10 ++++----- pkg/core/native/native_neo.go | 10 +++++++-- pkg/core/native/native_test/neo_test.go | 29 ++++++++++++++----------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index 95faa2467..13f8ae0d9 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -77,19 +77,17 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts { gas := newGAS(int64(cfg.InitialGASSupply), cfg.P2PSigExtensions) neo := newNEO() + policy := newPolicy() neo.GAS = gas + neo.Policy = policy gas.NEO = neo mgmt.NEO = neo + policy.NEO = neo cs.GAS = gas cs.NEO = neo - cs.Contracts = append(cs.Contracts, neo) - cs.Contracts = append(cs.Contracts, gas) - - policy := newPolicy() - policy.NEO = neo cs.Policy = policy - cs.Contracts = append(cs.Contracts, policy) + cs.Contracts = append(cs.Contracts, neo, gas, policy) desig := newDesignate(cfg.P2PSigExtensions) desig.NEO = neo diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index c3ee86eac..715c7f140 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -20,17 +20,20 @@ import ( "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/encoding/bigint" + "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // NEO represents NEO native contract. type NEO struct { nep17TokenNative - GAS *GAS + GAS *GAS + Policy *Policy // gasPerBlock represents current value of generated gas per block. // It is append-only and doesn't need to be copied when used. @@ -822,11 +825,14 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d *dao.Simple, value *bi func (n *NEO) getCandidates(d *dao.Simple, sortByKey bool) ([]keyWithVotes, error) { arr := make([]keyWithVotes, 0) + buf := io.NewBufBinWriter() d.Seek(n.ID, storage.SeekRange{Prefix: []byte{prefixCandidate}}, func(k, v []byte) bool { c := new(candidate).FromBytes(v) - if c.Registered { + emit.CheckSig(buf.BinWriter, k) + if c.Registered && !n.Policy.IsBlockedInternal(d, hash.Hash160(buf.Bytes())) { arr = append(arr, keyWithVotes{Key: string(k), Votes: &c.Votes}) } + buf.Reset() return true }) diff --git a/pkg/core/native/native_test/neo_test.go b/pkg/core/native/native_test/neo_test.go index d31dd4bab..3ade90aa4 100644 --- a/pkg/core/native/native_test/neo_test.go +++ b/pkg/core/native/native_test/neo_test.go @@ -49,6 +49,7 @@ func TestNEO_RegisterPrice(t *testing.T) { func TestNEO_Vote(t *testing.T) { neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000) neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator) + policyInvoker := neoCommitteeInvoker.CommitteeInvoker(neoCommitteeInvoker.NativeHash(t, nativenames.Policy)) e := neoCommitteeInvoker.Executor cfg := e.Chain.GetConfig() @@ -71,23 +72,23 @@ func TestNEO_Vote(t *testing.T) { // voters vote for candidates. The aim of this test is to check that voting // reward is proportional to the NEO balance. - voters := make([]neotest.Signer, committeeSize) + voters := make([]neotest.Signer, committeeSize+1) // referenceAccounts perform the same actions as voters except voting, i.e. we // will transfer the same amount of NEO to referenceAccounts and see how much // GAS they receive for NEO ownership. We need these values to be able to define // how much GAS voters receive for NEO ownership. - referenceAccounts := make([]neotest.Signer, committeeSize) - candidates := make([]neotest.Signer, committeeSize) - for i := 0; i < committeeSize; i++ { + referenceAccounts := make([]neotest.Signer, committeeSize+1) + candidates := make([]neotest.Signer, committeeSize+1) + for i := 0; i < committeeSize+1; i++ { voters[i] = e.NewAccount(t, 10_0000_0000) referenceAccounts[i] = e.NewAccount(t, 10_0000_0000) candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration } txes := make([]*transaction.Transaction, 0, committeeSize*4-2) - for i := 0; i < committeeSize; i++ { - transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize-i)*1000000, nil) + for i := 0; i < committeeSize+1; i++ { + transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil) txes = append(txes, transferTx) - transferTx = neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), referenceAccounts[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize-i)*1000000, nil) + transferTx = neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), referenceAccounts[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil) txes = append(txes, transferTx) if i > 0 { registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes()) @@ -96,6 +97,7 @@ func TestNEO_Vote(t *testing.T) { txes = append(txes, voteTx) } } + txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().PrivateKey().PublicKey().GetScriptHash())) neoValidatorsInvoker.AddNewBlock(t, txes...) for _, tx := range txes { e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values @@ -137,12 +139,12 @@ func TestNEO_Vote(t *testing.T) { require.EqualValues(t, sortedCandidates, pubs) t.Run("check voter rewards", func(t *testing.T) { - gasBalance := make([]*big.Int, len(voters)) - referenceGASBalance := make([]*big.Int, len(referenceAccounts)) - neoBalance := make([]*big.Int, len(voters)) - txes = make([]*transaction.Transaction, 0, len(voters)) + gasBalance := make([]*big.Int, len(voters)-1) + referenceGASBalance := make([]*big.Int, len(referenceAccounts)-1) + neoBalance := make([]*big.Int, len(voters)-1) + txes = make([]*transaction.Transaction, 0, len(voters)-1) var refTxFee int64 - for i := range voters { + for i := range voters[:len(voters)-1] { h := voters[i].ScriptHash() refH := referenceAccounts[i].ScriptHash() gasBalance[i] = e.Chain.GetUtilityTokenBalance(h) @@ -169,7 +171,7 @@ func TestNEO_Vote(t *testing.T) { // GAS increase consists of 2 parts: NEO holding + voting for committee nodes. // Here we check that 2-nd part exists and is proportional to the amount of NEO given. - for i := range voters { + for i := range voters[:len(voters)-1] { newGAS := e.Chain.GetUtilityTokenBalance(voters[i].ScriptHash()) newGAS.Sub(newGAS, gasBalance[i]) gasForHold := referenceGASBalance[i] @@ -197,6 +199,7 @@ func TestNEO_Vote(t *testing.T) { require.NoError(t, err) for i := range pubs { require.NotEqual(t, candidates[0], pubs[i]) + require.NotEqual(t, candidates[len(candidates)-1], pubs[i]) } }