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-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"
"github.com/filecoin-project/lotus/node/impl/full"
2019-08-20 16:50:17 +00:00
logging "github.com/ipfs/go-log"
"go.opencensus.io/trace"
"go.uber.org/fx"
"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-08-20 16:50:17 +00:00
type api struct {
fx . In
2019-07-08 23:48:49 +00:00
2019-08-20 16:50:17 +00:00
full . ChainAPI
2019-10-14 14:21:37 +00:00
full . SyncAPI
2019-08-20 16:50:17 +00:00
full . MpoolAPI
full . WalletAPI
2019-08-21 16:31:14 +00:00
full . StateAPI
2019-07-11 02:36:43 +00:00
}
2019-08-20 16:50:17 +00:00
func NewMiner ( api api ) * Miner {
2019-07-11 02:36:43 +00:00
return & Miner {
2019-09-23 15:27:30 +00:00
api : api ,
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
time . Sleep ( build . BlockDelay * time . Second / 2 )
return nil
} ,
2019-07-11 02:36:43 +00:00
}
2019-07-08 23:48:49 +00:00
}
type Miner struct {
api api
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-10-10 02:03:42 +00:00
if base . ts . Equals ( lastBase . ts ) && len ( lastBase . tickets ) == len ( base . tickets ) {
2019-10-15 05:07:28 +00:00
log . Errorf ( "BestMiningCandidate from the previous round: %s (tkts:%d)" , lastBase . ts . Cids ( ) , len ( lastBase . tickets ) )
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 {
log . Errorf ( "mining block failed: %s" , err )
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 {
nextRound := time . Unix ( int64 ( base . ts . MinTimestamp ( ) + uint64 ( build . BlockDelay * len ( base . tickets ) ) ) , 0 )
time . Sleep ( time . Until ( nextRound ) )
2019-07-08 23:48:49 +00:00
}
}
}
type MiningBase struct {
2019-07-26 04:54:22 +00:00
ts * types . TipSet
2019-08-15 02:30:21 +00:00
tickets [ ] * types . Ticket
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
}
}
return & MiningBase {
ts : bts ,
} , 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-18 21:59:31 +00:00
ticket , err := m . scratchTicket ( 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-11-18 21:59:31 +00:00
win , proof , err := gen . IsRoundWinner ( ctx , base . ts , append ( base . tickets , ticket ) , addr , & m . api )
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 check if we win next round: %w" , err )
2019-07-08 23:48:49 +00:00
}
if ! win {
m . submitNullTicket ( base , ticket )
return nil , nil
}
2019-11-18 21:59:31 +00:00
b , err := m . createBlock ( base , addr , ticket , proof )
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-10-10 23:41:48 +00:00
log . Infow ( "mined new block" , "cid" , b . Cid ( ) )
2019-07-08 23:48:49 +00:00
return b , nil
}
2019-08-15 02:30:21 +00:00
func ( m * Miner ) submitNullTicket ( base * MiningBase , ticket * types . Ticket ) {
2019-07-08 23:48:49 +00:00
base . tickets = append ( base . tickets , ticket )
m . lastWork = base
}
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-08-16 04:40:59 +00:00
return gen . ComputeVRF ( ctx , m . api . WalletSign , w , 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-18 21:59:31 +00:00
func ( m * Miner ) scratchTicket ( ctx context . Context , addr address . Address , base * MiningBase ) ( * types . Ticket , error ) {
2019-08-15 02:30:21 +00:00
var lastTicket * types . Ticket
if len ( base . tickets ) > 0 {
lastTicket = base . tickets [ len ( base . tickets ) - 1 ]
} else {
2019-08-16 00:17:09 +00:00
lastTicket = base . ts . MinTicket ( )
2019-08-15 02:30:21 +00:00
}
2019-11-18 21:59:31 +00:00
vrfOut , err := m . computeVRF ( ctx , addr , lastTicket . VRFProof )
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-11-18 21:59:31 +00:00
func ( m * Miner ) createBlock ( base * MiningBase , addr address . Address , ticket * types . Ticket , proof types . ElectionProof ) ( * types . BlockMsg , error ) {
2019-07-08 23:48:49 +00:00
2019-07-11 02:36:43 +00:00
pending , err := m . api . MpoolPending ( context . TODO ( ) , base . ts )
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 get pending messages: %w" , err )
2019-07-08 23:48:49 +00:00
}
2019-09-26 20:47:34 +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-10-09 04:38:59 +00:00
uts := base . ts . MinTimestamp ( ) + uint64 ( build . BlockDelay * ( len ( base . tickets ) + 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-18 21:59:31 +00:00
return m . api . MinerCreateBlock ( context . TODO ( ) , addr , base . ts , append ( base . tickets , ticket ) , proof , msgs , uint64 ( uts ) )
2019-07-08 23:48:49 +00:00
}
2019-09-26 20:47:34 +00:00
type actorLookup func ( context . Context , address . Address , * types . TipSet ) ( * types . Actor , error )
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-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 20:47:34 +00:00
act , err := al ( ctx , from , base . ts )
2019-09-26 03:48:53 +00:00
if err != nil {
return nil , xerrors . Errorf ( "failed to check message sender balance: %w" , err )
}
2019-09-26 03:53:52 +00:00
if _ , ok := inclNonces [ from ] ; ! ok {
inclNonces [ from ] = act . Nonce
2019-09-26 20:47:34 +00:00
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-09-26 20:47:34 +00:00
log . Warnf ( "message in mempool has too high of a nonce (%d > %d) %s" , msg . Message . Nonce , inclNonces [ from ] , msg . Cid ( ) )
continue
}
if msg . Message . Nonce < inclNonces [ from ] {
2019-11-24 16:35:50 +00:00
log . Warnf ( "message in mempool has already used nonce (%d < %d), from %s, to %s, %s" , msg . Message . Nonce , inclNonces [ from ] , msg . Message . From , msg . Message . To , msg . Cid ( ) )
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
}