lotus/chain/gen/gen.go

617 lines
16 KiB
Go
Raw Normal View History

2019-07-25 22:15:33 +00:00
package gen
import (
"bytes"
2019-07-25 22:15:33 +00:00
"context"
"crypto/sha256"
"encoding/binary"
"fmt"
2019-11-29 03:38:18 +00:00
"io/ioutil"
2019-07-30 13:20:40 +00:00
"sync/atomic"
2019-07-25 22:15:33 +00:00
2019-11-29 03:38:18 +00:00
ffi "github.com/filecoin-project/filecoin-ffi"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-car"
offline "github.com/ipfs/go-ipfs-exchange-offline"
"github.com/ipfs/go-merkledag"
peer "github.com/libp2p/go-libp2p-peer"
2019-11-21 22:21:45 +00:00
"go.opencensus.io/trace"
2019-08-15 18:45:32 +00:00
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/address"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
2019-11-29 03:38:18 +00:00
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
"github.com/filecoin-project/lotus/genesis"
2019-11-21 22:21:45 +00:00
"github.com/filecoin-project/lotus/lib/sectorbuilder"
"github.com/filecoin-project/lotus/node/repo"
2019-07-26 12:19:27 +00:00
2019-07-25 22:15:33 +00:00
block "github.com/ipfs/go-block-format"
2019-07-26 12:19:27 +00:00
"github.com/ipfs/go-cid"
2019-07-25 22:15:33 +00:00
blockstore "github.com/ipfs/go-ipfs-blockstore"
logging "github.com/ipfs/go-log"
)
var log = logging.Logger("gen")
2019-10-08 00:28:13 +00:00
const msgsPerBlock = 20
2019-07-30 13:20:40 +00:00
2019-07-25 22:15:33 +00:00
type ChainGen struct {
accounts []address.Address
msgsPerBlock int
bs blockstore.Blockstore
cs *store.ChainStore
sm *stmgr.StateManager
genesis *types.BlockHeader
CurTipset *store.FullTipSet
2019-07-25 22:15:33 +00:00
Timestamper func(*types.TipSet, uint64) uint64
2019-10-10 03:04:10 +00:00
2019-07-30 13:20:40 +00:00
w *wallet.Wallet
2019-11-21 22:21:45 +00:00
eppProvs map[address.Address]ElectionPoStProver
Miners []address.Address
mworkers []address.Address
2019-07-30 13:20:40 +00:00
receivers []address.Address
banker address.Address
bankerNonce uint64
2019-07-29 19:34:09 +00:00
r repo.Repo
2019-07-29 19:34:09 +00:00
lr repo.LockedRepo
2019-07-25 22:15:33 +00:00
}
type mybs struct {
blockstore.Blockstore
}
func (m mybs) Get(c cid.Cid) (block.Block, error) {
b, err := m.Blockstore.Get(c)
if err != nil {
return nil, err
}
return b, nil
}
func NewGenerator() (*ChainGen, error) {
mr := repo.NewMemory(nil)
2019-11-12 17:59:38 +00:00
lr, err := mr.Lock(repo.StorageMiner)
2019-07-25 22:15:33 +00:00
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("taking mem-repo lock failed: %w", err)
2019-07-25 22:15:33 +00:00
}
2019-07-29 19:34:09 +00:00
ds, err := lr.Datastore("/metadata")
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("failed to get metadata datastore: %w", err)
2019-07-29 19:34:09 +00:00
}
bds, err := lr.Datastore("/blocks")
2019-07-25 22:15:33 +00:00
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("failed to get blocks datastore: %w", err)
2019-07-25 22:15:33 +00:00
}
2019-07-29 19:34:09 +00:00
bs := mybs{blockstore.NewIdStore(blockstore.NewBlockstore(bds))}
2019-07-25 22:15:33 +00:00
ks, err := lr.KeyStore()
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("getting repo keystore failed: %w", err)
2019-07-25 22:15:33 +00:00
}
w, err := wallet.NewWallet(ks)
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("creating memrepo wallet failed: %w", err)
2019-07-25 22:15:33 +00:00
}
worker1, err := w.GenerateKey(types.KTBLS)
if err != nil {
return nil, xerrors.Errorf("failed to generate worker key: %w", err)
}
worker2, err := w.GenerateKey(types.KTBLS)
2019-07-25 22:15:33 +00:00
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("failed to generate worker key: %w", err)
2019-07-25 22:15:33 +00:00
}
banker, err := w.GenerateKey(types.KTSecp256k1)
2019-07-25 22:15:33 +00:00
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("failed to generate banker key: %w", err)
2019-07-25 22:15:33 +00:00
}
2019-07-30 13:20:40 +00:00
receievers := make([]address.Address, msgsPerBlock)
for r := range receievers {
receievers[r], err = w.GenerateKey(types.KTBLS)
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("failed to generate receiver key: %w", err)
2019-07-30 13:20:40 +00:00
}
}
2019-11-29 03:38:18 +00:00
// TODO: this is really weird, we have to guess the miner addresses that
// will be created in order to preseal data for them
maddr1, err := address.NewFromString("t0101")
if err != nil {
return nil, err
}
m1temp, err := ioutil.TempDir("", "preseal")
if err != nil {
return nil, err
}
genm1, err := seed.PreSeal(maddr1, 1024, 1, m1temp, []byte("some randomness"))
if err != nil {
return nil, err
}
2019-09-06 20:03:28 +00:00
minercfg := &GenMinerCfg{
PeerIDs: []peer.ID{"peerID1", "peerID2"},
2019-11-29 03:38:18 +00:00
PreSeals: map[string]genesis.GenesisMiner{
maddr1.String(): *genm1,
},
2019-09-06 20:03:28 +00:00
}
2019-07-25 22:15:33 +00:00
genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{
worker1: types.FromFil(40000),
worker2: types.FromFil(40000),
banker: types.FromFil(50000),
}, minercfg, 100000)
2019-07-25 22:15:33 +00:00
if err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("make genesis block failed: %w", err)
2019-07-25 22:15:33 +00:00
}
cs := store.NewChainStore(bs, ds)
genfb := &types.FullBlock{Header: genb.Genesis}
gents := store.NewFullTipSet([]*types.FullBlock{genfb})
2019-07-25 22:15:33 +00:00
2019-07-26 12:19:27 +00:00
if err := cs.SetGenesis(genb.Genesis); err != nil {
2019-08-15 18:45:32 +00:00
return nil, xerrors.Errorf("set genesis failed: %w", err)
2019-07-26 12:19:27 +00:00
}
if len(minercfg.MinerAddrs) == 0 {
return nil, xerrors.Errorf("MakeGenesisBlock failed to set miner address")
}
2019-11-21 22:21:45 +00:00
mgen := make(map[address.Address]ElectionPoStProver)
for _, m := range minercfg.MinerAddrs {
mgen[m] = &eppProvider{}
}
sm := stmgr.NewStateManager(cs)
2019-07-25 22:15:33 +00:00
gen := &ChainGen{
bs: bs,
cs: cs,
sm: sm,
2019-07-25 22:15:33 +00:00
msgsPerBlock: msgsPerBlock,
genesis: genb.Genesis,
2019-07-30 13:20:40 +00:00
w: w,
2019-11-28 22:50:58 +00:00
Miners: minercfg.MinerAddrs,
eppProvs: mgen,
//mworkers: minercfg.Workers,
2019-07-30 13:20:40 +00:00
banker: banker,
receivers: receievers,
CurTipset: gents,
2019-07-29 19:34:09 +00:00
r: mr,
2019-07-29 19:34:09 +00:00
lr: lr,
2019-07-25 22:15:33 +00:00
}
return gen, nil
}
func (cg *ChainGen) Genesis() *types.BlockHeader {
return cg.genesis
}
func (cg *ChainGen) GenesisCar() ([]byte, error) {
offl := offline.Exchange(cg.bs)
blkserv := blockservice.New(cg.bs, offl)
dserv := merkledag.NewDAGService(blkserv)
out := new(bytes.Buffer)
if err := car.WriteCar(context.TODO(), dserv, []cid.Cid{cg.Genesis().Cid()}, out); err != nil {
2019-11-21 22:21:45 +00:00
return nil, xerrors.Errorf("genesis car write car failed: %w", err)
}
return out.Bytes(), nil
}
2019-11-21 22:21:45 +00:00
func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round int64) (*types.EPostProof, *types.Ticket, error) {
lastTicket := pts.MinTicket()
st := pts.ParentState()
worker, err := stmgr.GetMinerWorkerRaw(ctx, cg.sm, st, m)
if err != nil {
return nil, nil, xerrors.Errorf("get miner worker: %w", err)
}
2019-08-16 04:40:59 +00:00
2019-11-21 22:21:45 +00:00
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, m, DSepTicket, lastTicket.VRFProof)
if err != nil {
return nil, nil, xerrors.Errorf("compute VRF: %w", err)
}
2019-08-15 02:30:21 +00:00
tick := &types.Ticket{
2019-10-09 04:38:59 +00:00
VRFProof: vrfout,
2019-08-15 02:30:21 +00:00
}
2019-11-21 22:21:45 +00:00
win, eproof, err := IsRoundWinner(ctx, pts, round, m, cg.eppProvs[m], &mca{w: cg.w, sm: cg.sm})
if err != nil {
return nil, nil, xerrors.Errorf("checking round winner failed: %w", err)
}
if !win {
return nil, tick, nil
}
return eproof, tick, nil
2019-07-25 22:15:33 +00:00
}
type MinedTipSet struct {
TipSet *store.FullTipSet
Messages []*types.SignedMessage
}
func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) {
mts, err := cg.NextTipSetFromMiners(cg.CurTipset.TipSet(), cg.Miners)
if err != nil {
return nil, err
}
cg.CurTipset = mts.TipSet
return mts, nil
}
func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Address) (*MinedTipSet, error) {
var blks []*types.FullBlock
msgs, err := cg.getRandomMessages()
2019-07-25 22:15:33 +00:00
if err != nil {
return nil, xerrors.Errorf("get random messages: %w", err)
2019-07-25 22:15:33 +00:00
}
for round := int64(base.Height() + 1); len(blks) == 0; round++ {
for _, m := range miners {
proof, t, err := cg.nextBlockProof(context.TODO(), base, m, round)
if err != nil {
return nil, xerrors.Errorf("next block proof: %w", err)
}
if proof != nil {
fblk, err := cg.makeBlock(base, m, proof, t, uint64(round), msgs)
if err != nil {
return nil, xerrors.Errorf("making a block for next tipset failed: %w", err)
}
2019-11-12 10:18:46 +00:00
if err := cg.cs.PersistBlockHeaders(fblk.Header); err != nil {
return nil, xerrors.Errorf("chainstore AddBlock: %w", err)
}
blks = append(blks, fblk)
}
}
}
fts := store.NewFullTipSet(blks)
return &MinedTipSet{
TipSet: fts,
Messages: msgs,
}, nil
}
2019-11-21 22:21:45 +00:00
func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof *types.EPostProof, ticket *types.Ticket, height uint64, msgs []*types.SignedMessage) (*types.FullBlock, error) {
2019-10-10 03:04:10 +00:00
var ts uint64
if cg.Timestamper != nil {
ts = cg.Timestamper(parents, height-parents.Height())
2019-10-10 03:04:10 +00:00
} else {
ts = parents.MinTimestamp() + ((height - parents.Height()) * build.BlockDelay)
2019-10-10 03:04:10 +00:00
}
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, ticket, eproof, msgs, height, ts)
if err != nil {
return nil, err
}
return fblk, err
}
// This function is awkward. It's used to deal with messages made when
// simulating forks
func (cg *ChainGen) ResyncBankerNonce(ts *types.TipSet) error {
act, err := cg.sm.GetActor(cg.banker, ts)
if err != nil {
return err
}
cg.bankerNonce = act.Nonce
return nil
}
func (cg *ChainGen) getRandomMessages() ([]*types.SignedMessage, error) {
2019-07-30 13:20:40 +00:00
msgs := make([]*types.SignedMessage, cg.msgsPerBlock)
for m := range msgs {
msg := types.Message{
To: cg.receivers[m%len(cg.receivers)],
2019-07-30 13:20:40 +00:00
From: cg.banker,
Nonce: atomic.AddUint64(&cg.bankerNonce, 1) - 1,
Value: types.NewInt(uint64(m + 1)),
Method: 0,
GasLimit: types.NewInt(10000),
GasPrice: types.NewInt(0),
}
sig, err := cg.w.Sign(context.TODO(), cg.banker, msg.Cid().Bytes())
2019-07-30 13:20:40 +00:00
if err != nil {
return nil, err
2019-07-30 13:20:40 +00:00
}
msgs[m] = &types.SignedMessage{
Message: msg,
Signature: *sig,
}
}
return msgs, nil
2019-07-25 22:15:33 +00:00
}
2019-07-29 19:34:09 +00:00
func (cg *ChainGen) YieldRepo() (repo.Repo, error) {
if err := cg.lr.Close(); err != nil {
return nil, err
}
return cg.r, nil
}
type MiningCheckAPI interface {
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
StateMinerPower(context.Context, address.Address, *types.TipSet) (api.MinerPower, error)
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
2019-11-21 22:21:45 +00:00
StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error)
2019-11-25 04:45:13 +00:00
StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
}
type mca struct {
w *wallet.Wallet
sm *stmgr.StateManager
}
func (mca mca) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, lb int64) ([]byte, error) {
return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), int64(lb))
}
func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) {
2019-09-09 20:03:10 +00:00
mpow, tpow, err := stmgr.GetPower(ctx, mca.sm, ts, maddr)
if err != nil {
return api.MinerPower{}, err
}
return api.MinerPower{
MinerPower: mpow,
TotalPower: tpow,
}, err
}
func (mca mca) StateMinerWorker(ctx context.Context, maddr address.Address, ts *types.TipSet) (address.Address, error) {
return stmgr.GetMinerWorkerRaw(ctx, mca.sm, ts.ParentState(), maddr)
}
2019-11-21 22:21:45 +00:00
func (mca mca) StateMinerSectorSize(ctx context.Context, maddr address.Address, ts *types.TipSet) (uint64, error) {
return stmgr.GetMinerSectorSize(ctx, mca.sm, ts, maddr)
}
2019-11-25 04:45:13 +00:00
func (mca mca) StateMinerProvingSet(ctx context.Context, maddr address.Address, ts *types.TipSet) ([]*api.ChainSectorInfo, error) {
return stmgr.GetMinerProvingSet(ctx, mca.sm, ts, maddr)
}
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*types.Signature, error) {
return mca.w.Sign(ctx, a, v)
}
2019-11-21 22:21:45 +00:00
type ElectionPoStProver interface {
2019-11-25 04:45:13 +00:00
GenerateCandidates(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte) ([]sectorbuilder.EPostCandidate, error)
ComputeProof(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte, []sectorbuilder.EPostCandidate) ([]byte, error)
2019-11-21 22:21:45 +00:00
}
type eppProvider struct {
sectors []ffi.PublicSectorInfo
2019-11-21 22:21:45 +00:00
}
2019-11-25 04:45:13 +00:00
func (epp *eppProvider) GenerateCandidates(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte) ([]sectorbuilder.EPostCandidate, error) {
2019-11-21 22:21:45 +00:00
return []sectorbuilder.EPostCandidate{
sectorbuilder.EPostCandidate{
SectorID: 1,
PartialTicket: [32]byte{},
Ticket: [32]byte{},
SectorChallengeIndex: 1,
},
}, nil
}
2019-11-25 04:45:13 +00:00
func (epp *eppProvider) ComputeProof(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte, winners []sectorbuilder.EPostCandidate) ([]byte, error) {
return []byte("valid proof"), nil
2019-11-21 22:21:45 +00:00
}
func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner address.Address, epp ElectionPoStProver, a MiningCheckAPI) (bool, *types.EPostProof, error) {
r, err := a.ChainGetRandomness(ctx, ts.Key(), round-build.EcRandomnessLookback)
if err != nil {
return false, nil, xerrors.Errorf("chain get randomness: %w", err)
}
mworker, err := a.StateMinerWorker(ctx, miner, ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to get miner worker: %w", err)
}
2019-11-21 22:21:45 +00:00
vrfout, err := ComputeVRF(ctx, a.WalletSign, mworker, miner, DSepElectionPost, r)
if err != nil {
return false, nil, xerrors.Errorf("failed to compute VRF: %w", err)
}
2019-11-25 04:45:13 +00:00
pset, err := a.StateMinerProvingSet(ctx, miner, ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to load proving set for miner: %w", err)
}
log.Warningf("Proving set for miner %s: %s", miner, pset)
var sinfos []ffi.PublicSectorInfo
2019-11-25 04:45:13 +00:00
for _, s := range pset {
var commRa [32]byte
copy(commRa[:], s.CommR)
sinfos = append(sinfos, ffi.PublicSectorInfo{
2019-11-25 04:45:13 +00:00
SectorID: s.SectorID,
CommR: commRa,
})
}
sectors := sectorbuilder.NewSortedPublicSectorInfo(sinfos)
2019-11-26 02:43:43 +00:00
hvrf := sha256.Sum256(vrfout)
2019-11-25 04:45:13 +00:00
log.Info("Replicas: ", sectors)
2019-11-26 02:43:43 +00:00
candidates, err := epp.GenerateCandidates(ctx, sectors, hvrf[:])
2019-11-21 22:21:45 +00:00
if err != nil {
2019-11-25 04:45:13 +00:00
return false, nil, xerrors.Errorf("failed to generate electionPoSt candidates: %w", err)
2019-11-21 22:21:45 +00:00
}
pow, err := a.StateMinerPower(ctx, miner, ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to check power: %w", err)
}
2019-11-21 22:21:45 +00:00
ssize, err := a.StateMinerSectorSize(ctx, miner, ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to look up miners sector size: %w", err)
}
var winners []sectorbuilder.EPostCandidate
for _, c := range candidates {
if types.IsTicketWinner(c.PartialTicket[:], ssize, pow.TotalPower, 1) {
winners = append(winners, c)
}
}
// no winners, sad
if len(winners) == 0 {
return false, nil, nil
}
2019-11-26 02:43:43 +00:00
proof, err := epp.ComputeProof(ctx, sectors, hvrf[:], winners)
2019-11-21 22:21:45 +00:00
if err != nil {
return false, nil, xerrors.Errorf("failed to compute snark for election proof: %w", err)
}
ept := types.EPostProof{
Proof: proof,
PostRand: vrfout,
}
for _, win := range winners {
2019-11-28 12:46:56 +00:00
ept.Candidates = append(ept.Candidates, types.EPostTicket{
2019-11-21 22:21:45 +00:00
Partial: win.PartialTicket[:],
SectorID: win.SectorID,
ChallengeIndex: win.SectorChallengeIndex,
})
}
return true, &ept, nil
}
type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error)
2019-11-21 22:21:45 +00:00
const (
DSepTicket = 1
DSepElectionPost = 2
)
func hashVRFBase(personalization uint64, miner address.Address, input []byte) ([]byte, error) {
if miner.Protocol() != address.ID {
return nil, xerrors.Errorf("miner address for compute VRF must be an ID address")
}
var persbuf [8]byte
binary.LittleEndian.PutUint64(persbuf[:], personalization)
h := sha256.New()
h.Write(persbuf[:])
h.Write([]byte{0})
h.Write(input)
h.Write([]byte{0})
h.Write(miner.Bytes())
return h.Sum(nil), nil
}
func VerifyVRF(ctx context.Context, worker, miner address.Address, p uint64, input, vrfproof []byte) error {
ctx, span := trace.StartSpan(ctx, "VerifyVRF")
defer span.End()
vrfBase, err := hashVRFBase(p, miner, input)
if err != nil {
return xerrors.Errorf("computing vrf base failed: %w", err)
}
sig := &types.Signature{
Type: types.KTBLS,
Data: vrfproof,
}
if err := sig.Verify(worker, vrfBase); err != nil {
return xerrors.Errorf("vrf was invalid: %w", err)
}
return nil
}
func ComputeVRF(ctx context.Context, sign SignFunc, worker, miner address.Address, p uint64, input []byte) ([]byte, error) {
sigInput, err := hashVRFBase(p, miner, input)
if err != nil {
return nil, err
}
sig, err := sign(ctx, worker, sigInput)
if err != nil {
return nil, err
}
if sig.Type != types.KTBLS {
return nil, fmt.Errorf("miner worker address was not a BLS key")
}
return sig.Data, nil
}
2019-11-21 22:21:45 +00:00
func TicketHash(t *types.Ticket, addr address.Address) []byte {
h := sha256.New()
2019-11-21 22:21:45 +00:00
h.Write(t.VRFProof)
2019-11-21 22:21:45 +00:00
// Field Delimeter
h.Write([]byte{0})
h.Write(addr.Bytes())
return h.Sum(nil)
}