Merge pull request #1300 from nspcc-dev/various-preview3-fixes

Various preview3 testnet fixes
This commit is contained in:
Roman Khimov 2020-08-10 21:49:30 +03:00 committed by GitHub
commit 25ce154cf8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 89 additions and 100 deletions

View file

@ -25,42 +25,6 @@ type storageOp struct {
Value string `json:"value,omitempty"` 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 // batchToMap converts batch to a map so that JSON is compatible
// with https://github.com/NeoResearch/neo-storage-audit/ // with https://github.com/NeoResearch/neo-storage-audit/
func batchToMap(index uint32, batch *storage.MemBatch) blockDump { func batchToMap(index uint32, batch *storage.MemBatch) blockDump {
@ -77,10 +41,9 @@ func batchToMap(index uint32, batch *storage.MemBatch) blockDump {
op = "Changed" op = "Changed"
} }
key = toNeoStorageKey(key[1:])
ops = append(ops, storageOp{ ops = append(ops, storageOp{
State: op, State: op,
Key: hex.EncodeToString(key), Key: hex.EncodeToString(key[1:]),
Value: hex.EncodeToString(batch.Put[i].Value), Value: hex.EncodeToString(batch.Put[i].Value),
}) })
} }
@ -91,10 +54,9 @@ func batchToMap(index uint32, batch *storage.MemBatch) blockDump {
continue continue
} }
key = toNeoStorageKey(key[1:])
ops = append(ops, storageOp{ ops = append(ops, storageOp{
State: "Deleted", State: "Deleted",
Key: hex.EncodeToString(key), Key: hex.EncodeToString(key[1:]),
}) })
} }

View file

@ -518,7 +518,7 @@ func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
if err != nil { if err != nil {
return 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 { if err != nil {
return nil return nil
} }

View file

@ -226,7 +226,7 @@ func newTestService(t *testing.T) *service {
} }
func getTestValidator(i int) (*privateKey, *publicKey) { func getTestValidator(i int) (*privateKey, *publicKey) {
key := testchain.PrivateKeyByID(i) key := testchain.PrivateKey(i)
return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()} return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()}
} }

View file

@ -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 { func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256, txs ...*transaction.Transaction) *block.Block {
validators, _ := validatorsFromConfig(cfg) validators, _ := validatorsFromConfig(cfg)
vlen := len(validators) valScript, _ := smartcontract.CreateDefaultMultiSigRedeemScript(validators)
valScript, _ := smartcontract.CreateMultiSigRedeemScript(
vlen-(vlen-1)/3,
validators,
)
witness := transaction.Witness{ witness := transaction.Witness{
VerificationScript: valScript, VerificationScript: valScript,
} }
@ -393,7 +389,7 @@ func addSigners(txs ...*transaction.Transaction) {
func signTx(bc *Blockchain, txs ...*transaction.Transaction) error { func signTx(bc *Blockchain, txs ...*transaction.Transaction) error {
validators := bc.GetStandByValidators() validators := bc.GetStandByValidators()
rawScript, err := smartcontract.CreateMultiSigRedeemScript(bc.config.ValidatorsCount/2+1, validators) rawScript, err := smartcontract.CreateDefaultMultiSigRedeemScript(validators)
if err != nil { if err != nil {
return fmt.Errorf("failed to sign tx: %w", err) return fmt.Errorf("failed to sign tx: %w", err)
} }

View file

@ -8,7 +8,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/state" "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/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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "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 { if g.nep5TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
return errors.New("already initialized") return errors.New("already initialized")
} }
h, _, err := getStandbyValidatorsHash(ic) h, err := getStandbyValidatorsHash(ic)
if err != nil { if err != nil {
return err return err
} }
@ -103,13 +102,12 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
return nil return nil
} }
func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, []*keys.PublicKey, error) { func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
vs := ic.Chain.GetStandByValidators() s, err := smartcontract.CreateDefaultMultiSigRedeemScript(ic.Chain.GetStandByValidators())
s, err := smartcontract.CreateMultiSigRedeemScript(len(vs)/2+1, vs)
if err != nil { 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 { func chainOnPersist(fs ...func(*interop.Context) error) func(*interop.Context) error {

View file

@ -136,23 +136,17 @@ func (n *NEO) Initialize(ic *interop.Context) error {
return errors.New("already initialized") return errors.New("already initialized")
} }
h, vs, err := getStandbyValidatorsHash(ic) h, err := getStandbyValidatorsHash(ic)
if err != nil { if err != nil {
return err return err
} }
n.mint(ic, h, big.NewInt(NEOTotalSupply)) 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 { if err != nil {
return err return err
} }
for i := range vs {
if err := n.RegisterCandidateInternal(ic, vs[i]); err != nil {
return err
}
}
return nil return nil
} }
@ -422,6 +416,7 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke
count = len(result) count = len(result)
} }
result = result[:count] result = result[:count]
sort.Sort(result)
n.validators.Store(result) n.validators.Store(result)
return result, nil return result, nil
} }

View file

