2019-11-08 15:40:21 +00:00
|
|
|
package consensus
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
"github.com/nspcc-dev/dbft/payload"
|
2021-03-25 18:59:54 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2021-01-14 11:17:00 +00:00
|
|
|
npayload "github.com/nspcc-dev/neo-go/pkg/network/payload"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-04-21 13:45:48 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
2019-11-08 15:40:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
messageType byte
|
|
|
|
|
|
|
|
message struct {
|
2021-01-14 11:17:00 +00:00
|
|
|
Type messageType
|
|
|
|
BlockIndex uint32
|
|
|
|
ValidatorIndex byte
|
|
|
|
ViewNumber byte
|
2019-11-08 15:40:21 +00:00
|
|
|
|
|
|
|
payload io.Serializable
|
2020-11-17 12:57:50 +00:00
|
|
|
// stateRootEnabled specifies if state root is exchanged during consensus.
|
|
|
|
stateRootEnabled bool
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Payload is a type for consensus-related messages.
|
|
|
|
Payload struct {
|
2021-01-14 11:17:00 +00:00
|
|
|
npayload.Extensible
|
|
|
|
message
|
2021-03-25 18:59:54 +00:00
|
|
|
network netmode.Magic
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
changeViewType messageType = 0x00
|
|
|
|
prepareRequestType messageType = 0x20
|
|
|
|
prepareResponseType messageType = 0x21
|
|
|
|
commitType messageType = 0x30
|
|
|
|
recoveryRequestType messageType = 0x40
|
|
|
|
recoveryMessageType messageType = 0x41
|
2020-07-13 15:07:02 +00:00
|
|
|
|
|
|
|
payloadGasLimit = 2000000 // 0.02 GAS
|
2019-11-08 15:40:21 +00:00
|
|
|
)
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
// ViewNumber implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) ViewNumber() byte {
|
|
|
|
return p.message.ViewNumber
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetViewNumber implements payload.ConsensusPayload interface.
|
|
|
|
func (p *Payload) SetViewNumber(view byte) {
|
|
|
|
p.message.ViewNumber = view
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) Type() payload.MessageType {
|
|
|
|
return payload.MessageType(p.message.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetType implements payload.ConsensusPayload interface.
|
|
|
|
func (p *Payload) SetType(t payload.MessageType) {
|
|
|
|
p.message.Type = messageType(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Payload implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) Payload() interface{} {
|
|
|
|
return p.payload
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetPayload implements payload.ConsensusPayload interface.
|
|
|
|
func (p *Payload) SetPayload(pl interface{}) {
|
|
|
|
p.payload = pl.(io.Serializable)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetChangeView implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) GetChangeView() payload.ChangeView { return p.payload.(payload.ChangeView) }
|
|
|
|
|
|
|
|
// GetPrepareRequest implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) GetPrepareRequest() payload.PrepareRequest {
|
|
|
|
return p.payload.(payload.PrepareRequest)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetPrepareResponse implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) GetPrepareResponse() payload.PrepareResponse {
|
|
|
|
return p.payload.(payload.PrepareResponse)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetCommit implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) GetCommit() payload.Commit { return p.payload.(payload.Commit) }
|
|
|
|
|
|
|
|
// GetRecoveryRequest implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) GetRecoveryRequest() payload.RecoveryRequest {
|
|
|
|
return p.payload.(payload.RecoveryRequest)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRecoveryMessage implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) GetRecoveryMessage() payload.RecoveryMessage {
|
|
|
|
return p.payload.(payload.RecoveryMessage)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidatorIndex implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) ValidatorIndex() uint16 {
|
2021-01-14 11:17:00 +00:00
|
|
|
return uint16(p.message.ValidatorIndex)
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetValidatorIndex implements payload.ConsensusPayload interface.
|
|
|
|
func (p *Payload) SetValidatorIndex(i uint16) {
|
2021-01-14 11:17:00 +00:00
|
|
|
p.message.ValidatorIndex = byte(i)
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Height implements payload.ConsensusPayload interface.
|
|
|
|
func (p Payload) Height() uint32 {
|
2021-01-14 11:17:00 +00:00
|
|
|
return p.message.BlockIndex
|
2019-11-15 10:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetHeight implements payload.ConsensusPayload interface.
|
|
|
|
func (p *Payload) SetHeight(h uint32) {
|
2021-01-14 11:17:00 +00:00
|
|
|
p.message.BlockIndex = h
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// EncodeBinary implements io.Serializable interface.
|
|
|
|
func (p *Payload) EncodeBinary(w *io.BinWriter) {
|
2021-01-14 11:17:00 +00:00
|
|
|
p.encodeData()
|
|
|
|
p.Extensible.EncodeBinary(w)
|
2020-06-26 15:27:18 +00:00
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
// Sign signs payload using the private key.
|
|
|
|
// It also sets corresponding verification and invocation scripts.
|
|
|
|
func (p *Payload) Sign(key *privateKey) error {
|
2021-01-14 11:17:00 +00:00
|
|
|
p.encodeData()
|
2021-03-25 18:59:54 +00:00
|
|
|
sig := key.PrivateKey.SignHashable(uint32(p.network), &p.Extensible)
|
2019-11-15 10:32:40 +00:00
|
|
|
|
2020-04-21 13:45:48 +00:00
|
|
|
buf := io.NewBufBinWriter()
|
|
|
|
emit.Bytes(buf.BinWriter, sig)
|
|
|
|
p.Witness.InvocationScript = buf.Bytes()
|
2019-12-03 14:25:25 +00:00
|
|
|
p.Witness.VerificationScript = key.PublicKey().GetVerificationScript()
|
2019-11-15 10:32:40 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hash implements payload.ConsensusPayload interface.
|
2019-11-08 15:40:21 +00:00
|
|
|
func (p *Payload) Hash() util.Uint256 {
|
2021-01-14 11:17:00 +00:00
|
|
|
if p.Extensible.Data == nil {
|
|
|
|
p.encodeData()
|
2020-01-30 12:51:49 +00:00
|
|
|
}
|
2021-01-14 11:17:00 +00:00
|
|
|
return p.Extensible.Hash()
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DecodeBinary implements io.Serializable interface.
|
|
|
|
func (p *Payload) DecodeBinary(r *io.BinReader) {
|
2021-01-14 11:17:00 +00:00
|
|
|
p.Extensible.DecodeBinary(r)
|
|
|
|
p.decodeData()
|
2019-11-08 15:40:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// EncodeBinary implements io.Serializable interface.
|
|
|
|
func (m *message) EncodeBinary(w *io.BinWriter) {
|
2021-01-14 11:17:00 +00:00
|
|
|
w.WriteB(byte(m.Type))
|
|
|
|
w.WriteU32LE(m.BlockIndex)
|
|
|
|
w.WriteB(m.ValidatorIndex)
|
2019-12-12 17:17:50 +00:00
|
|
|
w.WriteB(m.ViewNumber)
|
2019-11-08 15:40:21 +00:00
|
|
|
m.payload.EncodeBinary(w)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DecodeBinary implements io.Serializable interface.
|
|
|
|
func (m *message) DecodeBinary(r *io.BinReader) {
|
2019-12-12 17:17:50 +00:00
|
|
|
m.Type = messageType(r.ReadB())
|
2021-01-14 11:17:00 +00:00
|
|
|
m.BlockIndex = r.ReadU32LE()
|
|
|
|
m.ValidatorIndex = r.ReadB()
|
2019-12-12 17:17:50 +00:00
|
|
|
m.ViewNumber = r.ReadB()
|
2019-11-08 15:40:21 +00:00
|
|
|
|
|
|
|
switch m.Type {
|
|
|
|
case changeViewType:
|
|
|
|
cv := new(changeView)
|
2019-11-15 10:32:40 +00:00
|
|
|
// newViewNumber is not marshaled
|
|
|
|
cv.newViewNumber = m.ViewNumber + 1
|
2019-11-08 15:40:21 +00:00
|
|
|
m.payload = cv
|
|
|
|
case prepareRequestType:
|
2020-11-17 12:57:50 +00:00
|
|
|
r := new(prepareRequest)
|
|
|
|
if m.stateRootEnabled {
|
|
|
|
r.stateRootEnabled = true
|
|
|
|
}
|
|
|
|
m.payload = r
|
2019-11-08 15:40:21 +00:00
|
|
|
case prepareResponseType:
|
|
|
|
m.payload = new(prepareResponse)
|
|
|
|
case commitType:
|
|
|
|
m.payload = new(commit)
|
|
|
|
case recoveryRequestType:
|
|
|
|
m.payload = new(recoveryRequest)
|
|
|
|
case recoveryMessageType:
|
2020-11-17 12:57:50 +00:00
|
|
|
r := new(recoveryMessage)
|
|
|
|
if m.stateRootEnabled {
|
|
|
|
r.stateRootEnabled = true
|
|
|
|
}
|
|
|
|
m.payload = r
|
2019-11-08 15:40:21 +00:00
|
|
|
default:
|
2020-08-06 14:44:08 +00:00
|
|
|
r.Err = fmt.Errorf("invalid type: 0x%02x", byte(m.Type))
|
2019-11-08 15:40:21 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
m.payload.DecodeBinary(r)
|
|
|
|
}
|
|
|
|
|
|
|
|
// String implements fmt.Stringer interface.
|
|
|
|
func (t messageType) String() string {
|
|
|
|
switch t {
|
|
|
|
case changeViewType:
|
|
|
|
return "ChangeView"
|
|
|
|
case prepareRequestType:
|
|
|
|
return "PrepareRequest"
|
|
|
|
case prepareResponseType:
|
|
|
|
return "PrepareResponse"
|
|
|
|
case commitType:
|
|
|
|
return "Commit"
|
|
|
|
case recoveryRequestType:
|
|
|
|
return "RecoveryRequest"
|
|
|
|
case recoveryMessageType:
|
|
|
|
return "RecoveryMessage"
|
|
|
|
default:
|
|
|
|
return fmt.Sprintf("UNKNOWN(0x%02x)", byte(t))
|
|
|
|
}
|
|
|
|
}
|
2020-04-15 15:56:45 +00:00
|
|
|
|
2021-01-14 11:17:00 +00:00
|
|
|
func (p *Payload) encodeData() {
|
|
|
|
if p.Extensible.Data == nil {
|
|
|
|
p.Extensible.ValidBlockStart = 0
|
|
|
|
p.Extensible.ValidBlockEnd = p.BlockIndex
|
|
|
|
bw := io.NewBufBinWriter()
|
|
|
|
p.message.EncodeBinary(bw.BinWriter)
|
|
|
|
p.Extensible.Data = bw.Bytes()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-15 15:56:45 +00:00
|
|
|
// decode data of payload into it's message
|
|
|
|
func (p *Payload) decodeData() error {
|
2021-01-14 11:17:00 +00:00
|
|
|
br := io.NewBinReaderFromBuf(p.Extensible.Data)
|
|
|
|
p.message.DecodeBinary(br)
|
2020-04-15 15:56:45 +00:00
|
|
|
if br.Err != nil {
|
2020-08-06 14:44:08 +00:00
|
|
|
return fmt.Errorf("can't decode message: %w", br.Err)
|
2020-04-15 15:56:45 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|