mirror of https://github.com/poanetwork/quorum.git
198 lines
4.8 KiB
Go
198 lines
4.8 KiB
Go
// Copyright 2017 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package core
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/consensus/istanbul"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
)
|
|
|
|
type Engine interface {
|
|
Start(lastSequence *big.Int, lastProposer common.Address, lastProposal istanbul.Proposal) error
|
|
Stop() error
|
|
}
|
|
|
|
type State uint64
|
|
|
|
const (
|
|
StateAcceptRequest State = iota
|
|
StatePreprepared
|
|
StatePrepared
|
|
StateCommitted
|
|
)
|
|
|
|
func (s State) String() string {
|
|
if s == StateAcceptRequest {
|
|
return "Accept request"
|
|
} else if s == StatePreprepared {
|
|
return "Preprepared"
|
|
} else if s == StatePrepared {
|
|
return "Prepared"
|
|
} else if s == StateCommitted {
|
|
return "Committed"
|
|
} else {
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// Cmp compares s and y and returns:
|
|
// -1 if s is the previous state of y
|
|
// 0 if s and y are the same state
|
|
// +1 if s is the next state of y
|
|
func (s State) Cmp(y State) int {
|
|
if uint64(s) < uint64(y) {
|
|
return -1
|
|
}
|
|
if uint64(s) > uint64(y) {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
const (
|
|
msgPreprepare uint64 = iota
|
|
msgPrepare
|
|
msgCommit
|
|
msgRoundChange
|
|
msgAll
|
|
)
|
|
|
|
type message struct {
|
|
Code uint64
|
|
Msg []byte
|
|
Address common.Address
|
|
Signature []byte
|
|
CommittedSeal []byte
|
|
}
|
|
|
|
// ==============================================
|
|
//
|
|
// define the functions that needs to be provided for rlp Encoder/Decoder.
|
|
|
|
// EncodeRLP serializes m into the Ethereum RLP format.
|
|
func (m *message) EncodeRLP(w io.Writer) error {
|
|
return rlp.Encode(w, []interface{}{m.Code, m.Msg, m.Address, m.Signature, m.CommittedSeal})
|
|
}
|
|
|
|
// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream.
|
|
func (m *message) DecodeRLP(s *rlp.Stream) error {
|
|
var msg struct {
|
|
Code uint64
|
|
Msg []byte
|
|
Address common.Address
|
|
Signature []byte
|
|
CommittedSeal []byte
|
|
}
|
|
|
|
if err := s.Decode(&msg); err != nil {
|
|
return err
|
|
}
|
|
m.Code, m.Msg, m.Address, m.Signature, m.CommittedSeal = msg.Code, msg.Msg, msg.Address, msg.Signature, msg.CommittedSeal
|
|
return nil
|
|
}
|
|
|
|
// ==============================================
|
|
//
|
|
// define the functions that needs to be provided for core.
|
|
|
|
func (m *message) FromPayload(b []byte, validateFn func([]byte, []byte) (common.Address, error)) error {
|
|
// Decode message
|
|
err := rlp.DecodeBytes(b, &m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Validate message (on a message without Signature)
|
|
if validateFn != nil {
|
|
var payload []byte
|
|
payload, err = m.PayloadNoSig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = validateFn(payload, m.Signature)
|
|
}
|
|
// Still return the message even the err is not nil
|
|
return err
|
|
}
|
|
|
|
func (m *message) Payload() ([]byte, error) {
|
|
return rlp.EncodeToBytes(m)
|
|
}
|
|
|
|
func (m *message) PayloadNoSig() ([]byte, error) {
|
|
return rlp.EncodeToBytes(&message{
|
|
Code: m.Code,
|
|
Msg: m.Msg,
|
|
Address: m.Address,
|
|
Signature: []byte{},
|
|
CommittedSeal: m.CommittedSeal,
|
|
})
|
|
}
|
|
|
|
func (m *message) Decode(val interface{}) error {
|
|
return rlp.DecodeBytes(m.Msg, val)
|
|
}
|
|
|
|
func (m *message) String() string {
|
|
return fmt.Sprintf("{Code: %v, Address: %v}", m.Code, m.Address.String())
|
|
}
|
|
|
|
// ==============================================
|
|
//
|
|
// helper functions
|
|
|
|
func Encode(val interface{}) ([]byte, error) {
|
|
return rlp.EncodeToBytes(val)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type roundChange struct {
|
|
Round *big.Int
|
|
Sequence *big.Int
|
|
Digest common.Hash
|
|
}
|
|
|
|
// EncodeRLP serializes rc into the Ethereum RLP format.
|
|
func (rc *roundChange) EncodeRLP(w io.Writer) error {
|
|
return rlp.Encode(w, []interface{}{
|
|
rc.Round,
|
|
rc.Sequence,
|
|
rc.Digest,
|
|
})
|
|
}
|
|
|
|
// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream.
|
|
func (rc *roundChange) DecodeRLP(s *rlp.Stream) error {
|
|
var rawRoundChange struct {
|
|
Round *big.Int
|
|
Sequence *big.Int
|
|
Digest common.Hash
|
|
}
|
|
if err := s.Decode(&rawRoundChange); err != nil {
|
|
return err
|
|
}
|
|
rc.Round, rc.Sequence, rc.Digest = rawRoundChange.Round, rawRoundChange.Sequence, rawRoundChange.Digest
|
|
return nil
|
|
}
|