156 lines
3.4 KiB
Go
156 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))
|
||
|
}
|
||
|
}
|