diff --git a/cli/server/dump.go b/cli/server/dump.go index 303c7e903..ccf958087 100644 --- a/cli/server/dump.go +++ b/cli/server/dump.go @@ -25,42 +25,6 @@ type storageOp struct { Value string `json:"value,omitempty"` } -// NEO has some differences of key storing (that should go away post-preview2). -// ours format: contract's ID (uint32le) + key -// theirs format: contract's ID (uint32le) + key with 16 between every 16 bytes, padded to len 16. -func toNeoStorageKey(key []byte) []byte { - if len(key) < 4 { - panic("invalid key in storage") - } - - // Prefix is a contract's ID in LE. - nkey := make([]byte, 4, len(key)) - copy(nkey, key[:4]) - key = key[4:] - - index := 0 - remain := len(key) - for remain >= 16 { - nkey = append(nkey, key[index:index+16]...) - nkey = append(nkey, 16) - index += 16 - remain -= 16 - } - - if remain > 0 { - nkey = append(nkey, key[index:]...) - } - - padding := 16 - remain - for i := 0; i < padding; i++ { - nkey = append(nkey, 0) - } - - nkey = append(nkey, byte(remain)) - - return nkey -} - // batchToMap converts batch to a map so that JSON is compatible // with https://github.com/NeoResearch/neo-storage-audit/ func batchToMap(index uint32, batch *storage.MemBatch) blockDump { @@ -77,10 +41,9 @@ func batchToMap(index uint32, batch *storage.MemBatch) blockDump { op = "Changed" } - key = toNeoStorageKey(key[1:]) ops = append(ops, storageOp{ State: op, - Key: hex.EncodeToString(key), + Key: hex.EncodeToString(key[1:]), Value: hex.EncodeToString(batch.Put[i].Value), }) } @@ -91,10 +54,9 @@ func batchToMap(index uint32, batch *storage.MemBatch) blockDump { continue } - key = toNeoStorageKey(key[1:]) ops = append(ops, storageOp{ State: "Deleted", - Key: hex.EncodeToString(key), + Key: hex.EncodeToString(key[1:]), }) } diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index d41373af4..7dca1ef13 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -518,7 +518,7 @@ func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block { if err != nil { return nil } - script, err := smartcontract.CreateMultiSigRedeemScript(len(validators)-(len(validators)-1)/3, validators) + script, err := smartcontract.CreateMultiSigRedeemScript(s.dbft.Context.M(), validators) if err != nil { return nil } diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 4dd826e94..acf219fe1 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -226,7 +226,7 @@ func newTestService(t *testing.T) *service { } func getTestValidator(i int) (*privateKey, *publicKey) { - key := testchain.PrivateKeyByID(i) + key := testchain.PrivateKey(i) return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()} } diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index e4ff60d17..9b6e2f9fa 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -51,11 +51,7 @@ func (bc *Blockchain) newBlock(txs ...*transaction.Transaction) *block.Block { func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256, txs ...*transaction.Transaction) *block.Block { validators, _ := validatorsFromConfig(cfg) - vlen := len(validators) - valScript, _ := smartcontract.CreateMultiSigRedeemScript( - vlen-(vlen-1)/3, - validators, - ) + valScript, _ := smartcontract.CreateDefaultMultiSigRedeemScript(validators) witness := transaction.Witness{ VerificationScript: valScript, } @@ -393,7 +389,7 @@ func addSigners(txs ...*transaction.Transaction) { func signTx(bc *Blockchain, txs ...*transaction.Transaction) error { validators := bc.GetStandByValidators() - rawScript, err := smartcontract.CreateMultiSigRedeemScript(bc.config.ValidatorsCount/2+1, validators) + rawScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(validators) if err != nil { return fmt.Errorf("failed to sign tx: %w", err) } diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index add761dc4..524d6879d 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -8,7 +8,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/state" "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" ) @@ -73,7 +72,7 @@ func (g *GAS) Initialize(ic *interop.Context) error { if g.nep5TokenNative.getTotalSupply(ic.DAO).Sign() != 0 { return errors.New("already initialized") } - h, _, err := getStandbyValidatorsHash(ic) + h, err := getStandbyValidatorsHash(ic) if err != nil { return err } @@ -103,13 +102,12 @@ func (g *GAS) OnPersist(ic *interop.Context) error { return nil } -func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, []*keys.PublicKey, error) { - vs := ic.Chain.GetStandByValidators() - s, err := smartcontract.CreateMultiSigRedeemScript(len(vs)/2+1, vs) +func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) { + s, err := smartcontract.CreateDefaultMultiSigRedeemScript(ic.Chain.GetStandByValidators()) if err != nil { - return util.Uint160{}, nil, err + return util.Uint160{}, err } - return hash.Hash160(s), vs, nil + return hash.Hash160(s), nil } func chainOnPersist(fs ...func(*interop.Context) error) func(*interop.Context) error { diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 52e02a663..12b0dc1c6 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -136,23 +136,17 @@ func (n *NEO) Initialize(ic *interop.Context) error { return errors.New("already initialized") } - h, vs, err := getStandbyValidatorsHash(ic) + h, err := getStandbyValidatorsHash(ic) if err != nil { return err } n.mint(ic, h, big.NewInt(NEOTotalSupply)) - err = ic.DAO.PutStorageItem(n.ContractID, []byte{prefixVotersCount}, &state.StorageItem{Value: []byte{0}}) + err = ic.DAO.PutStorageItem(n.ContractID, []byte{prefixVotersCount}, &state.StorageItem{Value: []byte{}}) if err != nil { return err } - for i := range vs { - if err := n.RegisterCandidateInternal(ic, vs[i]); err != nil { - return err - } - } - return nil } @@ -422,6 +416,7 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke count = len(result) } result = result[:count] + sort.Sort(result) n.validators.Store(result) return result, nil } diff --git a/pkg/core/native_neo_test.go b/pkg/core/native_neo_test.go index 71df775e0..3fe7591b9 100644 --- a/pkg/core/native_neo_test.go +++ b/pkg/core/native_neo_test.go @@ -2,6 +2,7 @@ package core import ( "math/big" + "sort" "testing" "github.com/nspcc-dev/neo-go/pkg/config/netmode" @@ -31,9 +32,11 @@ func TestNEO_Vote(t *testing.T) { ic := bc.newInteropContext(trigger.System, bc.dao, nil, tx) ic.VM = vm.New() + standBySorted := bc.GetStandByValidators() + sort.Sort(standBySorted) pubs, err := neo.GetValidatorsInternal(bc, ic.DAO) require.NoError(t, err) - require.Equal(t, bc.GetStandByValidators(), pubs) + require.Equal(t, standBySorted, pubs) sz := testchain.Size() @@ -62,21 +65,10 @@ func TestNEO_Vote(t *testing.T) { require.NoError(t, neo.VoteInternal(ic, h, candidates[i])) } - // First 3 validators must be the ones we have voted for. + // We still haven't voted enough validators in. pubs, err = neo.GetValidatorsInternal(bc, ic.DAO) require.NoError(t, err) - for i := 1; i < sz; i++ { - require.Equal(t, pubs[i-1], candidates[i]) - } - - var ok bool - for _, p := range bc.GetStandByValidators() { - if pubs[sz-1].Equal(p) { - ok = true - break - } - } - require.True(t, ok, "last validator must be stand by") + require.Equal(t, standBySorted, pubs) // Register and give some value to the last validator. require.NoError(t, neo.RegisterCandidateInternal(ic, candidates[0])) @@ -88,7 +80,9 @@ func TestNEO_Vote(t *testing.T) { pubs, err = neo.GetValidatorsInternal(bc, ic.DAO) require.NoError(t, err) - require.Equal(t, candidates, pubs) + sortedCandidates := candidates.Copy() + sort.Sort(sortedCandidates) + require.EqualValues(t, sortedCandidates, pubs) require.NoError(t, neo.UnregisterCandidateInternal(ic, candidates[0])) require.Error(t, neo.VoteInternal(ic, h, candidates[0])) diff --git a/pkg/core/util.go b/pkg/core/util.go index 7f8d4eb20..999a5eef5 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -118,11 +118,7 @@ func committeeFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, e } func getNextConsensusAddress(validators []*keys.PublicKey) (val util.Uint160, err error) { - vlen := len(validators) - raw, err := smartcontract.CreateMultiSigRedeemScript( - vlen-(vlen-1)/3, - validators, - ) + raw, err := smartcontract.CreateDefaultMultiSigRedeemScript(validators) if err != nil { return val, err } diff --git a/pkg/internal/testchain/address.go b/pkg/internal/testchain/address.go index 2c31486fd..b242b5747 100644 --- a/pkg/internal/testchain/address.go +++ b/pkg/internal/testchain/address.go @@ -64,7 +64,7 @@ func MultisigVerificationScript() []byte { pubs = append(pubs, priv.PublicKey()) } - script, err := smartcontract.CreateMultiSigRedeemScript(3, pubs) + script, err := smartcontract.CreateDefaultMultiSigRedeemScript(pubs) if err != nil { panic(err) } diff --git a/pkg/network/blockqueue.go b/pkg/network/blockqueue.go index ed6eac1ae..179c468a1 100644 --- a/pkg/network/blockqueue.go +++ b/pkg/network/blockqueue.go @@ -48,7 +48,7 @@ func (bq *blockQueue) run() { err := bq.chain.AddBlock(minblock) if err != nil { // The block might already be added by consensus. - if _, errget := bq.chain.GetBlock(minblock.Hash()); errget != nil { + if bq.chain.BlockHeight() < minblock.Index { bq.log.Warn("blockQueue: failed adding block into the blockchain", zap.String("error", err.Error()), zap.Uint32("blockHeight", bq.chain.BlockHeight()), diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index e783a43f5..f89bf00ac 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -856,7 +856,7 @@ func (s *Server) getValidators(_ request.Params) (interface{}, *response.Error) if err != nil { return nil, response.NewRPCError("can't get enrollments", "", err) } - var res []result.Validator + var res = make([]result.Validator, 0) for _, v := range enrollments { res = append(res, result.Validator{ PublicKey: *v.Key, diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 8c45b8ac4..8c6c3cba6 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -52,8 +52,8 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "36c3b0c85d98607db00b711885ec3e411d9b1672" -const deploymentTxHash = "60a1fc8ceb7948d9933aec0cedd148441568575c40af7e0985cc366ed153d57e" +const testContractHash = "5b5f77b947194ba45ff5fa1ba6e066af5636d110" +const deploymentTxHash = "af0f94f6bdc5aada7abf1db19f1fcd2ea56cea596c41dc4abdfa6cd9664a7d72" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { @@ -451,6 +451,7 @@ var rpcTestCases = map[string][]rpcTestCase{ result: func(*executor) interface{} { return &[]result.Validator{} }, + /* preview3 doesn't return any validators until there is a vote check: func(t *testing.T, e *executor, validators interface{}) { var expected []result.Validator sBValidators := e.chain.GetStandByValidators() @@ -467,6 +468,7 @@ var rpcTestCases = map[string][]rpcTestCase{ assert.ElementsMatch(t, expected, *actual) }, + */ }, }, "getversion": { @@ -1025,7 +1027,7 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "915.78962700", + Amount: "915.61054740", LastUpdated: 6, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), @@ -1143,16 +1145,6 @@ func checkNep5TransfersAux(t *testing.T, e *executor, acc interface{}, start, en } netFee += b.Transactions[j].NetworkFee } - if i > 0 { - expected.Received = append(expected.Received, result.NEP5Transfer{ - Timestamp: b.Timestamp, - Asset: e.chain.UtilityTokenHash(), - Address: "", // minted from network fees. - Amount: amountToString(big.NewInt(netFee), 8), - Index: b.Index, - TxHash: b.Hash(), - }) - } } require.Equal(t, expected.Address, res.Address) diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index e34c40cd3..430f85dcd 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ diff --git a/pkg/smartcontract/contract.go b/pkg/smartcontract/contract.go index 0b12dc3ba..811ac6ae2 100644 --- a/pkg/smartcontract/contract.go +++ b/pkg/smartcontract/contract.go @@ -10,7 +10,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/opcode" ) -// CreateMultiSigRedeemScript creates a script runnable by the VM. +// CreateMultiSigRedeemScript creates an "m out of n" type verification script +// where n is the length of publicKeys. func CreateMultiSigRedeemScript(m int, publicKeys keys.PublicKeys) ([]byte, error) { if m < 1 { return nil, fmt.Errorf("param m cannot be smaller or equal to 1 got %d", m) @@ -34,3 +35,11 @@ func CreateMultiSigRedeemScript(m int, publicKeys keys.PublicKeys) ([]byte, erro return buf.Bytes(), nil } + +// CreateDefaultMultiSigRedeemScript creates an "m out of n" type verification script +// using publicKeys length with the default BFT assumptions of (n - (n-1)/3) for m. +func CreateDefaultMultiSigRedeemScript(publicKeys keys.PublicKeys) ([]byte, error) { + n := len(publicKeys) + m := n - (n-1)/3 + return CreateMultiSigRedeemScript(m, publicKeys) +} diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go index 889def6be..1c8e74f9c 100644 --- a/pkg/smartcontract/contract_test.go +++ b/pkg/smartcontract/contract_test.go @@ -36,3 +36,50 @@ func TestCreateMultiSigRedeemScript(t *testing.T) { assert.Equal(t, opcode.SYSCALL, opcode.Opcode(br.ReadB())) assert.Equal(t, emit.InteropNameToID([]byte("Neo.Crypto.CheckMultisigWithECDsaSecp256r1")), br.ReadU32LE()) } + +func TestCreateDefaultMultiSigRedeemScript(t *testing.T) { + var validators = make([]*keys.PublicKey, 0) + + var addKey = func() { + key, err := keys.NewPrivateKey() + require.NoError(t, err) + validators = append(validators, key.PublicKey()) + } + var checkM = func(m int) { + validScript, err := CreateMultiSigRedeemScript(m, validators) + require.NoError(t, err) + defaultScript, err := CreateDefaultMultiSigRedeemScript(validators) + require.NoError(t, err) + require.Equal(t, validScript, defaultScript) + } + + // 1 out of 1 + addKey() + checkM(1) + + // 2 out of 2 + addKey() + checkM(2) + + // 3 out of 4 + for i := 0; i < 2; i++ { + addKey() + } + checkM(3) + + // 5 out of 6 + for i := 0; i < 2; i++ { + addKey() + } + checkM(5) + + // 5 out of 7 + addKey() + checkM(5) + + // 7 out of 10 + for i := 0; i < 3; i++ { + addKey() + } + checkM(7) +}