consensus: move stateroot message generation to commit phase

* update dbft library, change to 64-bit timestamps and CV reason
 * modify messages
 * generate stateroot witness data from prepare* messages during commit

Fix #1273.
This commit is contained in:
Roman Khimov 2020-08-09 23:31:34 +03:00
parent 92851aa8e4
commit 253c39d4ee
14 changed files with 172 additions and 119 deletions

2
go.mod
View file

@ -9,7 +9,7 @@ require (
github.com/go-yaml/yaml v2.1.0+incompatible github.com/go-yaml/yaml v2.1.0+incompatible
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/mr-tron/base58 v1.1.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-20200810081309-f40804dbf8a0
github.com/nspcc-dev/rfc6979 v0.2.0 github.com/nspcc-dev/rfc6979 v0.2.0
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v1.2.1 github.com/prometheus/client_golang v1.2.1

4
go.sum
View file

@ -156,8 +156,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-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 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-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-20200810081309-f40804dbf8a0 h1:4XrUJSvClcBQVZJQqI9EHW/kAIWcrycgTa5J0lBO3R8=
github.com/nspcc-dev/dbft v0.0.0-20200623100921-5a182c20965e/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= github.com/nspcc-dev/dbft v0.0.0-20200810081309-f40804dbf8a0/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/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 h1:ftN+59WqxSWz/RCgXYOfhmltOOqU+udsNQSvN6wkFck=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=

View file

@ -30,7 +30,13 @@ func (c changeView) NewViewNumber() byte { return c.newViewNumber }
func (c *changeView) SetNewViewNumber(view byte) { c.newViewNumber = view } func (c *changeView) SetNewViewNumber(view byte) { c.newViewNumber = view }
// Timestamp implements payload.ChangeView interface. // Timestamp implements payload.ChangeView interface.
func (c changeView) Timestamp() uint32 { return c.timestamp } func (c changeView) Timestamp() uint64 { return uint64(c.timestamp) * nanoInSec }
// SetTimestamp implements payload.ChangeView interface. // SetTimestamp implements payload.ChangeView interface.
func (c *changeView) SetTimestamp(ts uint32) { c.timestamp = ts } func (c *changeView) SetTimestamp(ts uint64) { c.timestamp = uint32(ts / nanoInSec) }
// Reason implements payload.ChangeView interface.
func (c changeView) Reason() payload.ChangeViewReason { return payload.CVUnknown }
// SetReason implements payload.ChangeView interface.
func (c *changeView) SetReason(_ payload.ChangeViewReason) {}

View file

@ -9,8 +9,8 @@ import (
func TestChangeView_Setters(t *testing.T) { func TestChangeView_Setters(t *testing.T) {
var c changeView var c changeView
c.SetTimestamp(123) c.SetTimestamp(123 * nanoInSec)
require.EqualValues(t, 123, c.Timestamp()) require.EqualValues(t, 123*nanoInSec, c.Timestamp())
c.SetNewViewNumber(2) c.SetNewViewNumber(2)
require.EqualValues(t, 2, c.NewViewNumber()) require.EqualValues(t, 2, c.NewViewNumber())

View file

@ -8,9 +8,6 @@ import (
// commit represents dBFT Commit message. // commit represents dBFT Commit message.
type commit struct { type commit struct {
signature [signatureSize]byte signature [signatureSize]byte
stateSig [signatureSize]byte
stateRootEnabled bool
} }
// signatureSize is an rfc6989 signature size in bytes // signatureSize is an rfc6989 signature size in bytes
@ -22,17 +19,11 @@ var _ payload.Commit = (*commit)(nil)
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (c *commit) EncodeBinary(w *io.BinWriter) { func (c *commit) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(c.signature[:]) w.WriteBytes(c.signature[:])
if c.stateRootEnabled {
w.WriteBytes(c.stateSig[:])
}
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (c *commit) DecodeBinary(r *io.BinReader) { func (c *commit) DecodeBinary(r *io.BinReader) {
r.ReadBytes(c.signature[:]) r.ReadBytes(c.signature[:])
if c.stateRootEnabled {
r.ReadBytes(c.stateSig[:])
}
} }
// Signature implements payload.Commit interface. // Signature implements payload.Commit interface.

View file

@ -16,7 +16,6 @@ import (
coreb "github.com/nspcc-dev/neo-go/pkg/core/block" coreb "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/cache" "github.com/nspcc-dev/neo-go/pkg/core/cache"
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mempool"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "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"
@ -136,7 +135,7 @@ func NewService(cfg Config) (Service, error) {
dbft.WithVerifyBlock(srv.verifyBlock), dbft.WithVerifyBlock(srv.verifyBlock),
dbft.WithGetBlock(srv.getBlock), dbft.WithGetBlock(srv.getBlock),
dbft.WithWatchOnly(func() bool { return false }), dbft.WithWatchOnly(func() bool { return false }),
dbft.WithNewBlockFromContext(newBlockFromContext), dbft.WithNewBlockFromContext(srv.newBlockFromContext),
dbft.WithCurrentHeight(cfg.Chain.BlockHeight), dbft.WithCurrentHeight(cfg.Chain.BlockHeight),
dbft.WithCurrentBlockHash(cfg.Chain.CurrentBlockHash), dbft.WithCurrentBlockHash(cfg.Chain.CurrentBlockHash),
dbft.WithGetValidators(srv.getValidators), dbft.WithGetValidators(srv.getValidators),
@ -144,12 +143,13 @@ func NewService(cfg Config) (Service, error) {
dbft.WithNewConsensusPayload(srv.newPayload), dbft.WithNewConsensusPayload(srv.newPayload),
dbft.WithNewPrepareRequest(srv.newPrepareRequest), dbft.WithNewPrepareRequest(srv.newPrepareRequest),
dbft.WithNewPrepareResponse(func() payload.PrepareResponse { return new(prepareResponse) }), dbft.WithNewPrepareResponse(srv.newPrepareResponse),
dbft.WithNewChangeView(func() payload.ChangeView { return new(changeView) }), dbft.WithNewChangeView(func() payload.ChangeView { return new(changeView) }),
dbft.WithNewCommit(srv.newCommit), dbft.WithNewCommit(func() payload.Commit { return new(commit) }),
dbft.WithNewRecoveryRequest(func() payload.RecoveryRequest { return new(recoveryRequest) }), dbft.WithNewRecoveryRequest(func() payload.RecoveryRequest { return new(recoveryRequest) }),
dbft.WithNewRecoveryMessage(srv.newRecoveryMessage), dbft.WithNewRecoveryMessage(srv.newRecoveryMessage),
dbft.WithVerifyPrepareRequest(srv.verifyRequest), dbft.WithVerifyPrepareRequest(srv.verifyRequest),
dbft.WithVerifyPrepareResponse(srv.verifyResponse),
) )
if srv.dbft == nil { if srv.dbft == nil {
@ -235,36 +235,42 @@ func (s *service) stateRootEnabled() bool {
} }
func (s *service) newPrepareRequest() payload.PrepareRequest { func (s *service) newPrepareRequest() payload.PrepareRequest {
res := &prepareRequest{
stateRootEnabled: s.stateRootEnabled(),
}
if !s.stateRootEnabled() { if !s.stateRootEnabled() {
return new(prepareRequest) return res
} }
sr, err := s.Chain.GetStateRoot(s.Chain.BlockHeight()) sig := s.getStateRootSig()
if err == nil { if sig != nil {
return &prepareRequest{ copy(res.stateRootSig[:], sig)
stateRootEnabled: true,
proposalStateRoot: sr.MPTRootBase,
}
} }
return &prepareRequest{stateRootEnabled: true} return res
} }
func (s *service) newCommit() payload.Commit { func (s *service) getStateRootSig() []byte {
var sig []byte
sr, err := s.Chain.GetStateRoot(s.dbft.BlockIndex - 1)
if err == nil {
data := sr.GetSignedPart()
sig, _ = s.dbft.Priv.Sign(data)
}
return sig
}
func (s *service) newPrepareResponse() payload.PrepareResponse {
res := &prepareResponse{
stateRootEnabled: s.stateRootEnabled(),
}
if !s.stateRootEnabled() { if !s.stateRootEnabled() {
return new(commit) return res
} }
c := &commit{stateRootEnabled: true} sig := s.getStateRootSig()
for _, p := range s.dbft.Context.PreparationPayloads { if sig != nil {
if p != nil && p.ViewNumber() == s.dbft.ViewNumber && p.Type() == payload.PrepareRequestType { copy(res.stateRootSig[:], sig)
pr := p.GetPrepareRequest().(*prepareRequest)
data := pr.proposalStateRoot.GetSignedPart()
sign, err := s.dbft.Priv.Sign(data)
if err == nil {
copy(c.stateSig[:], sign)
}
break
}
} }
return c return res
} }
func (s *service) newRecoveryMessage() payload.RecoveryMessage { func (s *service) newRecoveryMessage() payload.RecoveryMessage {
@ -393,15 +399,29 @@ func (s *service) verifyBlock(b block.Block) bool {
return true return true
} }
func (s *service) verifyStateRootSig(index int, sig []byte) error {
r, err := s.Chain.GetStateRoot(s.dbft.BlockIndex - 1)
if err != nil {
return fmt.Errorf("can't get local state root: %v", err)
}
validators := s.getValidators()
if index >= len(validators) {
return errors.New("bad validator index")
}
pub := validators[index]
if pub.Verify(r.GetSignedPart(), sig) != nil {
return errors.New("bad state root signature")
}
return nil
}
func (s *service) verifyRequest(p payload.ConsensusPayload) error { func (s *service) verifyRequest(p payload.ConsensusPayload) error {
req := p.GetPrepareRequest().(*prepareRequest) req := p.GetPrepareRequest().(*prepareRequest)
if s.stateRootEnabled() { if s.stateRootEnabled() {
r, err := s.Chain.GetStateRoot(s.dbft.BlockIndex - 1) err := s.verifyStateRootSig(int(p.ValidatorIndex()), req.stateRootSig[:])
if err != nil { if err != nil {
return fmt.Errorf("can't get local state root: %v", err) return err
}
if !r.Equals(&req.proposalStateRoot) {
return errors.New("state root mismatch")
} }
} }
// Save lastProposal for getVerified(). // Save lastProposal for getVerified().
@ -411,6 +431,14 @@ func (s *service) verifyRequest(p payload.ConsensusPayload) error {
return nil return nil
} }
func (s *service) verifyResponse(p payload.ConsensusPayload) error {
if !s.stateRootEnabled() {
return nil
}
resp := p.GetPrepareResponse().(*prepareResponse)
return s.verifyStateRootSig(int(p.ValidatorIndex()), resp.stateRootSig[:])
}
func (s *service) processBlock(b block.Block) { func (s *service) processBlock(b block.Block) {
bb := &b.(*neoBlock).Block bb := &b.(*neoBlock).Block
bb.Script = *(s.getBlockWitness(bb)) bb.Script = *(s.getBlockWitness(bb))
@ -422,36 +450,26 @@ func (s *service) processBlock(b block.Block) {
s.log.Warn("error on add block", zap.Error(err)) s.log.Warn("error on add block", zap.Error(err))
} }
} }
var rb *state.MPTRootBase
for _, p := range s.dbft.PreparationPayloads {
if p != nil && p.Type() == payload.PrepareRequestType {
rb = &p.GetPrepareRequest().(*prepareRequest).proposalStateRoot
}
}
w := s.getWitness(func(p payload.Commit) []byte { return p.(*commit).stateSig[:] })
r := &state.MPTRoot{
MPTRootBase: *rb,
Witness: w,
}
if err := s.Chain.AddStateRoot(r); err != nil {
s.log.Warn("errors while adding state root", zap.Error(err))
}
s.Broadcast(r)
} }
func (s *service) getBlockWitness(_ *coreb.Block) *transaction.Witness { func (s *service) getBlockWitness(_ *coreb.Block) *transaction.Witness {
return s.getWitness(func(p payload.Commit) []byte { return p.Signature() }) return s.getWitness(func(ctx dbft.Context, i int) []byte {
if p := ctx.CommitPayloads[i]; p != nil && p.ViewNumber() == ctx.ViewNumber {
return p.GetCommit().Signature()
}
return nil
})
} }
func (s *service) getWitness(f func(p payload.Commit) []byte) *transaction.Witness { func (s *service) getWitness(f func(dbft.Context, int) []byte) *transaction.Witness {
dctx := s.dbft.Context dctx := s.dbft.Context
pubs := convertKeys(dctx.Validators) pubs := convertKeys(dctx.Validators)
sigs := make(map[*keys.PublicKey][]byte) sigs := make(map[*keys.PublicKey][]byte)
for i := range pubs { for i := range pubs {
if p := dctx.CommitPayloads[i]; p != nil && p.ViewNumber() == dctx.ViewNumber { sig := f(dctx, i)
sigs[pubs[i]] = f(p.GetCommit()) if sig != nil {
sigs[pubs[i]] = sig
} }
} }
@ -603,7 +621,29 @@ func convertKeys(validators []crypto.PublicKey) (pubs []*keys.PublicKey) {
return return
} }
func newBlockFromContext(ctx *dbft.Context) block.Block { func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
if s.stateRootEnabled() {
// This is being called when we're ready to commit, so we can safely
// relay stateroot here.
stateRoot, err := s.Chain.GetStateRoot(s.dbft.Context.BlockIndex - 1)
if err != nil {
s.log.Warn("can't get stateroot", zap.Uint32("block", s.dbft.Context.BlockIndex-1))
}
r := stateRoot.MPTRoot
r.Witness = s.getWitness(func(ctx dbft.Context, i int) []byte {
if p := ctx.PreparationPayloads[i]; p != nil && p.ViewNumber() == ctx.ViewNumber {
if int(ctx.PrimaryIndex) == i {
return p.GetPrepareRequest().(*prepareRequest).stateRootSig[:]
}
return p.GetPrepareResponse().(*prepareResponse).stateRootSig[:]
}
return nil
})
if err := s.Chain.AddStateRoot(&r); err != nil {
s.log.Warn("errors while adding state root", zap.Error(err))
}
s.Broadcast(&r)
}
block := new(neoBlock) block := new(neoBlock)
if len(ctx.TransactionHashes) == 0 { if len(ctx.TransactionHashes) == 0 {
return nil return nil

View file

@ -49,20 +49,24 @@ func TestService_GetVerified(t *testing.T) {
// Everyone sends a message. // Everyone sends a message.
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
p := new(Payload) p := srv.newPayload().(*Payload)
p.message = &message{} p.SetHeight(1)
p.SetValidatorIndex(uint16(i))
priv, _ := getTestValidator(i)
// To properly sign stateroot in prepare request.
srv.dbft.Priv = priv
// One PrepareRequest and three ChangeViews. // One PrepareRequest and three ChangeViews.
if i == 1 { if i == 1 {
p.SetType(payload.PrepareRequestType) p.SetType(payload.PrepareRequestType)
p.SetPayload(&prepareRequest{transactionHashes: hashes, minerTx: *newMinerTx(999)}) preq := srv.newPrepareRequest().(*prepareRequest)
preq.transactionHashes = hashes
preq.minerTx = *newMinerTx(999)
p.SetPayload(preq)
} else { } else {
p.SetType(payload.ChangeViewType) p.SetType(payload.ChangeViewType)
p.SetPayload(&changeView{newViewNumber: 1, timestamp: uint32(time.Now().Unix())}) p.SetPayload(&changeView{newViewNumber: 1, timestamp: uint32(time.Now().Unix())})
} }
p.SetHeight(1)
p.SetValidatorIndex(uint16(i))
priv, _ := getTestValidator(i)
require.NoError(t, p.Sign(priv)) require.NoError(t, p.Sign(priv))
// Skip srv.OnPayload, because the service is not really started. // Skip srv.OnPayload, because the service is not really started.

View file

@ -49,6 +49,8 @@ const (
commitType messageType = 0x30 commitType messageType = 0x30
recoveryRequestType messageType = 0x40 recoveryRequestType messageType = 0x40
recoveryMessageType messageType = 0x41 recoveryMessageType messageType = 0x41
nanoInSec = 1000_000_000
) )
// ViewNumber implements payload.ConsensusPayload interface. // ViewNumber implements payload.ConsensusPayload interface.
@ -289,11 +291,11 @@ func (m *message) DecodeBinary(r *io.BinReader) {
stateRootEnabled: m.stateRootEnabled, stateRootEnabled: m.stateRootEnabled,
} }
case prepareResponseType: case prepareResponseType:
m.payload = new(prepareResponse) m.payload = &prepareResponse{
case commitType:
m.payload = &commit{
stateRootEnabled: m.stateRootEnabled, stateRootEnabled: m.stateRootEnabled,
} }
case commitType:
m.payload = new(commit)
case recoveryRequestType: case recoveryRequestType:
m.payload = new(recoveryRequest) m.payload = new(recoveryRequest)
case recoveryMessageType: case recoveryMessageType:

View file

@ -173,13 +173,13 @@ func testEncodeDecode(srEnabled bool, mt messageType, actual io.Serializable) fu
} }
func TestCommit_Serializable(t *testing.T) { func TestCommit_Serializable(t *testing.T) {
t.Run("WithStateRoot", testEncodeDecode(true, commitType, &commit{stateRootEnabled: true})) testEncodeDecode(false, commitType, &commit{})
t.Run("NoStateRoot", testEncodeDecode(false, commitType, &commit{stateRootEnabled: false}))
} }
func TestPrepareResponse_Serializable(t *testing.T) { func TestPrepareResponse_Serializable(t *testing.T) {
resp := randomMessage(t, prepareResponseType) t.Run("WithStateRoot", testEncodeDecode(true, prepareResponseType, &prepareResponse{stateRootEnabled: true}))
testserdes.EncodeDecodeBinary(t, resp, new(prepareResponse)) t.Run("NoStateRoot", testEncodeDecode(false, prepareResponseType, &prepareResponse{stateRootEnabled: false}))
} }
func TestPrepareRequest_Serializable(t *testing.T) { func TestPrepareRequest_Serializable(t *testing.T) {
@ -231,14 +231,15 @@ func randomMessage(t *testing.T, mt messageType, srEnabled ...bool) io.Serializa
case prepareRequestType: case prepareRequestType:
return randomPrepareRequest(t, srEnabled...) return randomPrepareRequest(t, srEnabled...)
case prepareResponseType: case prepareResponseType:
return &prepareResponse{preparationHash: random.Uint256()} var p = prepareResponse{preparationHash: random.Uint256()}
if len(srEnabled) > 0 && srEnabled[0] {
p.stateRootEnabled = true
random.Fill(p.stateRootSig[:])
}
return &p
case commitType: case commitType:
var c commit var c commit
random.Fill(c.signature[:]) random.Fill(c.signature[:])
if len(srEnabled) > 0 && srEnabled[0] {
c.stateRootEnabled = true
random.Fill(c.stateSig[:])
}
return &c return &c
case recoveryRequestType: case recoveryRequestType:
return &recoveryRequest{timestamp: rand.Uint32()} return &recoveryRequest{timestamp: rand.Uint32()}
@ -268,9 +269,7 @@ func randomPrepareRequest(t *testing.T, srEnabled ...bool) *prepareRequest {
if len(srEnabled) > 0 && srEnabled[0] { if len(srEnabled) > 0 && srEnabled[0] {
req.stateRootEnabled = true req.stateRootEnabled = true
req.proposalStateRoot.Index = rand.Uint32() random.Fill(req.stateRootSig[:])
req.proposalStateRoot.PrevHash = random.Uint256()
req.proposalStateRoot.Root = random.Uint256()
} }
return req return req
@ -318,9 +317,10 @@ func randomRecoveryMessage(t *testing.T, srEnabled ...bool) *recoveryMessage {
if len(srEnabled) > 0 && srEnabled[0] { if len(srEnabled) > 0 && srEnabled[0] {
rec.stateRootEnabled = true rec.stateRootEnabled = true
rec.prepareRequest.stateRootEnabled = true rec.prepareRequest.stateRootEnabled = true
for _, c := range rec.commitPayloads { random.Fill(prepReq.stateRootSig[:])
c.stateRootEnabled = true for _, p := range rec.preparationPayloads {
random.Fill(c.StateSignature[:]) p.stateRootEnabled = true
random.Fill(p.StateRootSig[:])
} }
} }
return rec return rec

View file

@ -2,7 +2,6 @@ package consensus
import ( import (
"github.com/nspcc-dev/dbft/payload" "github.com/nspcc-dev/dbft/payload"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -15,7 +14,7 @@ type prepareRequest struct {
transactionHashes []util.Uint256 transactionHashes []util.Uint256
minerTx transaction.Transaction minerTx transaction.Transaction
nextConsensus util.Uint160 nextConsensus util.Uint160
proposalStateRoot state.MPTRootBase stateRootSig [signatureSize]byte
stateRootEnabled bool stateRootEnabled bool
} }
@ -30,7 +29,7 @@ func (p *prepareRequest) EncodeBinary(w *io.BinWriter) {
w.WriteArray(p.transactionHashes) w.WriteArray(p.transactionHashes)
p.minerTx.EncodeBinary(w) p.minerTx.EncodeBinary(w)
if p.stateRootEnabled { if p.stateRootEnabled {
p.proposalStateRoot.EncodeBinary(w) w.WriteBytes(p.stateRootSig[:])
} }
} }
@ -42,7 +41,7 @@ func (p *prepareRequest) DecodeBinary(r *io.BinReader) {
r.ReadArray(&p.transactionHashes) r.ReadArray(&p.transactionHashes)
p.minerTx.DecodeBinary(r) p.minerTx.DecodeBinary(r)
if p.stateRootEnabled { if p.stateRootEnabled {
p.proposalStateRoot.DecodeBinary(r) r.ReadBytes(p.stateRootSig[:])
} }
} }

View file

@ -9,6 +9,9 @@ import (
// prepareResponse represents dBFT PrepareResponse message. // prepareResponse represents dBFT PrepareResponse message.
type prepareResponse struct { type prepareResponse struct {
preparationHash util.Uint256 preparationHash util.Uint256
stateRootSig [signatureSize]byte
stateRootEnabled bool
} }
var _ payload.PrepareResponse = (*prepareResponse)(nil) var _ payload.PrepareResponse = (*prepareResponse)(nil)
@ -16,11 +19,17 @@ var _ payload.PrepareResponse = (*prepareResponse)(nil)
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (p *prepareResponse) EncodeBinary(w *io.BinWriter) { func (p *prepareResponse) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(p.preparationHash[:]) w.WriteBytes(p.preparationHash[:])
if p.stateRootEnabled {
w.WriteBytes(p.stateRootSig[:])
}
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (p *prepareResponse) DecodeBinary(r *io.BinReader) { func (p *prepareResponse) DecodeBinary(r *io.BinReader) {
r.ReadBytes(p.preparationHash[:]) r.ReadBytes(p.preparationHash[:])
if p.stateRootEnabled {
r.ReadBytes(p.stateRootSig[:])
}
} }
// PreparationHash implements payload.PrepareResponse interface. // PreparationHash implements payload.PrepareResponse interface.

View file

@ -32,15 +32,15 @@ type (
ViewNumber byte ViewNumber byte
ValidatorIndex uint16 ValidatorIndex uint16
Signature [signatureSize]byte Signature [signatureSize]byte
StateSignature [signatureSize]byte
InvocationScript []byte InvocationScript []byte
stateRootEnabled bool
} }
preparationCompact struct { preparationCompact struct {
ValidatorIndex uint16 ValidatorIndex uint16
InvocationScript []byte InvocationScript []byte
StateRootSig [signatureSize]byte
stateRootEnabled bool
} }
) )
@ -72,17 +72,17 @@ func (m *recoveryMessage) DecodeBinary(r *io.BinReader) {
} }
} }
r.ReadArray(&m.preparationPayloads)
lu := r.ReadVarUint() lu := r.ReadVarUint()
if lu > state.MaxValidatorsVoted { if lu > state.MaxValidatorsVoted {
r.Err = errors.New("too many preparation payloads") r.Err = errors.New("too many preparation payloads")
return return
} }
m.commitPayloads = make([]*commitCompact, lu) m.preparationPayloads = make([]*preparationCompact, lu)
for i := uint64(0); i < lu; i++ { for i := uint64(0); i < lu; i++ {
m.commitPayloads[i] = &commitCompact{stateRootEnabled: m.stateRootEnabled} m.preparationPayloads[i] = &preparationCompact{stateRootEnabled: m.stateRootEnabled}
m.commitPayloads[i].DecodeBinary(r) m.preparationPayloads[i].DecodeBinary(r)
} }
r.ReadArray(&m.commitPayloads)
} }
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
@ -127,9 +127,6 @@ func (p *commitCompact) DecodeBinary(r *io.BinReader) {
p.ViewNumber = r.ReadB() p.ViewNumber = r.ReadB()
p.ValidatorIndex = r.ReadU16LE() p.ValidatorIndex = r.ReadU16LE()
r.ReadBytes(p.Signature[:]) r.ReadBytes(p.Signature[:])
if p.stateRootEnabled {
r.ReadBytes(p.StateSignature[:])
}
p.InvocationScript = r.ReadVarBytes(1024) p.InvocationScript = r.ReadVarBytes(1024)
} }
@ -138,9 +135,6 @@ func (p *commitCompact) EncodeBinary(w *io.BinWriter) {
w.WriteB(p.ViewNumber) w.WriteB(p.ViewNumber)
w.WriteU16LE(p.ValidatorIndex) w.WriteU16LE(p.ValidatorIndex)
w.WriteBytes(p.Signature[:]) w.WriteBytes(p.Signature[:])
if p.stateRootEnabled {
w.WriteBytes(p.StateSignature[:])
}
w.WriteVarBytes(p.InvocationScript) w.WriteVarBytes(p.InvocationScript)
} }
@ -148,12 +142,18 @@ func (p *commitCompact) EncodeBinary(w *io.BinWriter) {
func (p *preparationCompact) DecodeBinary(r *io.BinReader) { func (p *preparationCompact) DecodeBinary(r *io.BinReader) {
p.ValidatorIndex = r.ReadU16LE() p.ValidatorIndex = r.ReadU16LE()
p.InvocationScript = r.ReadVarBytes(1024) p.InvocationScript = r.ReadVarBytes(1024)
if p.stateRootEnabled {
r.ReadBytes(p.StateRootSig[:])
}
} }
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (p *preparationCompact) EncodeBinary(w *io.BinWriter) { func (p *preparationCompact) EncodeBinary(w *io.BinWriter) {
w.WriteU16LE(p.ValidatorIndex) w.WriteU16LE(p.ValidatorIndex)
w.WriteVarBytes(p.InvocationScript) w.WriteVarBytes(p.InvocationScript)
if p.stateRootEnabled {
w.WriteBytes(p.StateRootSig[:])
}
} }
// AddPayload implements payload.RecoveryMessage interface. // AddPayload implements payload.RecoveryMessage interface.
@ -170,13 +170,17 @@ func (m *recoveryMessage) AddPayload(p payload.ConsensusPayload) {
h := p.Hash() h := p.Hash()
m.preparationHash = &h m.preparationHash = &h
m.preparationPayloads = append(m.preparationPayloads, &preparationCompact{ m.preparationPayloads = append(m.preparationPayloads, &preparationCompact{
stateRootEnabled: m.stateRootEnabled,
ValidatorIndex: p.ValidatorIndex(), ValidatorIndex: p.ValidatorIndex(),
InvocationScript: p.(*Payload).Witness.InvocationScript, InvocationScript: p.(*Payload).Witness.InvocationScript,
StateRootSig: p.GetPrepareRequest().(*prepareRequest).stateRootSig,
}) })
case payload.PrepareResponseType: case payload.PrepareResponseType:
m.preparationPayloads = append(m.preparationPayloads, &preparationCompact{ m.preparationPayloads = append(m.preparationPayloads, &preparationCompact{
stateRootEnabled: m.stateRootEnabled,
ValidatorIndex: p.ValidatorIndex(), ValidatorIndex: p.ValidatorIndex(),
InvocationScript: p.(*Payload).Witness.InvocationScript, InvocationScript: p.(*Payload).Witness.InvocationScript,
StateRootSig: p.GetPrepareResponse().(*prepareResponse).stateRootSig,
}) })
if m.preparationHash == nil { if m.preparationHash == nil {
@ -187,16 +191,14 @@ func (m *recoveryMessage) AddPayload(p payload.ConsensusPayload) {
m.changeViewPayloads = append(m.changeViewPayloads, &changeViewCompact{ m.changeViewPayloads = append(m.changeViewPayloads, &changeViewCompact{
ValidatorIndex: p.ValidatorIndex(), ValidatorIndex: p.ValidatorIndex(),
OriginalViewNumber: p.ViewNumber(), OriginalViewNumber: p.ViewNumber(),
Timestamp: p.GetChangeView().Timestamp(), Timestamp: p.GetChangeView().(*changeView).timestamp,
InvocationScript: p.(*Payload).Witness.InvocationScript, InvocationScript: p.(*Payload).Witness.InvocationScript,
}) })
case payload.CommitType: case payload.CommitType:
m.commitPayloads = append(m.commitPayloads, &commitCompact{ m.commitPayloads = append(m.commitPayloads, &commitCompact{
stateRootEnabled: m.stateRootEnabled,
ValidatorIndex: p.ValidatorIndex(), ValidatorIndex: p.ValidatorIndex(),
ViewNumber: p.ViewNumber(), ViewNumber: p.ViewNumber(),
Signature: p.GetCommit().(*commit).signature, Signature: p.GetCommit().(*commit).signature,
StateSignature: p.GetCommit().(*commit).stateSig,
InvocationScript: p.(*Payload).Witness.InvocationScript, InvocationScript: p.(*Payload).Witness.InvocationScript,
}) })
} }
@ -239,6 +241,9 @@ func (m *recoveryMessage) GetPrepareResponses(p payload.ConsensusPayload, valida
for i, resp := range m.preparationPayloads { for i, resp := range m.preparationPayloads {
r := fromPayload(prepareResponseType, p.(*Payload), &prepareResponse{ r := fromPayload(prepareResponseType, p.(*Payload), &prepareResponse{
preparationHash: *m.preparationHash, preparationHash: *m.preparationHash,
stateRootSig: resp.StateRootSig,
stateRootEnabled: m.stateRootEnabled,
}) })
r.SetValidatorIndex(resp.ValidatorIndex) r.SetValidatorIndex(resp.ValidatorIndex)
r.Witness.InvocationScript = resp.InvocationScript r.Witness.InvocationScript = resp.InvocationScript
@ -277,9 +282,6 @@ func (m *recoveryMessage) GetCommits(p payload.ConsensusPayload, validators []cr
for i, c := range m.commitPayloads { for i, c := range m.commitPayloads {
cc := fromPayload(commitType, p.(*Payload), &commit{ cc := fromPayload(commitType, p.(*Payload), &commit{
signature: c.Signature, signature: c.Signature,
stateSig: c.StateSignature,
stateRootEnabled: m.stateRootEnabled,
}) })
cc.SetValidatorIndex(c.ValidatorIndex) cc.SetValidatorIndex(c.ValidatorIndex)
cc.Witness.InvocationScript = c.InvocationScript cc.Witness.InvocationScript = c.InvocationScript

View file

@ -23,7 +23,7 @@ func (m *recoveryRequest) EncodeBinary(w *io.BinWriter) {
} }
// Timestamp implements payload.RecoveryRequest interface. // Timestamp implements payload.RecoveryRequest interface.
func (m *recoveryRequest) Timestamp() uint32 { return m.timestamp } func (m *recoveryRequest) Timestamp() uint64 { return uint64(m.timestamp) * nanoInSec }
// SetTimestamp implements payload.RecoveryRequest interface. // SetTimestamp implements payload.RecoveryRequest interface.
func (m *recoveryRequest) SetTimestamp(ts uint32) { m.timestamp = ts } func (m *recoveryRequest) SetTimestamp(ts uint64) { m.timestamp = uint32(ts / nanoInSec) }

View file

@ -9,6 +9,6 @@ import (
func TestRecoveryRequest_Setters(t *testing.T) { func TestRecoveryRequest_Setters(t *testing.T) {
var r recoveryRequest var r recoveryRequest
r.SetTimestamp(123) r.SetTimestamp(123 * nanoInSec)
require.EqualValues(t, 123, r.Timestamp()) require.EqualValues(t, 123*nanoInSec, r.Timestamp())
} }