2019-07-08 23:48:49 +00:00
package miner
import (
"context"
2019-08-20 16:50:17 +00:00
"sync"
2019-07-08 23:48:49 +00:00
"time"
2019-11-25 04:45:13 +00:00
"github.com/filecoin-project/lotus/api"
2019-10-18 04:47:41 +00:00
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/address"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/types"
2019-08-20 16:50:17 +00:00
logging "github.com/ipfs/go-log"
"go.opencensus.io/trace"
"golang.org/x/xerrors"
2019-07-08 23:48:49 +00:00
)
var log = logging . Logger ( "miner" )
2019-10-09 04:38:59 +00:00
type waitFunc func ( ctx context . Context ) error
2019-09-23 15:27:30 +00:00
2019-11-25 04:45:13 +00:00
func NewMiner ( api api . FullNode , epp gen . ElectionPoStProver ) * Miner {
2019-07-11 02:36:43 +00:00
return & Miner {
2019-09-23 15:27:30 +00:00
api : api ,
2019-11-25 04:45:13 +00:00
epp : epp ,
2019-10-09 04:38:59 +00:00
waitFunc : func ( ctx context . Context ) error {
// Wait around for half the block time in case other parents come in
2019-12-03 04:34:41 +00:00
time . Sleep ( build . PropagationDelay * time . Second )
2019-10-09 04:38:59 +00:00
return nil
} ,
2019-07-11 02:36:43 +00:00
}
2019-07-08 23:48:49 +00:00
}
type Miner struct {
2019-11-25 04:45:13 +00:00
api api . FullNode
2019-07-08 23:48:49 +00:00
2019-11-21 22:21:45 +00:00
epp gen . ElectionPoStProver
2019-08-20 16:50:17 +00:00
lk sync . Mutex
addresses [ ] address . Address
2019-08-20 18:05:17 +00:00
stop chan struct { }
stopping chan struct { }
2019-07-11 02:36:43 +00:00
2019-10-09 04:38:59 +00:00
waitFunc waitFunc
2019-07-08 23:48:49 +00:00
lastWork * MiningBase
}
2019-08-21 15:14:38 +00:00
func ( m * Miner ) Addresses ( ) ( [ ] address . Address , error ) {
m . lk . Lock ( )
defer m . lk . Unlock ( )
out := make ( [ ] address . Address , len ( m . addresses ) )
copy ( out , m . addresses )
return out , nil
}
2019-08-20 16:50:17 +00:00
func ( m * Miner ) Register ( addr address . Address ) error {
m . lk . Lock ( )
defer m . lk . Unlock ( )
if len ( m . addresses ) > 0 {
2019-11-18 21:59:31 +00:00
for _ , a := range m . addresses {
if a == addr {
log . Warnf ( "miner.Register called more than once for actor '%s'" , addr )
return xerrors . Errorf ( "miner.Register called more than once for actor '%s'" , addr )
}
2019-08-20 17:19:24 +00:00
}
2019-08-20 16:50:17 +00:00
}
m . addresses = append ( m . addresses , addr )
2019-11-18 21:59:31 +00:00
if len ( m . addresses ) == 1 {
m . stop = make ( chan struct { } )
go m . mine ( context . TODO ( ) )
}
2019-08-20 16:50:17 +00:00
return nil
}
2019-08-20 18:05:17 +00:00
func ( m * Miner ) Unregister ( ctx context . Context , addr address . Address ) error {
m . lk . Lock ( )
2019-11-19 23:21:54 +00:00
defer m . lk . Unlock ( )
2019-08-20 18:05:17 +00:00
if len ( m . addresses ) == 0 {
return xerrors . New ( "no addresses registered" )
}
2019-11-18 21:59:31 +00:00
idx := - 1
2019-08-20 18:05:17 +00:00
2019-11-18 21:59:31 +00:00
for i , a := range m . addresses {
if a == addr {
idx = i
break
}
}
if idx == - 1 {
2019-08-20 18:05:17 +00:00
return xerrors . New ( "unregister: address not found" )
}
2019-11-18 21:59:31 +00:00
m . addresses [ idx ] = m . addresses [ len ( m . addresses ) - 1 ]
m . addresses = m . addresses [ : len ( m . addresses ) - 1 ]
2019-08-20 18:05:17 +00:00
// Unregistering last address, stop mining first
2019-11-18 21:59:31 +00:00
if len ( m . addresses ) == 0 && m . stop != nil {
2019-11-19 23:21:54 +00:00
m . stopping = make ( chan struct { } )
2019-08-20 18:05:17 +00:00
stopping := m . stopping
2019-11-19 23:21:54 +00:00
close ( m . stop )
2019-08-20 18:05:17 +00:00
select {
case <- stopping :
case <- ctx . Done ( ) :
return ctx . Err ( )
}
}
return nil
}
2019-08-20 16:50:17 +00:00
func ( m * Miner ) mine ( ctx context . Context ) {
2019-07-26 19:01:02 +00:00
ctx , span := trace . StartSpan ( ctx , "/mine" )
defer span . End ( )
2019-08-20 18:05:17 +00:00
2019-10-10 02:03:42 +00:00
var lastBase MiningBase
2019-10-10 00:38:39 +00:00
2019-11-19 19:36:03 +00:00
eventLoop :
2019-07-08 23:48:49 +00:00
for {
2019-08-20 18:05:17 +00:00
select {
case <- m . stop :
2019-11-19 23:21:54 +00:00
stopping := m . stopping
2019-08-20 18:05:17 +00:00
m . stop = nil
m . stopping = nil
2019-11-19 23:21:54 +00:00
close ( stopping )
2019-08-20 18:17:59 +00:00
return
2019-11-19 23:21:54 +00:00
2019-08-20 18:05:17 +00:00
default :
}
2019-10-09 09:18:33 +00:00
2019-11-18 21:59:31 +00:00
m . lk . Lock ( )
addrs := m . addresses
m . lk . Unlock ( )
2019-10-09 09:18:33 +00:00
// Sleep a small amount in order to wait for other blocks to arrive
2019-10-09 04:38:59 +00:00
if err := m . waitFunc ( ctx ) ; err != nil {
log . Error ( err )
return
}
2019-08-20 18:05:17 +00:00
2019-10-15 05:00:30 +00:00
base , err := m . GetBestMiningCandidate ( ctx )
2019-07-08 23:48:49 +00:00
if err != nil {
log . Errorf ( "failed to get best mining candidate: %s" , err )
continue
}
2019-11-19 15:53:00 +00:00
if base . ts . Equals ( lastBase . ts ) && lastBase . nullRounds == base . nullRounds {
2019-12-02 20:04:04 +00:00
log . Warnf ( "BestMiningCandidate from the previous round: %s (nulls:%d)" , lastBase . ts . Cids ( ) , lastBase . nullRounds )
2019-10-10 02:03:42 +00:00
time . Sleep ( build . BlockDelay * time . Second )
2019-10-10 00:38:39 +00:00
continue
}
2019-10-10 02:03:42 +00:00
lastBase = * base
2019-07-08 23:48:49 +00:00
2019-11-18 21:59:31 +00:00
blks := make ( [ ] * types . BlockMsg , 0 )
for _ , addr := range addrs {
b , err := m . mineOne ( ctx , addr , base )
if err != nil {
2019-11-26 15:15:32 +00:00
log . Errorf ( "mining block failed: %+v" , err )
2019-11-18 21:59:31 +00:00
continue
}
2019-11-19 01:05:51 +00:00
if b != nil {
blks = append ( blks , b )
}
2019-07-08 23:48:49 +00:00
}
2019-11-18 21:59:31 +00:00
if len ( blks ) != 0 {
btime := time . Unix ( int64 ( blks [ 0 ] . Header . Timestamp ) , 0 )
2019-10-09 09:11:41 +00:00
if time . Now ( ) . Before ( btime ) {
time . Sleep ( time . Until ( btime ) )
2019-10-10 00:20:47 +00:00
} else {
2019-10-14 13:51:51 +00:00
log . Warnw ( "mined block in the past" , "block-time" , btime ,
"time" , time . Now ( ) , "duration" , time . Now ( ) . Sub ( btime ) )
2019-10-09 09:11:41 +00:00
}
2019-11-19 19:36:03 +00:00
mWon := make ( map [ address . Address ] struct { } )
for _ , b := range blks {
_ , notOk := mWon [ b . Header . Miner ]
if notOk {
log . Errorw ( "2 blocks for the same miner. Throwing hands in the air. Report this. It is important." , "bloks" , blks )
continue eventLoop
}
mWon [ b . Header . Miner ] = struct { } { }
}
2019-11-18 21:59:31 +00:00
for _ , b := range blks {
if err := m . api . SyncSubmitBlock ( ctx , b ) ; err != nil {
log . Errorf ( "failed to submit newly mined block: %s" , err )
}
2019-07-08 23:48:49 +00:00
}
2019-10-09 09:18:33 +00:00
} else {
2019-11-19 15:53:00 +00:00
nextRound := time . Unix ( int64 ( base . ts . MinTimestamp ( ) + uint64 ( build . BlockDelay * base . nullRounds ) ) , 0 )
2019-10-09 09:18:33 +00:00
time . Sleep ( time . Until ( nextRound ) )
2019-07-08 23:48:49 +00:00
}
}
}
type MiningBase struct {
2019-11-19 15:53:00 +00:00
ts * types . TipSet
nullRounds uint64
2019-07-08 23:48:49 +00:00
}
2019-10-15 05:00:30 +00:00
func ( m * Miner ) GetBestMiningCandidate ( ctx context . Context ) ( * MiningBase , error ) {
bts , err := m . api . ChainHead ( ctx )
2019-07-08 23:48:49 +00:00
if err != nil {
return nil , err
}
if m . lastWork != nil {
if m . lastWork . ts . Equals ( bts ) {
return m . lastWork , nil
}
2019-10-15 05:00:30 +00:00
btsw , err := m . api . ChainTipSetWeight ( ctx , bts )
if err != nil {
return nil , err
}
ltsw , err := m . api . ChainTipSetWeight ( ctx , m . lastWork . ts )
if err != nil {
return nil , err
}
if types . BigCmp ( btsw , ltsw ) <= 0 {
2019-07-08 23:48:49 +00:00
return m . lastWork , nil
}
}
2019-12-03 07:58:38 +00:00
m . lastWork = & MiningBase { ts : bts }
return m . lastWork , nil
2019-07-08 23:48:49 +00:00
}
2019-12-02 19:51:41 +00:00
func ( m * Miner ) hasPower ( ctx context . Context , addr address . Address , ts * types . TipSet ) ( bool , error ) {
2019-11-29 20:18:34 +00:00
power , err := m . api . StateMinerPower ( ctx , addr , ts )
if err != nil {
return false , err
}
return power . MinerPower . Equals ( types . NewInt ( 0 ) ) , nil
}
2019-11-18 21:59:31 +00:00
func ( m * Miner ) mineOne ( ctx context . Context , addr address . Address , base * MiningBase ) ( * types . BlockMsg , error ) {
2019-11-07 00:18:06 +00:00
log . Debugw ( "attempting to mine a block" , "tipset" , types . LogCids ( base . ts . Cids ( ) ) )
2019-11-27 14:18:51 +00:00
start := time . Now ( )
2019-12-02 19:51:41 +00:00
hasPower , err := m . hasPower ( ctx , addr , base . ts )
2019-11-29 20:18:34 +00:00
if err != nil {
return nil , xerrors . Errorf ( "checking if miner is slashed: %w" , err )
}
2019-12-02 19:51:41 +00:00
if hasPower {
// slashed or just have no power yet
2019-11-29 20:18:34 +00:00
base . nullRounds ++
return nil , nil
}
2019-11-21 22:21:45 +00:00
ticket , err := m . computeTicket ( ctx , addr , base )
2019-07-08 23:48:49 +00:00
if err != nil {
2019-11-22 16:20:56 +00:00
return nil , xerrors . Errorf ( "scratching ticket failed: %w" , err )
2019-07-08 23:48:49 +00:00
}
2019-12-03 21:27:07 +00:00
proofin , err := gen . IsRoundWinner ( ctx , base . ts , int64 ( base . ts . Height ( ) + base . nullRounds + 1 ) , addr , m . epp , m . api )
2019-07-08 23:48:49 +00:00
if err != nil {
2019-11-25 04:45:13 +00:00
return nil , xerrors . Errorf ( "failed to check if we win next round: %w" , err )
2019-07-08 23:48:49 +00:00
}
2019-12-03 21:27:07 +00:00
if proofin == nil {
2019-11-19 15:53:00 +00:00
base . nullRounds ++
2019-07-08 23:48:49 +00:00
return nil , nil
}
2019-12-03 18:25:56 +00:00
// get pending messages early,
pending , err := m . api . MpoolPending ( context . TODO ( ) , base . ts )
if err != nil {
return nil , xerrors . Errorf ( "failed to get pending messages: %w" , err )
}
proof , err := gen . ComputeProof ( ctx , m . epp , proofin )
if err != nil {
return nil , xerrors . Errorf ( "computing election proof: %w" , err )
}
b , err := m . createBlock ( base , addr , ticket , proof , pending )
2019-07-08 23:48:49 +00:00
if err != nil {
2019-11-22 16:20:56 +00:00
return nil , xerrors . Errorf ( "failed to create block: %w" , err )
2019-07-08 23:48:49 +00:00
}
2019-12-01 22:22:10 +00:00
log . Infow ( "mined new block" , "cid" , b . Cid ( ) , "height" , b . Header . Height )
2019-07-08 23:48:49 +00:00
2019-11-27 14:18:51 +00:00
dur := time . Now ( ) . Sub ( start )
log . Infof ( "Creating block took %s" , dur )
2019-12-03 00:08:08 +00:00
if dur > time . Second * build . BlockDelay {
log . Warn ( "CAUTION: block production took longer than the block delay. Your computer may not be fast enough to keep up" )
}
2019-11-27 14:18:51 +00:00
2019-07-08 23:48:49 +00:00
return b , nil
}
2019-11-18 21:59:31 +00:00
func ( m * Miner ) computeVRF ( ctx context . Context , addr address . Address , input [ ] byte ) ( [ ] byte , error ) {
w , err := m . getMinerWorker ( ctx , addr , nil )
2019-08-15 02:30:21 +00:00
if err != nil {
return nil , err
}
2019-11-21 22:21:45 +00:00
return gen . ComputeVRF ( ctx , m . api . WalletSign , w , addr , gen . DSepTicket , input )
2019-08-15 02:30:21 +00:00
}
func ( m * Miner ) getMinerWorker ( ctx context . Context , addr address . Address , ts * types . TipSet ) ( address . Address , error ) {
2019-09-06 06:26:02 +00:00
ret , err := m . api . StateCall ( ctx , & types . Message {
2019-08-15 02:30:21 +00:00
From : addr ,
To : addr ,
Method : actors . MAMethods . GetWorkerAddr ,
} , ts )
if err != nil {
return address . Undef , xerrors . Errorf ( "failed to get miner worker addr: %w" , err )
}
if ret . ExitCode != 0 {
return address . Undef , xerrors . Errorf ( "failed to get miner worker addr (exit code %d)" , ret . ExitCode )
}
w , err := address . NewFromBytes ( ret . Return )
if err != nil {
return address . Undef , xerrors . Errorf ( "GetWorkerAddr returned malformed address: %w" , err )
}
return w , nil
}
2019-11-21 22:21:45 +00:00
func ( m * Miner ) computeTicket ( ctx context . Context , addr address . Address , base * MiningBase ) ( * types . Ticket , error ) {
2019-08-15 02:30:21 +00:00
2019-11-30 04:31:16 +00:00
vrfBase := base . ts . MinTicket ( ) . VRFProof
2019-11-19 15:53:00 +00:00
vrfOut , err := m . computeVRF ( ctx , addr , vrfBase )
2019-08-15 02:30:21 +00:00
if err != nil {
return nil , err
}
return & types . Ticket {
2019-10-09 04:38:59 +00:00
VRFProof : vrfOut ,
2019-08-15 02:30:21 +00:00
} , nil
2019-07-08 23:48:49 +00:00
}
2019-12-03 18:25:56 +00:00
func ( m * Miner ) createBlock ( base * MiningBase , addr address . Address , ticket * types . Ticket , proof * types . EPostProof , pending [ ] * types . SignedMessage ) ( * types . BlockMsg , error ) {
2019-12-03 20:05:54 +00:00
msgs , err := selectMessages ( context . TODO ( ) , m . api . StateGetActor , base , pending )
2019-09-26 03:48:53 +00:00
if err != nil {
return nil , xerrors . Errorf ( "message filtering failed: %w" , err )
}
2019-07-11 02:36:43 +00:00
2019-11-19 15:53:00 +00:00
uts := base . ts . MinTimestamp ( ) + uint64 ( build . BlockDelay * ( base . nullRounds + 1 ) )
nheight := base . ts . Height ( ) + base . nullRounds + 1
2019-09-06 17:44:09 +00:00
2019-07-08 23:48:49 +00:00
// why even return this? that api call could just submit it for us
2019-11-19 15:53:00 +00:00
return m . api . MinerCreateBlock ( context . TODO ( ) , addr , base . ts , ticket , proof , msgs , nheight , uint64 ( uts ) )
2019-07-08 23:48:49 +00:00
}
2019-12-03 20:05:54 +00:00
type actorLookup func ( context . Context , address . Address , * types . TipSet ) ( * types . Actor , error )
2019-09-26 20:47:34 +00:00
2019-12-03 18:25:56 +00:00
func countFrom ( msgs [ ] * types . SignedMessage , from address . Address ) ( out int ) {
for _ , msg := range msgs {
if msg . Message . From == from {
out ++
}
}
return out
}
2019-09-26 20:47:34 +00:00
func selectMessages ( ctx context . Context , al actorLookup , base * MiningBase , msgs [ ] * types . SignedMessage ) ( [ ] * types . SignedMessage , error ) {
2019-09-26 03:48:53 +00:00
out := make ( [ ] * types . SignedMessage , 0 , len ( msgs ) )
2019-09-26 03:53:52 +00:00
inclNonces := make ( map [ address . Address ] uint64 )
2019-09-26 20:47:34 +00:00
inclBalances := make ( map [ address . Address ] types . BigInt )
2019-12-03 18:25:56 +00:00
2019-09-26 03:48:53 +00:00
for _ , msg := range msgs {
2019-10-14 03:28:19 +00:00
if msg . Message . To == address . Undef {
log . Warnf ( "message in mempool had bad 'To' address" )
continue
}
2019-09-26 03:53:52 +00:00
from := msg . Message . From
2019-09-26 03:48:53 +00:00
2019-09-26 03:53:52 +00:00
if _ , ok := inclNonces [ from ] ; ! ok {
2019-12-03 20:05:54 +00:00
act , err := al ( ctx , from , base . ts )
2019-12-03 06:41:28 +00:00
if err != nil {
return nil , xerrors . Errorf ( "failed to check message sender balance: %w" , err )
}
2019-12-03 20:05:54 +00:00
inclNonces [ from ] = act . Nonce
inclBalances [ from ] = act . Balance
2019-09-26 03:53:52 +00:00
}
2019-09-26 20:47:34 +00:00
if inclBalances [ from ] . LessThan ( msg . Message . RequiredFunds ( ) ) {
log . Warnf ( "message in mempool does not have enough funds: %s" , msg . Cid ( ) )
2019-09-26 03:48:53 +00:00
continue
}
2019-09-26 03:53:52 +00:00
if msg . Message . Nonce > inclNonces [ from ] {
2019-12-03 18:25:56 +00:00
log . Warnf ( "message in mempool has too high of a nonce (%d > %d) %s (%d pending for orig)" , msg . Message . Nonce , inclNonces [ from ] , msg . Cid ( ) , countFrom ( msgs , from ) )
2019-09-26 20:47:34 +00:00
continue
}
if msg . Message . Nonce < inclNonces [ from ] {
2019-12-03 18:25:56 +00:00
log . Warnf ( "message in mempool has already used nonce (%d < %d), from %s, to %s, %s (%d pending for)" , msg . Message . Nonce , inclNonces [ from ] , msg . Message . From , msg . Message . To , msg . Cid ( ) , countFrom ( msgs , from ) )
2019-09-26 03:53:52 +00:00
continue
}
2019-09-26 20:47:34 +00:00
inclNonces [ from ] = msg . Message . Nonce + 1
inclBalances [ from ] = types . BigSub ( inclBalances [ from ] , msg . Message . RequiredFunds ( ) )
2019-09-26 03:53:52 +00:00
2019-09-26 03:48:53 +00:00
out = append ( out , msg )
}
return out , nil
2019-07-08 23:48:49 +00:00
}