neo-go/pkg/consensus/payload.go
2019-11-13 17:27:25 +03:00

155 lines
3.4 KiB
Go

package consensus
import (
"fmt"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/pkg/errors"
)
type (
messageType byte
message struct {
Type messageType
ViewNumber byte
payload io.Serializable
}
// Payload is a type for consensus-related messages.
Payload struct {
message
Version uint32
ValidatorIndex uint16
PrevHash util.Uint256
Height uint32
Timestamp uint32
Witness transaction.Witness
}
)
const (
changeViewType messageType = 0x00
prepareRequestType messageType = 0x20
prepareResponseType messageType = 0x21
commitType messageType = 0x30
recoveryRequestType messageType = 0x40
recoveryMessageType messageType = 0x41
)
// EncodeBinaryUnsigned writes payload to w excluding signature.
func (p *Payload) EncodeBinaryUnsigned(w *io.BinWriter) {
w.WriteLE(p.Version)
w.WriteBE(p.PrevHash[:])
w.WriteLE(p.Height)
w.WriteLE(p.ValidatorIndex)
w.WriteLE(p.Timestamp)
ww := io.NewBufBinWriter()
p.message.EncodeBinary(ww.BinWriter)
w.WriteBytes(ww.Bytes())
}
// EncodeBinary implements io.Serializable interface.
func (p *Payload) EncodeBinary(w *io.BinWriter) {
p.EncodeBinaryUnsigned(w)
w.WriteLE(byte(1))
p.Witness.EncodeBinary(w)
}
// DecodeBinaryUnsigned reads payload from w excluding signature.
func (p *Payload) DecodeBinaryUnsigned(r *io.BinReader) {
r.ReadLE(&p.Version)
r.ReadBE(p.PrevHash[:])
r.ReadLE(&p.Height)
r.ReadLE(&p.ValidatorIndex)
r.ReadLE(&p.Timestamp)
data := r.ReadBytes()
rr := io.NewBinReaderFromBuf(data)
p.message.DecodeBinary(rr)
}
// Hash returns 32-byte message hash.
func (p *Payload) Hash() util.Uint256 {
w := io.NewBufBinWriter()
p.EncodeBinaryUnsigned(w.BinWriter)
return hash.DoubleSha256(w.Bytes())
}
// DecodeBinary implements io.Serializable interface.
func (p *Payload) DecodeBinary(r *io.BinReader) {
p.DecodeBinaryUnsigned(r)
var b byte
r.ReadLE(&b)
if b != 1 {
r.Err = errors.New("invalid format")
return
}
p.Witness.DecodeBinary(r)
}
// EncodeBinary implements io.Serializable interface.
func (m *message) EncodeBinary(w *io.BinWriter) {
w.WriteLE(byte(m.Type))
w.WriteLE(m.ViewNumber)
m.payload.EncodeBinary(w)
}
// DecodeBinary implements io.Serializable interface.
func (m *message) DecodeBinary(r *io.BinReader) {
r.ReadLE((*byte)(&m.Type))
r.ReadLE(&m.ViewNumber)
switch m.Type {
case changeViewType:
cv := new(changeView)
// NewViewNumber is not marshaled
cv.NewViewNumber = m.ViewNumber + 1
m.payload = cv
case prepareRequestType:
m.payload = new(prepareRequest)
case prepareResponseType:
m.payload = new(prepareResponse)
case commitType:
m.payload = new(commit)
case recoveryRequestType:
m.payload = new(recoveryRequest)
case recoveryMessageType:
m.payload = new(recoveryMessage)
default:
r.Err = errors.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))
}
}