109 lines
4.0 KiB
Go
109 lines
4.0 KiB
Go
package consensus
|
|
|
|
import (
|
|
"context"
|
|
|
|
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
|
"go.opencensus.io/stats"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
|
|
"github.com/filecoin-project/lotus/api"
|
|
"github.com/filecoin-project/lotus/build"
|
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
"github.com/filecoin-project/lotus/chain/vm"
|
|
"github.com/filecoin-project/lotus/metrics"
|
|
)
|
|
|
|
type Consensus interface {
|
|
// ValidateBlockHeader is called by peers when they receive a new block through the network.
|
|
//
|
|
// This is a fast sanity-check validation performed by the PubSub protocol before delivering
|
|
// it to the syncer. It checks that the block has the right format and it performs
|
|
// other consensus-specific light verifications like ensuring that the block is signed by
|
|
// a valid miner, or that it includes all the data required for a full verification.
|
|
ValidateBlockHeader(ctx context.Context, b *types.BlockHeader) (rejectReason string, err error)
|
|
|
|
// ValidateBlock is called by the syncer to determine if to accept a block or not.
|
|
//
|
|
// It performs all the checks needed by the syncer to accept
|
|
// the block (signature verifications, VRF checks, message validity, etc.)
|
|
ValidateBlock(ctx context.Context, b *types.FullBlock) (err error)
|
|
|
|
// IsEpochBeyondCurrMax is used to configure the fork rules for longest-chain
|
|
// consensus protocols.
|
|
IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool
|
|
|
|
// CreateBlock implements all the logic required to propose and assemble a new Filecoin block.
|
|
//
|
|
// This function encapsulate all the consensus-specific actions to propose a new block
|
|
// such as the ordering of transactions, the inclusion of consensus proofs, the signature
|
|
// of the block, etc.
|
|
CreateBlock(ctx context.Context, w api.Wallet, bt *api.BlockTemplate) (*types.FullBlock, error)
|
|
|
|
// SignBlock determines how should blocks be signed for them to be considered valid by the consensus
|
|
// engine.
|
|
SignBlock(ctx context.Context, w api.Wallet, addr address.Address, next *types.BlockHeader) error
|
|
// VerifyBlockSignature verifies the signature scheme implemented by the consensus algorithm.
|
|
VerifyBlockSignature(ctx context.Context, h *types.BlockHeader, addr address.Address) error
|
|
}
|
|
|
|
// RewardFunc parametrizes the logic for rewards when a message is executed.
|
|
//
|
|
// Each consensus implementation can set their own reward function.
|
|
type RewardFunc func(ctx context.Context, vmi vm.Interface, em stmgr.ExecMonitor,
|
|
epoch abi.ChainEpoch, ts *types.TipSet, params []byte) error
|
|
|
|
// ValidateBlockPubsub implements the common checks performed by all consensus implementations
|
|
// when a block is received through the pubsub channel.
|
|
func ValidateBlockPubsub(ctx context.Context, cns Consensus, self bool, msg *pubsub.Message) (pubsub.ValidationResult, string) {
|
|
if self {
|
|
return validateLocalBlock(ctx, msg)
|
|
}
|
|
|
|
// track validation time
|
|
begin := build.Clock.Now()
|
|
defer func() {
|
|
log.Debugf("block validation time: %s", build.Clock.Since(begin))
|
|
}()
|
|
|
|
stats.Record(ctx, metrics.BlockReceived.M(1))
|
|
|
|
recordFailureFlagPeer := func(what string) {
|
|
// bv.Validate will flag the peer in that case
|
|
panic(what)
|
|
}
|
|
|
|
blk, what, err := decodeAndCheckBlock(msg)
|
|
if err != nil {
|
|
log.Error("got invalid block over pubsub: ", err)
|
|
recordFailureFlagPeer(what)
|
|
return pubsub.ValidationReject, what
|
|
}
|
|
|
|
// validate the block meta: the Message CID in the header must match the included messages
|
|
err = validateMsgMeta(ctx, blk)
|
|
if err != nil {
|
|
log.Warnf("error validating message metadata: %s", err)
|
|
recordFailureFlagPeer("invalid_block_meta")
|
|
return pubsub.ValidationReject, "invalid_block_meta"
|
|
}
|
|
|
|
reject, err := cns.ValidateBlockHeader(ctx, blk.Header)
|
|
if err != nil {
|
|
if reject == "" {
|
|
log.Warn("ignoring block msg: ", err)
|
|
return pubsub.ValidationIgnore, reject
|
|
}
|
|
recordFailureFlagPeer(reject)
|
|
return pubsub.ValidationReject, reject
|
|
}
|
|
|
|
// all good, accept the block
|
|
msg.ValidatorData = blk
|
|
stats.Record(ctx, metrics.BlockValidationSuccess.M(1))
|
|
return pubsub.ValidationAccept, ""
|
|
}
|