2022-07-04 04:49:07 +03:00
package core
import (
"fmt"
"math/big"
"math/rand"
"sync"
"time"
lru "github.com/hashicorp/golang-lru"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
)
const (
verifiedCacheSize = 256
maxForkHeight = 11
// defaultPeerNumber is default number of verify peers
defaultPeerNumber = 3
// pruneHeightDiff indicates that if the height difference between current block and task's
// corresponding block is larger than it, the task should be pruned.
pruneHeightDiff = 15
pruneInterval = 5 * time . Second
resendInterval = 2 * time . Second
// tryAllPeersTime is the time that a block has not been verified and then try all the valid verify peers.
tryAllPeersTime = 15 * time . Second
// maxWaitVerifyResultTime is the max time of waiting for ancestor's verify result.
maxWaitVerifyResultTime = 30 * time . Second
)
var (
verifyTaskCounter = metrics . NewRegisteredCounter ( "verifymanager/task/total" , nil )
verifyTaskSucceedMeter = metrics . NewRegisteredMeter ( "verifymanager/task/result/succeed" , nil )
verifyTaskFailedMeter = metrics . NewRegisteredMeter ( "verifymanager/task/result/failed" , nil )
verifyTaskExecutionTimer = metrics . NewRegisteredTimer ( "verifymanager/task/execution" , nil )
)
type remoteVerifyManager struct {
bc * BlockChain
taskLock sync . RWMutex
tasks map [ common . Hash ] * verifyTask
peers verifyPeers
verifiedCache * lru . Cache
allowInsecure bool
// Subscription
chainBlockCh chan ChainHeadEvent
chainHeadSub event . Subscription
// Channels
verifyCh chan common . Hash
messageCh chan verifyMessage
}
func NewVerifyManager ( blockchain * BlockChain , peers verifyPeers , allowInsecure bool ) ( * remoteVerifyManager , error ) {
verifiedCache , _ := lru . New ( verifiedCacheSize )
block := blockchain . CurrentBlock ( )
if block == nil {
return nil , ErrCurrentBlockNotFound
}
// rewind to last non verified block
number := new ( big . Int ) . Sub ( block . Number ( ) , big . NewInt ( int64 ( maxForkHeight ) ) )
if number . Cmp ( common . Big0 ) < 0 {
blockchain . SetHead ( 0 )
} else {
numberU64 := number . Uint64 ( )
blockchain . SetHead ( numberU64 )
block := blockchain . GetBlockByNumber ( numberU64 )
for i := 0 ; i < maxForkHeight && block . NumberU64 ( ) > 0 ; i ++ {
// When inserting a block,
// the block before 11 blocks will be verified,
// so the parent block of 11-22 will directly write the verification information.
verifiedCache . Add ( block . Hash ( ) , true )
block = blockchain . GetBlockByHash ( block . ParentHash ( ) )
if block == nil {
return nil , fmt . Errorf ( "block is nil, number: %d" , number )
}
}
}
vm := & remoteVerifyManager {
bc : blockchain ,
tasks : make ( map [ common . Hash ] * verifyTask ) ,
peers : peers ,
verifiedCache : verifiedCache ,
allowInsecure : allowInsecure ,
chainBlockCh : make ( chan ChainHeadEvent , chainHeadChanSize ) ,
verifyCh : make ( chan common . Hash , maxForkHeight ) ,
messageCh : make ( chan verifyMessage ) ,
}
vm . chainHeadSub = blockchain . SubscribeChainBlockEvent ( vm . chainBlockCh )
return vm , nil
}
func ( vm * remoteVerifyManager ) mainLoop ( ) {
defer vm . chainHeadSub . Unsubscribe ( )
pruneTicker := time . NewTicker ( pruneInterval )
defer pruneTicker . Stop ( )
for {
select {
case h := <- vm . chainBlockCh :
vm . NewBlockVerifyTask ( h . Block . Header ( ) )
case hash := <- vm . verifyCh :
vm . cacheBlockVerified ( hash )
vm . taskLock . Lock ( )
if task , ok := vm . tasks [ hash ] ; ok {
vm . CloseTask ( task )
verifyTaskSucceedMeter . Mark ( 1 )
verifyTaskExecutionTimer . Update ( time . Since ( task . startAt ) )
}
vm . taskLock . Unlock ( )
case <- pruneTicker . C :
vm . taskLock . Lock ( )
for _ , task := range vm . tasks {
if vm . bc . insertStopped ( ) || ( vm . bc . CurrentHeader ( ) . Number . Cmp ( task . blockHeader . Number ) == 1 &&
vm . bc . CurrentHeader ( ) . Number . Uint64 ( ) - task . blockHeader . Number . Uint64 ( ) > pruneHeightDiff ) {
vm . CloseTask ( task )
verifyTaskFailedMeter . Mark ( 1 )
}
}
vm . taskLock . Unlock ( )
case message := <- vm . messageCh :
vm . taskLock . RLock ( )
if vt , ok := vm . tasks [ message . verifyResult . BlockHash ] ; ok {
vt . messageCh <- message
}
vm . taskLock . RUnlock ( )
// System stopped
case <- vm . bc . quit :
vm . taskLock . RLock ( )
for _ , task := range vm . tasks {
task . Close ( )
}
vm . taskLock . RUnlock ( )
return
case <- vm . chainHeadSub . Err ( ) :
return
}
}
}
func ( vm * remoteVerifyManager ) NewBlockVerifyTask ( header * types . Header ) {
for i := 0 ; header != nil && i <= maxForkHeight ; i ++ {
// if is genesis block, mark it as verified and break.
if header . Number . Uint64 ( ) == 0 {
vm . cacheBlockVerified ( header . Hash ( ) )
break
}
func ( hash common . Hash ) {
// if verified cache record that this block has been verified, skip.
if _ , ok := vm . verifiedCache . Get ( hash ) ; ok {
return
}
// if there already has a verify task for this block, skip.
vm . taskLock . RLock ( )
_ , ok := vm . tasks [ hash ]
vm . taskLock . RUnlock ( )
if ok {
return
}
if header . TxHash == types . EmptyRootHash {
log . Debug ( "this is an empty block:" , "block" , hash , "number" , header . Number )
vm . cacheBlockVerified ( hash )
return
}
var diffLayer * types . DiffLayer
if cached , ok := vm . bc . diffLayerChanCache . Get ( hash ) ; ok {
diffLayerCh := cached . ( chan struct { } )
<- diffLayerCh
diffLayer = vm . bc . GetTrustedDiffLayer ( hash )
}
// if this block has no diff, there is no need to verify it.
if diffLayer == nil {
log . Info ( "block's trusted diffLayer is nil" , "hash" , hash , "number" , header . Number )
return
}
diffHash , err := CalculateDiffHash ( diffLayer )
if err != nil {
log . Error ( "failed to get diff hash" , "block" , hash , "number" , header . Number , "error" , err )
return
}
verifyTask := NewVerifyTask ( diffHash , header , vm . peers , vm . verifyCh , vm . allowInsecure )
vm . taskLock . Lock ( )
vm . tasks [ hash ] = verifyTask
vm . taskLock . Unlock ( )
verifyTaskCounter . Inc ( 1 )
} ( header . Hash ( ) )
header = vm . bc . GetHeaderByHash ( header . ParentHash )
}
}
func ( vm * remoteVerifyManager ) cacheBlockVerified ( hash common . Hash ) {
if vm . verifiedCache . Len ( ) >= verifiedCacheSize {
vm . verifiedCache . RemoveOldest ( )
}
vm . verifiedCache . Add ( hash , true )
}
// AncestorVerified function check block has been verified or it's a empty block.
func ( vm * remoteVerifyManager ) AncestorVerified ( header * types . Header ) bool {
// find header of H-11 block.
header = vm . bc . GetHeaderByNumber ( header . Number . Uint64 ( ) - maxForkHeight )
2022-07-22 10:54:06 +03:00
// If start from genesis block, there has not a H-11 block,return true.
// Either if the block is an empty block, return true.
if header == nil || header . TxHash == types . EmptyRootHash {
2022-07-04 04:49:07 +03:00
return true
}
hash := header . Hash ( )
// Check if the task is complete
vm . taskLock . RLock ( )
task , exist := vm . tasks [ hash ]
vm . taskLock . RUnlock ( )
timeout := time . NewTimer ( maxWaitVerifyResultTime )
defer timeout . Stop ( )
if exist {
select {
case <- task . terminalCh :
case <- timeout . C :
return false
}
}
_ , exist = vm . verifiedCache . Get ( hash )
return exist
}
func ( vm * remoteVerifyManager ) HandleRootResponse ( vr * VerifyResult , pid string ) error {
vm . messageCh <- verifyMessage { verifyResult : vr , peerId : pid }
return nil
}
func ( vm * remoteVerifyManager ) CloseTask ( task * verifyTask ) {
delete ( vm . tasks , task . blockHeader . Hash ( ) )
task . Close ( )
verifyTaskCounter . Dec ( 1 )
}
type VerifyResult struct {
Status types . VerifyStatus
BlockNumber uint64
BlockHash common . Hash
Root common . Hash
}
type verifyMessage struct {
verifyResult * VerifyResult
peerId string
}
type verifyTask struct {
diffhash common . Hash
blockHeader * types . Header
candidatePeers verifyPeers
badPeers map [ string ] struct { }
startAt time . Time
allowInsecure bool
messageCh chan verifyMessage
terminalCh chan struct { }
}
func NewVerifyTask ( diffhash common . Hash , header * types . Header , peers verifyPeers , verifyCh chan common . Hash , allowInsecure bool ) * verifyTask {
vt := & verifyTask {
diffhash : diffhash ,
blockHeader : header ,
candidatePeers : peers ,
badPeers : make ( map [ string ] struct { } ) ,
allowInsecure : allowInsecure ,
messageCh : make ( chan verifyMessage ) ,
terminalCh : make ( chan struct { } ) ,
}
go vt . Start ( verifyCh )
return vt
}
func ( vt * verifyTask ) Close ( ) {
// It is safe to call close multiple
select {
case <- vt . terminalCh :
default :
close ( vt . terminalCh )
}
}
func ( vt * verifyTask ) Start ( verifyCh chan common . Hash ) {
vt . startAt = time . Now ( )
vt . sendVerifyRequest ( defaultPeerNumber )
resend := time . NewTicker ( resendInterval )
defer resend . Stop ( )
for {
select {
case msg := <- vt . messageCh :
switch msg . verifyResult . Status {
case types . StatusFullVerified :
vt . compareRootHashAndMark ( msg , verifyCh )
case types . StatusPartiallyVerified :
log . Warn ( "block is insecure verified" , "hash" , msg . verifyResult . BlockHash , "number" , msg . verifyResult . BlockNumber )
if vt . allowInsecure {
vt . compareRootHashAndMark ( msg , verifyCh )
}
case types . StatusDiffHashMismatch , types . StatusImpossibleFork , types . StatusUnexpectedError :
vt . badPeers [ msg . peerId ] = struct { } { }
log . Info ( "peer is not available" , "hash" , msg . verifyResult . BlockHash , "number" , msg . verifyResult . BlockNumber , "peer" , msg . peerId , "reason" , msg . verifyResult . Status . Msg )
case types . StatusBlockTooNew , types . StatusBlockNewer , types . StatusPossibleFork :
log . Info ( "return msg from peer" , "peerId" , msg . peerId , "hash" , msg . verifyResult . BlockHash , "msg" , msg . verifyResult . Status . Msg )
}
newVerifyMsgTypeGauge ( msg . verifyResult . Status . Code , msg . peerId ) . Inc ( 1 )
case <- resend . C :
// if a task has run over 15s, try all the vaild peers to verify.
if time . Since ( vt . startAt ) < tryAllPeersTime {
vt . sendVerifyRequest ( 1 )
} else {
vt . sendVerifyRequest ( - 1 )
}
case <- vt . terminalCh :
return
}
}
}
// sendVerifyRequest func select at most n peers from (candidatePeers-badPeers) randomly and send verify request.
// when n<0, send to all the peers exclude badPeers.
func ( vt * verifyTask ) sendVerifyRequest ( n int ) {
var validPeers [ ] VerifyPeer
candidatePeers := vt . candidatePeers . GetVerifyPeers ( )
for _ , p := range candidatePeers {
if _ , ok := vt . badPeers [ p . ID ( ) ] ; ! ok {
validPeers = append ( validPeers , p )
}
}
// if has not valid peer, log warning.
if len ( validPeers ) == 0 {
log . Warn ( "there is no valid peer for block" , "number" , vt . blockHeader . Number )
return
}
if n < len ( validPeers ) && n > 0 {
rand . Seed ( time . Now ( ) . UnixNano ( ) )
rand . Shuffle ( len ( validPeers ) , func ( i , j int ) { validPeers [ i ] , validPeers [ j ] = validPeers [ j ] , validPeers [ i ] } )
} else {
n = len ( validPeers )
}
for i := 0 ; i < n ; i ++ {
p := validPeers [ i ]
p . RequestRoot ( vt . blockHeader . Number . Uint64 ( ) , vt . blockHeader . Hash ( ) , vt . diffhash )
}
}
func ( vt * verifyTask ) compareRootHashAndMark ( msg verifyMessage , verifyCh chan common . Hash ) {
if msg . verifyResult . Root == vt . blockHeader . Root {
// write back to manager so that manager can cache the result and delete this task.
verifyCh <- msg . verifyResult . BlockHash
} else {
vt . badPeers [ msg . peerId ] = struct { } { }
}
}
type VerifyPeer interface {
RequestRoot ( blockNumber uint64 , blockHash common . Hash , diffHash common . Hash ) error
ID ( ) string
}
type verifyPeers interface {
GetVerifyPeers ( ) [ ] VerifyPeer
}
type VerifyMode uint32
const (
LocalVerify VerifyMode = iota
FullVerify
InsecureVerify
NoneVerify
)
func ( mode VerifyMode ) IsValid ( ) bool {
return mode >= LocalVerify && mode <= NoneVerify
}
func ( mode VerifyMode ) String ( ) string {
switch mode {
case LocalVerify :
return "local"
case FullVerify :
return "full"
case InsecureVerify :
return "insecure"
case NoneVerify :
return "none"
default :
return "unknown"
}
}
func ( mode VerifyMode ) MarshalText ( ) ( [ ] byte , error ) {
switch mode {
case LocalVerify :
return [ ] byte ( "local" ) , nil
case FullVerify :
return [ ] byte ( "full" ) , nil
case InsecureVerify :
return [ ] byte ( "insecure" ) , nil
case NoneVerify :
return [ ] byte ( "none" ) , nil
default :
return nil , fmt . Errorf ( "unknown verify mode %d" , mode )
}
}
func ( mode * VerifyMode ) UnmarshalText ( text [ ] byte ) error {
switch string ( text ) {
case "local" :
* mode = LocalVerify
case "full" :
* mode = FullVerify
case "insecure" :
* mode = InsecureVerify
case "none" :
* mode = NoneVerify
default :
return fmt . Errorf ( ` unknown sync mode %q, want "full", "light" or "insecure" ` , text )
}
return nil
}
func ( mode VerifyMode ) NeedRemoteVerify ( ) bool {
return mode == FullVerify || mode == InsecureVerify
}
func newVerifyMsgTypeGauge ( msgType uint16 , peerId string ) metrics . Gauge {
m := fmt . Sprintf ( "verifymanager/message/%d/peer/%s" , msgType , peerId )
return metrics . GetOrRegisterGauge ( m , nil )
}