diff --git a/go.mod b/go.mod index de9fc5226..b6c004c6d 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/go-yaml/yaml v2.1.0+incompatible github.com/gorilla/websocket v1.4.2 github.com/mr-tron/base58 v1.1.2 - github.com/nspcc-dev/dbft v0.0.0-20200623100921-5a182c20965e + github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570 github.com/nspcc-dev/rfc6979 v0.2.0 github.com/pierrec/lz4 v2.5.2+incompatible github.com/pkg/errors v0.8.1 diff --git a/go.sum b/go.sum index 4950195d0..57963dec3 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a h1:ajvxgEe9qY4vvoSm github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1 h1:yEx9WznS+rjE0jl0dLujCxuZSIb+UTjF+005TJu/nNI= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/dbft v0.0.0-20200623100921-5a182c20965e h1:QOT9slflIkEKb5wY0ZUC0dCmCgoqGlhOAh9+xWMIxfg= -github.com/nspcc-dev/dbft v0.0.0-20200623100921-5a182c20965e/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= +github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570 h1:EHBwlOyd2m06C3dnxhpPokpYqlNg7u5ZX/uPBhjYuZ4= +github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= github.com/nspcc-dev/neofs-crypto v0.2.0 h1:ftN+59WqxSWz/RCgXYOfhmltOOqU+udsNQSvN6wkFck= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= diff --git a/pkg/consensus/block.go b/pkg/consensus/block.go index cc4a77f99..390846fc6 100644 --- a/pkg/consensus/block.go +++ b/pkg/consensus/block.go @@ -65,7 +65,7 @@ func (n *neoBlock) PrevHash() util.Uint256 { return n.Block.PrevHash } func (n *neoBlock) MerkleRoot() util.Uint256 { return n.Block.MerkleRoot } // Timestamp implements block.Block interface. -func (n *neoBlock) Timestamp() uint64 { return n.Block.Timestamp * 1000000 } +func (n *neoBlock) Timestamp() uint64 { return n.Block.Timestamp * nsInMs } // Index implements block.Block interface. func (n *neoBlock) Index() uint32 { return n.Block.Index } diff --git a/pkg/consensus/change_view.go b/pkg/consensus/change_view.go index 30ecf4eb0..fedc72c8e 100644 --- a/pkg/consensus/change_view.go +++ b/pkg/consensus/change_view.go @@ -8,19 +8,22 @@ import ( // changeView represents dBFT ChangeView message. type changeView struct { newViewNumber byte - timestamp uint32 + timestamp uint64 + reason payload.ChangeViewReason } var _ payload.ChangeView = (*changeView)(nil) // EncodeBinary implements io.Serializable interface. func (c *changeView) EncodeBinary(w *io.BinWriter) { - w.WriteU32LE(c.timestamp) + w.WriteU64LE(c.timestamp) + w.WriteB(byte(c.reason)) } // DecodeBinary implements io.Serializable interface. func (c *changeView) DecodeBinary(r *io.BinReader) { - c.timestamp = r.ReadU32LE() + c.timestamp = r.ReadU64LE() + c.reason = payload.ChangeViewReason(r.ReadB()) } // NewViewNumber implements payload.ChangeView interface. @@ -30,7 +33,13 @@ func (c changeView) NewViewNumber() byte { return c.newViewNumber } func (c *changeView) SetNewViewNumber(view byte) { c.newViewNumber = view } // Timestamp implements payload.ChangeView interface. -func (c changeView) Timestamp() uint32 { return c.timestamp } +func (c changeView) Timestamp() uint64 { return c.timestamp * nsInMs } // SetTimestamp implements payload.ChangeView interface. -func (c *changeView) SetTimestamp(ts uint32) { c.timestamp = ts } +func (c *changeView) SetTimestamp(ts uint64) { c.timestamp = ts / nsInMs } + +// Reason implements payload.ChangeView interface. +func (c changeView) Reason() payload.ChangeViewReason { return c.reason } + +// SetReason implements payload.ChangeView interface. +func (c *changeView) SetReason(reason payload.ChangeViewReason) { c.reason = reason } diff --git a/pkg/consensus/change_view_test.go b/pkg/consensus/change_view_test.go index 4d1142f39..eaad6b2db 100644 --- a/pkg/consensus/change_view_test.go +++ b/pkg/consensus/change_view_test.go @@ -9,8 +9,8 @@ import ( func TestChangeView_Setters(t *testing.T) { var c changeView - c.SetTimestamp(123) - require.EqualValues(t, 123, c.Timestamp()) + c.SetTimestamp(123 * nsInMs) + require.EqualValues(t, 123*nsInMs, c.Timestamp()) c.SetNewViewNumber(2) require.EqualValues(t, 2, c.NewViewNumber()) diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index c0a74e666..921da8659 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -32,6 +32,9 @@ const cacheMaxCapacity = 100 // defaultTimePerBlock is a period between blocks which is used in NEO. const defaultTimePerBlock = 15 * time.Second +// Number of nanoseconds in millisecond. +const nsInMs = 1000000 + // Service represents consensus instance. type Service interface { // Start initializes dBFT and starts event loop for consensus service. @@ -446,12 +449,16 @@ func (s *service) getVerifiedTx() []block.Transaction { return res } -func (s *service) getValidators(_ ...block.Transaction) []crypto.PublicKey { +func (s *service) getValidators(txes ...block.Transaction) []crypto.PublicKey { var ( pKeys []*keys.PublicKey err error ) - pKeys, err = s.Chain.GetValidators() + if txes == nil { + pKeys, err = s.Chain.GetNextBlockValidators() + } else { + pKeys, err = s.Chain.GetValidators() + } if err != nil { s.log.Error("error while trying to get validators", zap.Error(err)) } @@ -464,15 +471,8 @@ func (s *service) getValidators(_ ...block.Transaction) []crypto.PublicKey { return pubs } -func (s *service) getConsensusAddress(validators ...crypto.PublicKey) (h util.Uint160) { - pubs := convertKeys(validators) - - script, err := smartcontract.CreateMultiSigRedeemScript(s.dbft.M(), pubs) - if err != nil { - return - } - - return crypto.Hash160(script) +func (s *service) getConsensusAddress(validators ...crypto.PublicKey) util.Uint160 { + return util.Uint160{} } func convertKeys(validators []crypto.PublicKey) (pubs []*keys.PublicKey) { @@ -491,9 +491,18 @@ func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block { } block.Block.Network = s.network - block.Block.Timestamp = ctx.Timestamp / 1000000 + block.Block.Timestamp = ctx.Timestamp / nsInMs block.Block.Index = ctx.BlockIndex - block.Block.NextConsensus = ctx.NextConsensus + + validators, err := s.Chain.GetValidators() + if err != nil { + return nil + } + script, err := smartcontract.CreateMultiSigRedeemScript(len(validators)-(len(validators)-1)/3, validators) + if err != nil { + return nil + } + block.Block.NextConsensus = crypto.Hash160(script) block.Block.PrevHash = ctx.PrevHash block.Block.Version = ctx.Version block.Block.ConsensusData.Nonce = ctx.Nonce diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 902a31546..569c1acac 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -63,7 +63,7 @@ func TestService_GetVerified(t *testing.T) { p.SetPayload(&prepareRequest{transactionHashes: hashes}) } else { p.SetType(payload.ChangeViewType) - p.SetPayload(&changeView{newViewNumber: 1, timestamp: uint32(time.Now().Unix())}) + p.SetPayload(&changeView{newViewNumber: 1, timestamp: uint64(time.Now().UnixNano() / nsInMs)}) } p.SetHeight(1) p.SetValidatorIndex(uint16(i)) diff --git a/pkg/consensus/payload_test.go b/pkg/consensus/payload_test.go index 45d44c6cc..1cbe97a3b 100644 --- a/pkg/consensus/payload_test.go +++ b/pkg/consensus/payload_test.go @@ -224,7 +224,7 @@ func randomMessage(t *testing.T, mt messageType) io.Serializable { switch mt { case changeViewType: return &changeView{ - timestamp: rand.Uint32(), + timestamp: rand.Uint64(), } case prepareRequestType: return randomPrepareRequest(t) @@ -235,7 +235,7 @@ func randomMessage(t *testing.T, mt messageType) io.Serializable { random.Fill(c.signature[:]) return &c case recoveryRequestType: - return &recoveryRequest{timestamp: rand.Uint32()} + return &recoveryRequest{timestamp: rand.Uint64()} case recoveryMessageType: return randomRecoveryMessage(t) default: @@ -256,7 +256,6 @@ func randomPrepareRequest(t *testing.T) *prepareRequest { for i := 0; i < txCount; i++ { req.transactionHashes[i] = random.Uint256() } - req.nextConsensus = random.Uint160() return req } @@ -289,7 +288,7 @@ func randomRecoveryMessage(t *testing.T) *recoveryMessage { }, changeViewPayloads: []*changeViewCompact{ { - Timestamp: rand.Uint32(), + Timestamp: rand.Uint64(), ValidatorIndex: 3, OriginalViewNumber: 3, InvocationScript: random.Bytes(4), diff --git a/pkg/consensus/prepare_request.go b/pkg/consensus/prepare_request.go index fb1d314f9..cded41ed2 100644 --- a/pkg/consensus/prepare_request.go +++ b/pkg/consensus/prepare_request.go @@ -11,7 +11,6 @@ type prepareRequest struct { timestamp uint64 nonce uint64 transactionHashes []util.Uint256 - nextConsensus util.Uint160 } var _ payload.PrepareRequest = (*prepareRequest)(nil) @@ -20,7 +19,6 @@ var _ payload.PrepareRequest = (*prepareRequest)(nil) func (p *prepareRequest) EncodeBinary(w *io.BinWriter) { w.WriteU64LE(p.timestamp) w.WriteU64LE(p.nonce) - w.WriteBytes(p.nextConsensus[:]) w.WriteArray(p.transactionHashes) } @@ -28,15 +26,14 @@ func (p *prepareRequest) EncodeBinary(w *io.BinWriter) { func (p *prepareRequest) DecodeBinary(r *io.BinReader) { p.timestamp = r.ReadU64LE() p.nonce = r.ReadU64LE() - r.ReadBytes(p.nextConsensus[:]) r.ReadArray(&p.transactionHashes) } // Timestamp implements payload.PrepareRequest interface. -func (p *prepareRequest) Timestamp() uint64 { return p.timestamp * 1000000 } +func (p *prepareRequest) Timestamp() uint64 { return p.timestamp * nsInMs } // SetTimestamp implements payload.PrepareRequest interface. -func (p *prepareRequest) SetTimestamp(ts uint64) { p.timestamp = ts / 1000000 } +func (p *prepareRequest) SetTimestamp(ts uint64) { p.timestamp = ts / nsInMs } // Nonce implements payload.PrepareRequest interface. func (p *prepareRequest) Nonce() uint64 { return p.nonce } @@ -51,7 +48,7 @@ func (p *prepareRequest) TransactionHashes() []util.Uint256 { return p.transacti func (p *prepareRequest) SetTransactionHashes(hs []util.Uint256) { p.transactionHashes = hs } // NextConsensus implements payload.PrepareRequest interface. -func (p *prepareRequest) NextConsensus() util.Uint160 { return p.nextConsensus } +func (p *prepareRequest) NextConsensus() util.Uint160 { return util.Uint160{} } // SetNextConsensus implements payload.PrepareRequest interface. -func (p *prepareRequest) SetNextConsensus(nc util.Uint160) { p.nextConsensus = nc } +func (p *prepareRequest) SetNextConsensus(_ util.Uint160) {} diff --git a/pkg/consensus/prepare_request_test.go b/pkg/consensus/prepare_request_test.go index 4b91e6cfb..188e32d97 100644 --- a/pkg/consensus/prepare_request_test.go +++ b/pkg/consensus/prepare_request_test.go @@ -20,7 +20,7 @@ func TestPrepareRequest_Setters(t *testing.T) { require.EqualValues(t, 1000000, p.Timestamp()) p.SetNextConsensus(util.Uint160{5, 6, 7}) - require.Equal(t, util.Uint160{5, 6, 7}, p.NextConsensus()) + require.Equal(t, util.Uint160{}, p.NextConsensus()) p.SetNonce(8765) require.EqualValues(t, 8765, p.Nonce()) diff --git a/pkg/consensus/recovery_message.go b/pkg/consensus/recovery_message.go index 8e894b884..817e241e8 100644 --- a/pkg/consensus/recovery_message.go +++ b/pkg/consensus/recovery_message.go @@ -21,7 +21,7 @@ type ( changeViewCompact struct { ValidatorIndex uint16 OriginalViewNumber byte - Timestamp uint32 + Timestamp uint64 InvocationScript []byte } @@ -95,7 +95,7 @@ func (m *recoveryMessage) EncodeBinary(w *io.BinWriter) { func (p *changeViewCompact) DecodeBinary(r *io.BinReader) { p.ValidatorIndex = r.ReadU16LE() p.OriginalViewNumber = r.ReadB() - p.Timestamp = r.ReadU32LE() + p.Timestamp = r.ReadU64LE() p.InvocationScript = r.ReadVarBytes(1024) } @@ -103,7 +103,7 @@ func (p *changeViewCompact) DecodeBinary(r *io.BinReader) { func (p *changeViewCompact) EncodeBinary(w *io.BinWriter) { w.WriteU16LE(p.ValidatorIndex) w.WriteB(p.OriginalViewNumber) - w.WriteU32LE(p.Timestamp) + w.WriteU64LE(p.Timestamp) w.WriteVarBytes(p.InvocationScript) } @@ -164,7 +164,7 @@ func (m *recoveryMessage) AddPayload(p payload.ConsensusPayload) { m.changeViewPayloads = append(m.changeViewPayloads, &changeViewCompact{ ValidatorIndex: p.ValidatorIndex(), OriginalViewNumber: p.ViewNumber(), - Timestamp: p.GetChangeView().Timestamp(), + Timestamp: p.GetChangeView().Timestamp() / nsInMs, InvocationScript: p.(*Payload).Witness.InvocationScript, }) case payload.CommitType: diff --git a/pkg/consensus/recovery_message_test.go b/pkg/consensus/recovery_message_test.go index 203edf45c..54544edb0 100644 --- a/pkg/consensus/recovery_message_test.go +++ b/pkg/consensus/recovery_message_test.go @@ -31,7 +31,6 @@ func TestRecoveryMessage_Setters(t *testing.T) { timestamp: 87, nonce: 321, transactionHashes: []util.Uint256{{1}}, - nextConsensus: util.Uint160{1, 2}, } p1 := new(Payload) p1.message = &message{} diff --git a/pkg/consensus/recovery_request.go b/pkg/consensus/recovery_request.go index d40c6f9e4..52e0fb269 100644 --- a/pkg/consensus/recovery_request.go +++ b/pkg/consensus/recovery_request.go @@ -7,23 +7,23 @@ import ( // recoveryRequest represents dBFT RecoveryRequest message. type recoveryRequest struct { - timestamp uint32 + timestamp uint64 } var _ payload.RecoveryRequest = (*recoveryRequest)(nil) // DecodeBinary implements io.Serializable interface. func (m *recoveryRequest) DecodeBinary(r *io.BinReader) { - m.timestamp = r.ReadU32LE() + m.timestamp = r.ReadU64LE() } // EncodeBinary implements io.Serializable interface. func (m *recoveryRequest) EncodeBinary(w *io.BinWriter) { - w.WriteU32LE(m.timestamp) + w.WriteU64LE(m.timestamp) } // Timestamp implements payload.RecoveryRequest interface. -func (m *recoveryRequest) Timestamp() uint32 { return m.timestamp } +func (m *recoveryRequest) Timestamp() uint64 { return m.timestamp * nsInMs } // SetTimestamp implements payload.RecoveryRequest interface. -func (m *recoveryRequest) SetTimestamp(ts uint32) { m.timestamp = ts } +func (m *recoveryRequest) SetTimestamp(ts uint64) { m.timestamp = ts / nsInMs } diff --git a/pkg/consensus/recovery_request_test.go b/pkg/consensus/recovery_request_test.go index e061711ff..adfc57c51 100644 --- a/pkg/consensus/recovery_request_test.go +++ b/pkg/consensus/recovery_request_test.go @@ -9,6 +9,6 @@ import ( func TestRecoveryRequest_Setters(t *testing.T) { var r recoveryRequest - r.SetTimestamp(123) - require.EqualValues(t, 123, r.Timestamp()) + r.SetTimestamp(123 * nsInMs) + require.EqualValues(t, 123*nsInMs, r.Timestamp()) } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index bbfa38219..a8646e571 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1240,8 +1240,13 @@ func (bc *Blockchain) GetStandByValidators() keys.PublicKeys { return bc.sbValidators.Copy() } -// GetValidators returns next block validators. +// GetValidators returns current validators. func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) { + return bc.contracts.NEO.GetValidatorsInternal(bc, bc.dao) +} + +// GetNextBlockValidators returns next block validators. +func (bc *Blockchain) GetNextBlockValidators() ([]*keys.PublicKey, error) { return bc.contracts.NEO.GetNextBlockValidatorsInternal(bc, bc.dao) } diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index ce723614b..9dcac9e33 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -36,6 +36,7 @@ type Blockchainer interface { HasTransaction(util.Uint256) bool GetAccountState(util.Uint160) *state.Account GetAppExecResult(util.Uint256) (*state.AppExecResult, error) + GetNextBlockValidators() ([]*keys.PublicKey, error) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog GetNEP5Balances(util.Uint160) *state.NEP5Balances GetValidators() ([]*keys.PublicKey, error) diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 6d169eaa4..747025315 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -142,7 +142,7 @@ func (n *NEO) Initialize(ic *interop.Context) error { // OnPersist implements Contract interface. func (n *NEO) OnPersist(ic *interop.Context) error { - pubs, err := n.getValidatorsInternal(ic.Chain, ic.DAO) + pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO) if err != nil { return err } @@ -385,16 +385,16 @@ func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Ite return stackitem.NewArray(arr) } -// getValidatorsInternal returns a list of current validators. -func (n *NEO) getValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { +// GetValidatorsInternal returns a list of current validators. +func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { if vals := n.validators.Load().(keys.PublicKeys); vals != nil { - return vals, nil + return vals.Copy(), nil } standByValidators := bc.GetStandByValidators() si := d.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { n.validators.Store(standByValidators) - return standByValidators, nil + return standByValidators.Copy(), nil } validatorsCount, err := ValidatorsCountFromBytes(si.Value) if err != nil { @@ -442,7 +442,7 @@ func (n *NEO) getValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke } func (n *NEO) getValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - result, err := n.getValidatorsInternal(ic.Chain, ic.DAO) + result, err := n.GetValidatorsInternal(ic.Chain, ic.DAO) if err != nil { panic(err) } @@ -470,7 +470,7 @@ func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao func (n *NEO) getNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { si := d.GetStorageItem(n.ContractID, nextValidatorsKey) if si == nil { - return n.getValidatorsInternal(bc, d) + return n.GetValidatorsInternal(bc, d) } pubs := keys.PublicKeys{} err := pubs.DecodeBytes(si.Value) diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index cc7dbcb7c..6004c44a4 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -77,6 +77,9 @@ func (chain testChain) GetHeader(hash util.Uint256) (*block.Header, error) { func (chain testChain) GetAccountState(util.Uint160) *state.Account { panic("TODO") } +func (chain testChain) GetNextBlockValidators() ([]*keys.PublicKey, error) { + panic("TODO") +} func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog { panic("TODO") }