@ -2,6 +2,7 @@ package core
import ( import (
"math/big" "math/big"
"sort"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "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 := bc.newInteropContext(trigger.System, bc.dao, nil, tx)
ic.VM = vm.New() ic.VM = vm.New()
standBySorted := bc.GetStandByValidators()
sort.Sort(standBySorted)
pubs, err := neo.GetValidatorsInternal(bc, ic.DAO) pubs, err := neo.GetValidatorsInternal(bc, ic.DAO)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, bc.GetStandByValidators(), pubs) require.Equal(t, standBySorted, pubs)
sz := testchain.Size() sz := testchain.Size()
@ -62,21 +65,10 @@ func TestNEO_Vote(t *testing.T) {
require.NoError(t, neo.VoteInternal(ic, h, candidates[i])) 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) pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
require.NoError(t, err) require.NoError(t, err)
for i := 1; i < sz; i++ { require.Equal(t, standBySorted, pubs)
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")
// Register and give some value to the last validator. // Register and give some value to the last validator.
require.NoError(t, neo.RegisterCandidateInternal(ic, candidates[0])) 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) pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
require.NoError(t, err) 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.NoError(t, neo.UnregisterCandidateInternal(ic, candidates[0]))
require.Error(t, neo.VoteInternal(ic, h, candidates[0])) require.Error(t, neo.VoteInternal(ic, h, candidates[0]))

View file

@ -118,11 +118,7 @@ func committeeFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, e
} }
func getNextConsensusAddress(validators []*keys.PublicKey) (val util.Uint160, err error) { func getNextConsensusAddress(validators []*keys.PublicKey) (val util.Uint160, err error) {
vlen := len(validators) raw, err := smartcontract.CreateDefaultMultiSigRedeemScript(validators)
raw, err := smartcontract.CreateMultiSigRedeemScript(
vlen-(vlen-1)/3,
validators,
)
if err != nil { if err != nil {
return val, err return val, err
} }

View file

@ -64,7 +64,7 @@ func MultisigVerificationScript() []byte {
pubs = append(pubs, priv.PublicKey()) pubs = append(pubs, priv.PublicKey())
} }
script, err := smartcontract.CreateMultiSigRedeemScript(3, pubs) script, err := smartcontract.CreateDefaultMultiSigRedeemScript(pubs)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -48,7 +48,7 @@ func (bq *blockQueue) run() {
err := bq.chain.AddBlock(minblock) err := bq.chain.AddBlock(minblock)
if err != nil { if err != nil {
// The block might already be added by consensus. // 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", bq.log.Warn("blockQueue: failed adding block into the blockchain",
zap.String("error", err.Error()), zap.String("error", err.Error()),
zap.Uint32("blockHeight", bq.chain.BlockHeight()), zap.Uint32("blockHeight", bq.chain.BlockHeight()),

View file

@ -856,7 +856,7 @@ func (s *Server) getValidators(_ request.Params) (interface{}, *response.Error)
if err != nil { if err != nil {
return nil, response.NewRPCError("can't get enrollments", "", err) return nil, response.NewRPCError("can't get enrollments", "", err)
} }
var res []result.Validator var res = make([]result.Validator, 0)
for _, v := range enrollments { for _, v := range enrollments {
res = append(res, result.Validator{ res = append(res, result.Validator{
PublicKey: *v.Key, PublicKey: *v.Key,

View file

@ -52,8 +52,8 @@ type rpcTestCase struct {
check func(t *testing.T, e *executor, result interface{}) check func(t *testing.T, e *executor, result interface{})
} }
const testContractHash = "36c3b0c85d98607db00b711885ec3e411d9b1672" const testContractHash = "5b5f77b947194ba45ff5fa1ba6e066af5636d110"
const deploymentTxHash = "60a1fc8ceb7948d9933aec0cedd148441568575c40af7e0985cc366ed153d57e" const deploymentTxHash = "af0f94f6bdc5aada7abf1db19f1fcd2ea56cea596c41dc4abdfa6cd9664a7d72"
var rpcTestCases = map[string][]rpcTestCase{ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
@ -451,6 +451,7 @@ var rpcTestCases = map[string][]rpcTestCase{
result: func(*executor) interface{} { result: func(*executor) interface{} {
return &[]result.Validator{} return &[]result.Validator{}
}, },
/* preview3 doesn't return any validators until there is a vote
check: func(t *testing.T, e *executor, validators interface{}) { check: func(t *testing.T, e *executor, validators interface{}) {
var expected []result.Validator var expected []result.Validator
sBValidators := e.chain.GetStandByValidators() sBValidators := e.chain.GetStandByValidators()
@ -467,6 +468,7 @@ var rpcTestCases = map[string][]rpcTestCase{
assert.ElementsMatch(t, expected, *actual) assert.ElementsMatch(t, expected, *actual)
}, },
*/
}, },
}, },
"getversion": { "getversion": {
@ -1025,7 +1027,7 @@ func checkNep5Balances(t *testing.T, e *executor, acc interface{}) {
}, },
{ {
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Amount: "915.78962700", Amount: "915.61054740",
LastUpdated: 6, LastUpdated: 6,
}}, }},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), 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 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) require.Equal(t, expected.Address, res.Address)

Binary file not shown.

View file

@ -10,7 +10,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "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) { func CreateMultiSigRedeemScript(m int, publicKeys keys.PublicKeys) ([]byte, error) {
if m < 1 { if m < 1 {
return nil, fmt.Errorf("param m cannot be smaller or equal to 1 got %d", m) 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 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)
}

View file

@ -36,3 +36,50 @@ func TestCreateMultiSigRedeemScript(t *testing.T) {
assert.Equal(t, opcode.SYSCALL, opcode.Opcode(br.ReadB())) assert.Equal(t, opcode.SYSCALL, opcode.Opcode(br.ReadB()))
assert.Equal(t, emit.InteropNameToID([]byte("Neo.Crypto.CheckMultisigWithECDsaSecp256r1")), br.ReadU32LE()) 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)
}