neo-go/pkg/consensus/payload.go
Roman Khimov ee0d92c6d2 dbft: update to AMEV-enabled version
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-01 22:29:51 +03:00

235 lines
6.2 KiB
Go

package consensus
import (
"fmt"
"github.com/nspcc-dev/dbft"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/io"
npayload "github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
type (
messageType byte
message struct {
Type messageType
BlockIndex uint32
ValidatorIndex byte
ViewNumber byte
payload io.Serializable
// stateRootEnabled specifies if state root is exchanged during consensus.
stateRootEnabled bool
}
// Payload is a type for consensus-related messages.
Payload struct {
npayload.Extensible
message
network netmode.Magic
}
)
const (
changeViewType messageType = 0x00
prepareRequestType messageType = 0x20
prepareResponseType messageType = 0x21
commitType messageType = 0x30
recoveryRequestType messageType = 0x40
recoveryMessageType messageType = 0x41
payloadGasLimit = 2000000 // 0.02 GAS
)
var _ dbft.ConsensusPayload[util.Uint256] = &Payload{}
// ViewNumber implements the payload.ConsensusPayload interface.
func (p Payload) ViewNumber() byte {
return p.message.ViewNumber
}
// SetViewNumber implements the payload.ConsensusPayload interface.
func (p *Payload) SetViewNumber(view byte) {
p.message.ViewNumber = view
}
// Type implements the payload.ConsensusPayload interface.
func (p Payload) Type() dbft.MessageType {
return dbft.MessageType(p.message.Type)
}
// Payload implements the payload.ConsensusPayload interface.
func (p Payload) Payload() any {
return p.payload
}
// GetChangeView implements the payload.ConsensusPayload interface.
func (p Payload) GetChangeView() dbft.ChangeView { return p.payload.(dbft.ChangeView) }
// GetPrepareRequest implements the payload.ConsensusPayload interface.
func (p Payload) GetPrepareRequest() dbft.PrepareRequest[util.Uint256] {
return p.payload.(dbft.PrepareRequest[util.Uint256])
}
// GetPrepareResponse implements the payload.ConsensusPayload interface.
func (p Payload) GetPrepareResponse() dbft.PrepareResponse[util.Uint256] {
return p.payload.(dbft.PrepareResponse[util.Uint256])
}
// GetPreCommit implements the payload.ConsensusPayload interface.
// It's a stub since PreCommits are never used on N3.
func (p Payload) GetPreCommit() dbft.PreCommit { return nil }
// GetCommit implements the payload.ConsensusPayload interface.
func (p Payload) GetCommit() dbft.Commit { return p.payload.(dbft.Commit) }
// GetRecoveryRequest implements the payload.ConsensusPayload interface.
func (p Payload) GetRecoveryRequest() dbft.RecoveryRequest {
return p.payload.(dbft.RecoveryRequest)
}
// GetRecoveryMessage implements the payload.ConsensusPayload interface.
func (p Payload) GetRecoveryMessage() dbft.RecoveryMessage[util.Uint256] {
return p.payload.(dbft.RecoveryMessage[util.Uint256])
}
// ValidatorIndex implements the payload.ConsensusPayload interface.
func (p Payload) ValidatorIndex() uint16 {
return uint16(p.message.ValidatorIndex)
}
// SetValidatorIndex implements the payload.ConsensusPayload interface.
func (p *Payload) SetValidatorIndex(i uint16) {
p.message.ValidatorIndex = byte(i)
}
// Height implements the payload.ConsensusPayload interface.
func (p Payload) Height() uint32 {
return p.message.BlockIndex
}
// EncodeBinary implements the io.Serializable interface.
func (p *Payload) EncodeBinary(w *io.BinWriter) {
p.encodeData()
p.Extensible.EncodeBinary(w)
}
// Sign signs payload using the private key.
// It also sets corresponding verification and invocation scripts.
func (p *Payload) Sign(key *privateKey) error {
p.encodeData()
sig := key.PrivateKey.SignHashable(uint32(p.network), &p.Extensible)
buf := io.NewBufBinWriter()
emit.Bytes(buf.BinWriter, sig)
p.Witness.InvocationScript = buf.Bytes()
p.Witness.VerificationScript = key.PublicKey().GetVerificationScript()
return nil
}
// Hash implements the payload.ConsensusPayload interface.
func (p *Payload) Hash() util.Uint256 {
if p.Extensible.Data == nil {
p.encodeData()
}
return p.Extensible.Hash()
}
// DecodeBinary implements the io.Serializable interface.
func (p *Payload) DecodeBinary(r *io.BinReader) {
p.Extensible.DecodeBinary(r)
if r.Err == nil {
r.Err = p.decodeData()
}
}
// EncodeBinary implements the io.Serializable interface.
func (m *message) EncodeBinary(w *io.BinWriter) {
w.WriteB(byte(m.Type))
w.WriteU32LE(m.BlockIndex)
w.WriteB(m.ValidatorIndex)
w.WriteB(m.ViewNumber)
m.payload.EncodeBinary(w)
}
// DecodeBinary implements the io.Serializable interface.
func (m *message) DecodeBinary(r *io.BinReader) {
m.Type = messageType(r.ReadB())
m.BlockIndex = r.ReadU32LE()
m.ValidatorIndex = r.ReadB()
m.ViewNumber = r.ReadB()
switch m.Type {
case changeViewType:
cv := new(changeView)
// newViewNumber is not marshaled
cv.newViewNumber = m.ViewNumber + 1
m.payload = cv
case prepareRequestType:
r := new(prepareRequest)
if m.stateRootEnabled {
r.stateRootEnabled = true
}
m.payload = r
case prepareResponseType:
m.payload = new(prepareResponse)
case commitType:
m.payload = new(commit)
case recoveryRequestType:
m.payload = new(recoveryRequest)
case recoveryMessageType:
r := new(recoveryMessage)
if m.stateRootEnabled {
r.stateRootEnabled = true
}
m.payload = r
default:
r.Err = fmt.Errorf("invalid type: 0x%02x", byte(m.Type))
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))
}
}
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()
}
}
// decode data of payload into its message.
func (p *Payload) decodeData() error {
br := io.NewBinReaderFromBuf(p.Extensible.Data)
p.message.DecodeBinary(br)
if br.Err != nil {
return fmt.Errorf("can't decode message: %w", br.Err)
}
return nil
}