Merge pull request #139 from filecoin-project/feat/realer-mining

base mining on power and VRFs
This commit is contained in:
Whyrusleeping 2019-08-19 11:12:04 -07:00 committed by GitHub
commit aed7b8b640
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 785 additions and 148 deletions

View File

@ -66,7 +66,7 @@ type FullNode interface {
// miner // miner
MinerStart(context.Context, address.Address) error MinerStart(context.Context, address.Address) error
MinerCreateBlock(context.Context, address.Address, *types.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) MinerCreateBlock(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error)
// // UX ? // // UX ?

View File

@ -54,8 +54,8 @@ type FullNodeStruct struct {
MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"` MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"`
MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"` MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"`
MinerStart func(context.Context, address.Address) error `perm:"admin"` MinerStart func(context.Context, address.Address) error `perm:"admin"`
MinerCreateBlock func(context.Context, address.Address, *types.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) `perm:"write"` MinerCreateBlock func(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) `perm:"write"`
WalletNew func(context.Context, string) (address.Address, error) `perm:"write"` WalletNew func(context.Context, string) (address.Address, error) `perm:"write"`
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"` WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
@ -163,7 +163,7 @@ func (c *FullNodeStruct) MinerStart(ctx context.Context, addr address.Address) e
return c.Internal.MinerStart(ctx, addr) return c.Internal.MinerStart(ctx, addr)
} }
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, tickets []types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) { func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, tickets []*types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) {
return c.Internal.MinerCreateBlock(ctx, addr, base, tickets, eproof, msgs) return c.Internal.MinerCreateBlock(ctx, addr, base, tickets, eproof, msgs)
} }

View File

@ -2,6 +2,7 @@ package actors
import ( import (
"context" "context"
"github.com/filecoin-project/go-lotus/chain/actors/aerrors" "github.com/filecoin-project/go-lotus/chain/actors/aerrors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
@ -22,6 +23,7 @@ func init() {
cbor.RegisterCborType(PieceInclVoucherData{}) cbor.RegisterCborType(PieceInclVoucherData{})
cbor.RegisterCborType(InclusionProof{}) cbor.RegisterCborType(InclusionProof{})
cbor.RegisterCborType(PaymentVerifyParams{}) cbor.RegisterCborType(PaymentVerifyParams{})
cbor.RegisterCborType(UpdatePeerIDParams{})
} }
var ProvingPeriodDuration = uint64(2 * 60) // an hour, for now var ProvingPeriodDuration = uint64(2 * 60) // an hour, for now

View File

@ -1,6 +1,7 @@
package actors_test package actors_test
import ( import (
"context"
"testing" "testing"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
@ -30,7 +31,7 @@ func signVoucher(t *testing.T, w *wallet.Wallet, addr address.Address, sv *types
t.Fatal(err) t.Fatal(err)
} }
sig, err := w.Sign(addr, vb) sig, err := w.Sign(context.TODO(), addr, vb)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -16,6 +16,7 @@ func init() {
cbor.RegisterCborType(CreateStorageMinerParams{}) cbor.RegisterCborType(CreateStorageMinerParams{})
cbor.RegisterCborType(IsMinerParam{}) cbor.RegisterCborType(IsMinerParam{})
cbor.RegisterCborType(PowerLookupParams{}) cbor.RegisterCborType(PowerLookupParams{})
cbor.RegisterCborType(UpdateStorageParams{})
} }
type StorageMarketActor struct{} type StorageMarketActor struct{}
@ -115,7 +116,8 @@ type UpdateStorageParams struct {
func (sma StorageMarketActor) UpdateStorage(act *types.Actor, vmctx types.VMContext, params *UpdateStorageParams) ([]byte, ActorError) { func (sma StorageMarketActor) UpdateStorage(act *types.Actor, vmctx types.VMContext, params *UpdateStorageParams) ([]byte, ActorError) {
var self StorageMarketState var self StorageMarketState
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil { old := vmctx.Storage().GetHead()
if err := vmctx.Storage().Get(old, &self); err != nil {
return nil, err return nil, err
} }
@ -125,6 +127,16 @@ func (sma StorageMarketActor) UpdateStorage(act *types.Actor, vmctx types.VMCont
} }
self.TotalStorage = types.BigAdd(self.TotalStorage, params.Delta) self.TotalStorage = types.BigAdd(self.TotalStorage, params.Delta)
nroot, err := vmctx.Storage().Put(self)
if err != nil {
return nil, err
}
if err := vmctx.Storage().Commit(old, nroot); err != nil {
return nil, err
}
return nil, nil return nil, nil
} }

View File

@ -2,9 +2,10 @@ package deals
import ( import (
"context" "context"
"github.com/filecoin-project/go-lotus/chain/actors"
"math" "math"
"github.com/filecoin-project/go-lotus/chain/actors"
sectorbuilder "github.com/filecoin-project/go-sectorbuilder" sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore"
@ -139,7 +140,7 @@ func (c *Client) sendProposal(s inet.Stream, proposal StorageDealProposal, from
if err != nil { if err != nil {
return err return err
} }
sig, err := c.w.Sign(from, msg) sig, err := c.w.Sign(context.TODO(), from, msg)
if err != nil { if err != nil {
return err return err
} }

View File

@ -9,11 +9,13 @@ import (
"github.com/ipfs/go-car" "github.com/ipfs/go-car"
offline "github.com/ipfs/go-ipfs-exchange-offline" offline "github.com/ipfs/go-ipfs-exchange-offline"
"github.com/ipfs/go-merkledag" "github.com/ipfs/go-merkledag"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/wallet" "github.com/filecoin-project/go-lotus/chain/wallet"
"github.com/filecoin-project/go-lotus/lib/vdf"
"github.com/filecoin-project/go-lotus/node/repo" "github.com/filecoin-project/go-lotus/node/repo"
block "github.com/ipfs/go-block-format" block "github.com/ipfs/go-block-format"
@ -41,6 +43,7 @@ type ChainGen struct {
w *wallet.Wallet w *wallet.Wallet
miner address.Address miner address.Address
mworker address.Address
receivers []address.Address receivers []address.Address
banker address.Address banker address.Address
bankerNonce uint64 bankerNonce uint64
@ -68,56 +71,60 @@ func NewGenerator() (*ChainGen, error) {
mr := repo.NewMemory(nil) mr := repo.NewMemory(nil)
lr, err := mr.Lock() lr, err := mr.Lock()
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("taking mem-repo lock failed: %w", err)
} }
ds, err := lr.Datastore("/metadata") ds, err := lr.Datastore("/metadata")
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to get metadata datastore: %w", err)
} }
bds, err := lr.Datastore("/blocks") bds, err := lr.Datastore("/blocks")
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to get blocks datastore: %w", err)
} }
bs := mybs{blockstore.NewIdStore(blockstore.NewBlockstore(bds))} bs := mybs{blockstore.NewIdStore(blockstore.NewBlockstore(bds))}
ks, err := lr.KeyStore() ks, err := lr.KeyStore()
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("getting repo keystore failed: %w", err)
} }
w, err := wallet.NewWallet(ks) w, err := wallet.NewWallet(ks)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("creating memrepo wallet failed: %w", err)
} }
miner, err := w.GenerateKey(types.KTBLS) worker, err := w.GenerateKey(types.KTBLS)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to generate worker key: %w", err)
} }
// KTBLS doesn't support signature verification or something like that yet
banker, err := w.GenerateKey(types.KTSecp256k1) banker, err := w.GenerateKey(types.KTSecp256k1)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to generate banker key: %w", err)
} }
receievers := make([]address.Address, msgsPerBlock) receievers := make([]address.Address, msgsPerBlock)
for r := range receievers { for r := range receievers {
receievers[r], err = w.GenerateKey(types.KTBLS) receievers[r], err = w.GenerateKey(types.KTBLS)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to generate receiver key: %w", err)
} }
} }
minercfg := &GenMinerCfg{
Worker: worker,
Owner: worker,
}
genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{ genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{
miner: types.NewInt(5), worker: types.NewInt(50000),
banker: types.NewInt(90000000), banker: types.NewInt(90000000),
}) }, minercfg)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("make genesis block failed: %w", err)
} }
cs := store.NewChainStore(bs, ds) cs := store.NewChainStore(bs, ds)
@ -125,7 +132,11 @@ func NewGenerator() (*ChainGen, error) {
genfb := &types.FullBlock{Header: genb.Genesis} genfb := &types.FullBlock{Header: genb.Genesis}
if err := cs.SetGenesis(genb.Genesis); err != nil { if err := cs.SetGenesis(genb.Genesis); err != nil {
return nil, err return nil, xerrors.Errorf("set genesis failed: %w", err)
}
if minercfg.MinerAddr == address.Undef {
return nil, xerrors.Errorf("MakeGenesisBlock failed to set miner address")
} }
gen := &ChainGen{ gen := &ChainGen{
@ -135,7 +146,8 @@ func NewGenerator() (*ChainGen, error) {
genesis: genb.Genesis, genesis: genb.Genesis,
w: w, w: w,
miner: miner, miner: minercfg.MinerAddr,
mworker: worker,
banker: banker, banker: banker,
receivers: receievers, receivers: receievers,
@ -166,12 +178,32 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) {
return out.Bytes(), nil return out.Bytes(), nil
} }
func (cg *ChainGen) nextBlockProof() (address.Address, types.ElectionProof, []types.Ticket, error) { func (cg *ChainGen) nextBlockProof(ctx context.Context) (address.Address, types.ElectionProof, []*types.Ticket, error) {
return cg.miner, []byte("cat in a box"), []types.Ticket{types.Ticket("im a ticket, promise")}, nil
ticks := cg.curBlock.Header.Tickets
lastTicket := ticks[len(ticks)-1]
vrfout, err := ComputeVRF(ctx, cg.w.Sign, cg.mworker, lastTicket.VDFResult)
if err != nil {
return address.Undef, nil, nil, err
}
out, proof, err := vdf.Run(vrfout)
if err != nil {
return address.Undef, nil, nil, err
}
tick := &types.Ticket{
VRFProof: vrfout,
VDFProof: proof,
VDFResult: out,
}
return cg.miner, []byte("cat in a box"), []*types.Ticket{tick}, nil
} }
func (cg *ChainGen) NextBlock() (*types.FullBlock, []*types.SignedMessage, error) { func (cg *ChainGen) NextBlock() (*types.FullBlock, []*types.SignedMessage, error) {
miner, proof, tickets, err := cg.nextBlockProof() miner, proof, tickets, err := cg.nextBlockProof(context.TODO())
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -199,7 +231,7 @@ func (cg *ChainGen) NextBlock() (*types.FullBlock, []*types.SignedMessage, error
return nil, nil, err return nil, nil, err
} }
sig, err := cg.w.Sign(cg.banker, unsigned) sig, err := cg.w.Sign(context.TODO(), cg.banker, unsigned)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -16,7 +16,7 @@ import (
"github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/chain/vm"
) )
func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, miner address.Address, parents *types.TipSet, tickets []types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*types.FullBlock, error) { func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, miner address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*types.FullBlock, error) {
st, err := cs.TipSetState(parents.Cids()) st, err := cs.TipSetState(parents.Cids())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to load tipset state") return nil, errors.Wrap(err, "failed to load tipset state")

View File

@ -7,11 +7,16 @@ import (
actors "github.com/filecoin-project/go-lotus/chain/actors" actors "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
"golang.org/x/xerrors"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
hamt "github.com/ipfs/go-hamt-ipld" hamt "github.com/ipfs/go-hamt-ipld"
bstore "github.com/ipfs/go-ipfs-blockstore" bstore "github.com/ipfs/go-ipfs-blockstore"
peer "github.com/libp2p/go-libp2p-peer"
sharray "github.com/whyrusleeping/sharray" sharray "github.com/whyrusleeping/sharray"
) )
@ -133,17 +138,132 @@ func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) {
}, nil }, nil
} }
func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.BigInt) (*GenesisBootstrap, error) { type GenMinerCfg struct {
fmt.Println("at end of make Genesis block") Owner address.Address
Worker address.Address
state, err := MakeInitialStateTree(bs, balances) // not quite generating real sectors yet, but this will be necessary
//SectorDir string
// The address of the created miner, this is set by the genesis setup
MinerAddr address.Address
PeerID peer.ID
}
func mustEnc(i interface{}) []byte {
enc, err := actors.SerializeParams(i)
if err != nil {
panic(err)
}
return enc
}
func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, gmcfg *GenMinerCfg) (cid.Cid, error) {
vm, err := vm.NewVM(sroot, 0, actors.NetworkAddress, cs)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err)
}
params := mustEnc(actors.CreateStorageMinerParams{
Owner: gmcfg.Owner,
Worker: gmcfg.Worker,
SectorSize: types.NewInt(1024),
PeerID: gmcfg.PeerID,
})
rval, err := doExec(ctx, vm, actors.StorageMarketAddress, gmcfg.Owner, actors.SMAMethods.CreateStorageMiner, params)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
}
maddr, err := address.NewFromBytes(rval)
if err != nil {
return cid.Undef, err
}
gmcfg.MinerAddr = maddr
params = mustEnc(actors.UpdateStorageParams{Delta: types.NewInt(5000)})
_, err = doExec(ctx, vm, actors.StorageMarketAddress, maddr, actors.SMAMethods.UpdateStorage, params)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to update total storage: %w", err)
}
// UGLY HACKY MODIFICATION OF MINER POWER
// we have to flush the vm here because it buffers stuff internally for perf reasons
if _, err := vm.Flush(ctx); err != nil {
return cid.Undef, xerrors.Errorf("vm.Flush failed: %w", err)
}
st := vm.StateTree()
mact, err := st.GetActor(maddr)
if err != nil {
return cid.Undef, xerrors.Errorf("get miner actor failed: %w", err)
}
cst := hamt.CSTFromBstore(cs.Blockstore())
var mstate actors.StorageMinerActorState
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
return cid.Undef, xerrors.Errorf("getting miner actor state failed: %w", err)
}
mstate.Power = types.NewInt(5000)
nstate, err := cst.Put(ctx, mstate)
if err != nil {
return cid.Undef, err
}
mact.Head = nstate
if err := st.SetActor(maddr, mact); err != nil {
return cid.Undef, err
}
// End of super haxx
return vm.Flush(ctx)
}
func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uint64, params []byte) ([]byte, error) {
ret, err := vm.ApplyMessage(context.TODO(), &types.Message{
To: to,
From: from,
Method: method,
Params: params,
GasLimit: types.NewInt(1000000),
GasPrice: types.NewInt(0),
Value: types.NewInt(0),
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ret.ExitCode != 0 {
return nil, fmt.Errorf("failed to call method: %s", ret.ActorErr)
}
return ret.Return, nil
}
func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.BigInt, gmcfg *GenMinerCfg) (*GenesisBootstrap, error) {
ctx := context.Background()
state, err := MakeInitialStateTree(bs, balances)
if err != nil {
return nil, xerrors.Errorf("make initial state tree failed: %w", err)
}
stateroot, err := state.Flush() stateroot, err := state.Flush()
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("flush state tree failed: %w", err)
}
// temp chainstore
cs := store.NewChainStore(bs, datastore.NewMapDatastore())
stateroot, err = SetupStorageMiners(ctx, cs, stateroot, gmcfg)
if err != nil {
return nil, xerrors.Errorf("setup storage miners failed: %w", err)
} }
cst := hamt.CSTFromBstore(bs) cst := hamt.CSTFromBstore(bs)
@ -162,9 +282,15 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B
fmt.Println("Empty Genesis root: ", emptyroot) fmt.Println("Empty Genesis root: ", emptyroot)
genesisticket := &types.Ticket{
VRFProof: []byte("vrf proof"),
VDFResult: []byte("i am a vdf result"),
VDFProof: []byte("vdf proof"),
}
b := &types.BlockHeader{ b := &types.BlockHeader{
Miner: actors.InitActorAddress, Miner: actors.InitActorAddress,
Tickets: []types.Ticket{}, Tickets: []*types.Ticket{genesisticket},
ElectionProof: []byte("the Genesis block"), ElectionProof: []byte("the Genesis block"),
Parents: []cid.Cid{}, Parents: []cid.Cid{},
Height: 0, Height: 0,
@ -187,3 +313,19 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B
Genesis: b, Genesis: b,
}, nil }, nil
} }
type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error)
func ComputeVRF(ctx context.Context, sign SignFunc, w address.Address, input []byte) ([]byte, error) {
sig, err := sign(ctx, w, input)
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
}

View File

@ -8,9 +8,11 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/chain/vm"
"github.com/filecoin-project/go-lotus/lib/vdf"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
dstore "github.com/ipfs/go-datastore" dstore "github.com/ipfs/go-datastore"
@ -287,7 +289,7 @@ func (syncer *Syncer) Sync(maybeHead *store.FullTipSet) error {
} }
if err := syncer.collectChain(maybeHead); err != nil { if err := syncer.collectChain(maybeHead); err != nil {
return err return xerrors.Errorf("collectChain failed: %w", err)
} }
if err := syncer.store.PutTipSet(maybeHead); err != nil { if err := syncer.store.PutTipSet(maybeHead); err != nil {
@ -311,6 +313,89 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
return nil return nil
} }
func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error {
var err error
enc, err := actors.SerializeParams(&actors.IsMinerParam{Addr: maddr})
if err != nil {
return err
}
ret, err := vm.Call(ctx, syncer.store, &types.Message{
To: actors.StorageMarketAddress,
From: maddr,
Method: actors.SMAMethods.IsMiner,
Params: enc,
}, baseTs)
if err != nil {
return xerrors.Errorf("checking if block miner is valid failed: %w", err)
}
if ret.ExitCode != 0 {
return xerrors.Errorf("StorageMarket.IsMiner check failed (exit code %d)", ret.ExitCode)
}
// TODO: ensure the miner is currently not late on their PoSt submission (this hasnt landed in the spec yet)
return nil
}
func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Address, tickets []*types.Ticket, base *types.TipSet) error {
if len(tickets) == 0 {
return xerrors.Errorf("block had no tickets")
}
cur := base.MinTicket()
for i := 0; i < len(tickets); i++ {
next := tickets[i]
sig := &types.Signature{
Type: types.KTBLS,
Data: next.VRFProof,
}
log.Infof("About to verify signature: ", sig.Data, mworker, cur.VDFResult)
if err := sig.Verify(mworker, cur.VDFResult); err != nil {
return xerrors.Errorf("invalid ticket, VRFProof invalid: %w", err)
}
// now verify the VDF
if err := vdf.Verify(next.VRFProof, next.VDFResult, next.VDFProof); err != nil {
return xerrors.Errorf("ticket %d had invalid VDF: %w", err)
}
cur = next
}
return nil
}
func getMinerWorker(ctx context.Context, cs *store.ChainStore, st cid.Cid, maddr address.Address) (address.Address, error) {
recp, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetWorkerAddr,
}, st, 0)
if err != nil {
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
}
if recp.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getting miner worker addr failed (exit code %d)", recp.ExitCode)
}
worker, err := address.NewFromBytes(recp.Return)
if err != nil {
return address.Undef, err
}
if worker.Protocol() == address.ID {
return address.Undef, xerrors.Errorf("need to resolve worker address to a pubkeyaddr")
}
return worker, nil
}
// Should match up with 'Semantical Validation' in validation.md in the spec
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error { func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
h := b.Header h := b.Header
stateroot, err := syncer.store.TipSetState(h.Parents) stateroot, err := syncer.store.TipSetState(h.Parents)
@ -318,12 +403,25 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err) return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err)
} }
baseTs, err := syncer.store.LoadTipSet(b.Header.Parents) baseTs, err := syncer.store.LoadTipSet(h.Parents)
if err != nil { if err != nil {
return err return err
} }
vmi, err := vm.NewVM(stateroot, b.Header.Height, b.Header.Miner, syncer.store) if err := syncer.minerIsValid(ctx, h.Miner, baseTs); err != nil {
return xerrors.Errorf("minerIsValid failed: %w", err)
}
waddr, err := getMinerWorker(ctx, syncer.store, stateroot, h.Miner)
if err != nil {
return xerrors.Errorf("getMinerWorker failed: %w", err)
}
if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil {
return xerrors.Errorf("validating block tickets failed: %w", err)
}
vmi, err := vm.NewVM(stateroot, h.Height, h.Miner, syncer.store)
if err != nil { if err != nil {
return err return err
} }
@ -549,7 +647,7 @@ func (syncer *Syncer) collectChain(fts *store.FullTipSet) error {
} }
if err := syncer.syncMessagesAndCheckState(headers); err != nil { if err := syncer.syncMessagesAndCheckState(headers); err != nil {
return err return xerrors.Errorf("collectChain syncMessages: %w", err)
} }
return nil return nil

View File

@ -54,6 +54,10 @@ func BigMul(a, b BigInt) BigInt {
return BigInt{big.NewInt(0).Mul(a.Int, b.Int)} return BigInt{big.NewInt(0).Mul(a.Int, b.Int)}
} }
func BigDiv(a, b BigInt) BigInt {
return BigInt{big.NewInt(0).Div(a.Int, b.Int)}
}
func BigAdd(a, b BigInt) BigInt { func BigAdd(a, b BigInt) BigInt {
return BigInt{big.NewInt(0).Add(a.Int, b.Int)} return BigInt{big.NewInt(0).Add(a.Int, b.Int)}
} }

View File

@ -1,6 +1,8 @@
package types package types
import ( import (
"fmt"
block "github.com/ipfs/go-block-format" block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor" cbor "github.com/ipfs/go-ipld-cbor"
@ -15,14 +17,22 @@ func init() {
TransformMarshal(atlas.MakeMarshalTransformFunc( TransformMarshal(atlas.MakeMarshalTransformFunc(
func(blk BlockHeader) ([]interface{}, error) { func(blk BlockHeader) ([]interface{}, error) {
if blk.Tickets == nil { if blk.Tickets == nil {
blk.Tickets = []Ticket{} blk.Tickets = []*Ticket{}
} }
if blk.Parents == nil { if blk.Parents == nil {
blk.Parents = []cid.Cid{} blk.Parents = []cid.Cid{}
} }
var tickarrs [][][]byte // oh boy
for _, t := range blk.Tickets {
tickarrs = append(tickarrs, [][]byte{
t.VRFProof, t.VDFResult, t.VDFProof,
})
}
return []interface{}{ return []interface{}{
blk.Miner.Bytes(), blk.Miner.Bytes(),
blk.Tickets, tickarrs,
blk.ElectionProof, blk.ElectionProof,
blk.Parents, blk.Parents,
blk.ParentWeight, blk.ParentWeight,
@ -39,10 +49,23 @@ func init() {
return BlockHeader{}, err return BlockHeader{}, err
} }
tickets := []Ticket{} tickets := []*Ticket{}
ticketarr, _ := arr[1].([]interface{}) ticketarr, _ := arr[1].([]interface{})
for _, t := range ticketarr { for _, t := range ticketarr {
tickets = append(tickets, Ticket(t.([]byte))) ticklist, ok := t.([]interface{})
if !ok {
return BlockHeader{}, fmt.Errorf("tickets were incorrectly formatted (type = %T)", t)
}
if len(ticklist) != 3 {
return BlockHeader{}, fmt.Errorf("ticket should be a three item array of Byte arrays (got len = %d)", len(ticklist))
}
tickets = append(tickets, &Ticket{
VRFProof: ticklist[0].([]byte),
VDFResult: ticklist[1].([]byte),
VDFProof: ticklist[2].([]byte),
})
} }
electionProof, _ := arr[2].([]byte) electionProof, _ := arr[2].([]byte)
@ -72,15 +95,21 @@ func init() {
})). })).
Complete()) Complete())
cbor.RegisterCborType(MsgMeta{}) cbor.RegisterCborType(MsgMeta{})
cbor.RegisterCborType(Ticket{})
}
type Ticket struct {
VRFProof []byte
VDFResult []byte
VDFProof []byte
} }
type Ticket []byte
type ElectionProof []byte type ElectionProof []byte
type BlockHeader struct { type BlockHeader struct {
Miner address.Address Miner address.Address
Tickets []Ticket Tickets []*Ticket
ElectionProof []byte ElectionProof []byte

View File

@ -55,6 +55,10 @@ func (m *SignedMessage) ToStorageBlock() (block.Block, error) {
} }
func (m *SignedMessage) Cid() cid.Cid { func (m *SignedMessage) Cid() cid.Cid {
if m.Signature.Type == KTBLS {
return m.Message.Cid()
}
sb, err := m.ToStorageBlock() sb, err := m.ToStorageBlock()
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -89,3 +90,18 @@ func (ts *TipSet) Equals(ots *TipSet) bool {
return true return true
} }
func (ts *TipSet) MinTicket() *Ticket {
if len(ts.Blocks()) == 0 {
panic("tipset has no blocks!")
}
var minTicket *Ticket
for _, b := range ts.Blocks() {
lastTicket := b.Tickets[len(b.Tickets)-1]
if minTicket == nil || bytes.Compare(lastTicket.VDFResult, minTicket.VDFResult) < 0 {
minTicket = lastTicket
}
}
return minTicket
}

View File

@ -6,19 +6,12 @@ import (
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
cid "github.com/ipfs/go-cid"
"golang.org/x/xerrors" "golang.org/x/xerrors"
) )
func Call(ctx context.Context, cs *store.ChainStore, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) { func CallRaw(ctx context.Context, cs *store.ChainStore, msg *types.Message, bstate cid.Cid, bheight uint64) (*types.MessageReceipt, error) {
if ts == nil { vmi, err := NewVM(bstate, bheight, actors.NetworkAddress, cs)
ts = cs.GetHeaviestTipSet()
}
state, err := cs.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
vmi, err := NewVM(state, ts.Height(), ts.Blocks()[0].Miner, cs)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to set up vm: %w", err) return nil, xerrors.Errorf("failed to set up vm: %w", err)
} }
@ -56,4 +49,18 @@ func Call(ctx context.Context, cs *store.ChainStore, msg *types.Message, ts *typ
log.Warnf("chain call failed: %s", ret.ActorErr) log.Warnf("chain call failed: %s", ret.ActorErr)
} }
return &ret.MessageReceipt, nil return &ret.MessageReceipt, nil
}
func Call(ctx context.Context, cs *store.ChainStore, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
if ts == nil {
ts = cs.GetHeaviestTipSet()
}
state, err := cs.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
return CallRaw(ctx, cs, msg, state, ts.Height())
} }

View File

@ -441,6 +441,10 @@ func Copy(ctx context.Context, from, to ipld.DAGService, root cid.Cid) error {
return nil return nil
} }
func (vm *VM) StateTree() types.StateTree {
return vm.cstate
}
func (vm *VM) TransferFunds(from, to address.Address, amt types.BigInt) error { func (vm *VM) TransferFunds(from, to address.Address, amt types.BigInt) error {
if from == to { if from == to {
return nil return nil

View File

@ -1,6 +1,7 @@
package wallet package wallet
import ( import (
"context"
"sort" "sort"
"strings" "strings"
@ -33,7 +34,7 @@ func NewWallet(keystore types.KeyStore) (*Wallet, error) {
return w, nil return w, nil
} }
func (w *Wallet) Sign(addr address.Address, msg []byte) (*types.Signature, error) { func (w *Wallet) Sign(ctx context.Context, addr address.Address, msg []byte) (*types.Signature, error) {
ki, err := w.findKey(addr) ki, err := w.findKey(addr)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -3,6 +3,7 @@ package cli
import ( import (
"fmt" "fmt"
"github.com/filecoin-project/go-lotus/chain/address"
"gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2"
) )
@ -25,8 +26,12 @@ var minerStart = &cli.Command{
ctx := ReqContext(cctx) ctx := ReqContext(cctx)
// TODO: this address needs to be the address of an actual miner if !cctx.Args().Present() {
maddr, err := api.WalletDefaultAddress(ctx) return fmt.Errorf("must specify miner actor address to mine for")
}
// TODO: need to pull this from disk or something
maddr, err := address.NewFromString(cctx.Args().First())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,11 +1,14 @@
package main package main
import ( import (
"context"
"github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/build" "github.com/filecoin-project/go-lotus/build"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
@ -17,6 +20,12 @@ import (
var initCmd = &cli.Command{ var initCmd = &cli.Command{
Name: "init", Name: "init",
Usage: "Initialize a lotus storage miner repo", Usage: "Initialize a lotus storage miner repo",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "actor",
Usage: "specify the address of an already created miner actor",
},
},
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
log.Info("Initializing lotus storage miner") log.Info("Initializing lotus storage miner")
log.Info("Checking if repo exists") log.Info("Checking if repo exists")
@ -77,85 +86,27 @@ var initCmd = &cli.Command{
return err return err
} }
log.Info("Creating StorageMarket.CreateStorageMiner message") var addr address.Address
if act := cctx.String("actor"); act != "" {
a, err := address.NewFromString(act)
if err != nil {
return err
}
defOwner, err := api.WalletDefaultAddress(ctx) if err := configureStorageMiner(ctx, api, a, peerid); err != nil {
if err != nil { return xerrors.Errorf("failed to configure storage miner: %w", err)
return err }
addr = a
} else {
a, err := createStorageMiner(ctx, api, peerid)
if err != nil {
return err
}
addr = a
} }
nonce, err := api.MpoolGetNonce(ctx, defOwner)
if err != nil {
return err
}
k, err := api.WalletNew(ctx, types.KTSecp256k1)
if err != nil {
return err
}
collateral := types.NewInt(1000) // TODO: Get this from params
params, err := actors.SerializeParams(actors.CreateStorageMinerParams{
Owner: defOwner,
Worker: k,
SectorSize: types.NewInt(actors.SectorSize),
PeerID: peerid,
})
if err != nil {
return err
}
createStorageMinerMsg := types.Message{
To: actors.StorageMarketAddress,
From: defOwner,
Nonce: nonce,
Value: collateral,
Method: actors.SMAMethods.CreateStorageMiner,
Params: params,
}
unsigned, err := createStorageMinerMsg.Serialize()
if err != nil {
return err
}
log.Info("Signing StorageMarket.CreateStorageMiner")
sig, err := api.WalletSign(ctx, defOwner, unsigned)
if err != nil {
return err
}
signed := &types.SignedMessage{
Message: createStorageMinerMsg,
Signature: *sig,
}
log.Infof("Pushing %s to Mpool", signed.Cid())
err = api.MpoolPush(ctx, signed)
if err != nil {
return err
}
log.Infof("Waiting for confirmation")
mw, err := api.ChainWaitMsg(ctx, signed.Cid())
if err != nil {
return err
}
addr, err := address.NewFromBytes(mw.Receipt.Return)
if err != nil {
return err
}
log.Infof("New storage miners address is: %s", addr)
ds, err := lr.Datastore("/metadata") ds, err := lr.Datastore("/metadata")
if err != nil { if err != nil {
return err return err
@ -170,3 +121,149 @@ var initCmd = &cli.Command{
return nil return nil
}, },
} }
func configureStorageMiner(ctx context.Context, api api.FullNode, addr address.Address, peerid peer.ID) error {
// This really just needs to be an api call at this point...
recp, err := api.ChainCall(ctx, &types.Message{
To: addr,
From: addr,
Method: actors.MAMethods.GetWorkerAddr,
}, nil)
if err != nil {
return xerrors.Errorf("failed to get worker address: %w", err)
}
if recp.ExitCode != 0 {
return xerrors.Errorf("getWorkerAddr returned exit code %d", recp.ExitCode)
}
waddr, err := address.NewFromBytes(recp.Return)
if err != nil {
return xerrors.Errorf("getWorkerAddr returned bad address: %w", err)
}
enc, err := actors.SerializeParams(&actors.UpdatePeerIDParams{PeerID: peerid})
if err != nil {
return err
}
nonce, err := api.MpoolGetNonce(ctx, waddr)
if err != nil {
return err
}
msg := &types.Message{
To: addr,
From: waddr,
Method: actors.MAMethods.UpdatePeerID,
Params: enc,
Nonce: nonce,
Value: types.NewInt(0),
GasPrice: types.NewInt(0),
GasLimit: types.NewInt(1000),
}
smsg, err := api.WalletSignMessage(ctx, waddr, msg)
if err != nil {
return err
}
if err := api.MpoolPush(ctx, smsg); err != nil {
return err
}
log.Info("Waiting for message: ", smsg.Cid())
ret, err := api.ChainWaitMsg(ctx, smsg.Cid())
if err != nil {
return err
}
if ret.Receipt.ExitCode != 0 {
return xerrors.Errorf("update peer id message failed with exit code %d", ret.Receipt.ExitCode)
}
return nil
}
func createStorageMiner(ctx context.Context, api api.FullNode, peerid peer.ID) (address.Address, error) {
log.Info("Creating StorageMarket.CreateStorageMiner message")
defOwner, err := api.WalletDefaultAddress(ctx)
if err != nil {
return address.Undef, err
}
nonce, err := api.MpoolGetNonce(ctx, defOwner)
if err != nil {
return address.Undef, err
}
k, err := api.WalletNew(ctx, types.KTSecp256k1)
if err != nil {
return address.Undef, err
}
collateral := types.NewInt(1000) // TODO: Get this from params
params, err := actors.SerializeParams(actors.CreateStorageMinerParams{
Owner: defOwner,
Worker: k,
SectorSize: types.NewInt(actors.SectorSize),
PeerID: peerid,
})
if err != nil {
return address.Undef, err
}
createStorageMinerMsg := types.Message{
To: actors.StorageMarketAddress,
From: defOwner,
Nonce: nonce,
Value: collateral,
Method: actors.SMAMethods.CreateStorageMiner,
Params: params,
GasLimit: types.NewInt(10000),
GasPrice: types.NewInt(0),
}
unsigned, err := createStorageMinerMsg.Serialize()
if err != nil {
return address.Undef, err
}
log.Info("Signing StorageMarket.CreateStorageMiner")
sig, err := api.WalletSign(ctx, defOwner, unsigned)
if err != nil {
return address.Undef, err
}
signed := &types.SignedMessage{
Message: createStorageMinerMsg,
Signature: *sig,
}
log.Infof("Pushing %s to Mpool", signed.Cid())
err = api.MpoolPush(ctx, signed)
if err != nil {
return address.Undef, err
}
log.Infof("Waiting for confirmation")
mw, err := api.ChainWaitMsg(ctx, signed.Cid())
if err != nil {
return address.Undef, err
}
addr, err := address.NewFromBytes(mw.Receipt.Return)
if err != nil {
return address.Undef, err
}
log.Infof("New storage miners address is: %s", addr)
return addr, nil
}

1
go.mod
View File

@ -40,6 +40,7 @@ require (
github.com/libp2p/go-libp2p-discovery v0.1.0 github.com/libp2p/go-libp2p-discovery v0.1.0
github.com/libp2p/go-libp2p-kad-dht v0.1.1 github.com/libp2p/go-libp2p-kad-dht v0.1.1
github.com/libp2p/go-libp2p-mplex v0.2.1 github.com/libp2p/go-libp2p-mplex v0.2.1
github.com/libp2p/go-libp2p-peer v0.2.0
github.com/libp2p/go-libp2p-peerstore v0.1.2-0.20190621130618-cfa9bb890c1a github.com/libp2p/go-libp2p-peerstore v0.1.2-0.20190621130618-cfa9bb890c1a
github.com/libp2p/go-libp2p-pnet v0.1.0 github.com/libp2p/go-libp2p-pnet v0.1.0
github.com/libp2p/go-libp2p-pubsub v0.1.0 github.com/libp2p/go-libp2p-pubsub v0.1.0

1
go.sum
View File

@ -305,6 +305,7 @@ github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFx
github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ=
github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=
github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo= github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo=
github.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY=
github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY=
github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20=
github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY=

28
lib/vdf/vdf.go Normal file
View File

@ -0,0 +1,28 @@
package vdf
import (
"bytes"
"crypto/sha256"
"fmt"
)
func Run(input []byte) ([]byte, []byte, error) {
h := sha256.Sum256(input)
// TODO: THIS IS A FAKE VDF. THE SPEC IS UNCLEAR ON WHAT TO REALLY DO HERE
return h[:], []byte("proof"), nil
}
func Verify(input []byte, out []byte, proof []byte) error {
// this is a fake VDF
h := sha256.Sum256(input)
if !bytes.Equal(h[:], out) {
return fmt.Errorf("vdf output incorrect")
}
if !bytes.Equal(proof, []byte("proof")) {
return fmt.Errorf("vdf proof failed to validate")
}
return nil
}

View File

@ -2,20 +2,28 @@ package miner
import ( import (
"context" "context"
"crypto/sha256"
"math/big"
"time" "time"
logging "github.com/ipfs/go-log" logging "github.com/ipfs/go-log"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.opencensus.io/trace" "go.opencensus.io/trace"
"golang.org/x/xerrors"
chain "github.com/filecoin-project/go-lotus/chain" chain "github.com/filecoin-project/go-lotus/chain"
"github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/gen"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/lib/vdf"
) )
var log = logging.Logger("miner") var log = logging.Logger("miner")
type api interface { type api interface {
ChainCall(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error)
ChainSubmitBlock(context.Context, *chain.BlockMsg) error ChainSubmitBlock(context.Context, *chain.BlockMsg) error
// returns a set of messages that havent been included in the chain as of // returns a set of messages that havent been included in the chain as of
@ -35,7 +43,9 @@ type api interface {
// it seems realllllly annoying to do all the actions necessary to build a // it seems realllllly annoying to do all the actions necessary to build a
// block through the API. so, we just add the block creation to the API // block through the API. so, we just add the block creation to the API
// now, all the 'miner' does is check if they win, and call create block // now, all the 'miner' does is check if they win, and call create block
MinerCreateBlock(context.Context, address.Address, *types.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) MinerCreateBlock(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error)
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
} }
func NewMiner(api api, addr address.Address) *Miner { func NewMiner(api api, addr address.Address) *Miner {
@ -83,7 +93,7 @@ func (m *Miner) Mine(ctx context.Context) {
type MiningBase struct { type MiningBase struct {
ts *types.TipSet ts *types.TipSet
tickets []types.Ticket tickets []*types.Ticket
} }
func (m *Miner) GetBestMiningCandidate() (*MiningBase, error) { func (m *Miner) GetBestMiningCandidate() (*MiningBase, error) {
@ -114,7 +124,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*chain.BlockMsg,
return nil, errors.Wrap(err, "scratching ticket failed") return nil, errors.Wrap(err, "scratching ticket failed")
} }
win, proof, err := m.isWinnerNextRound(base) win, proof, err := m.isWinnerNextRound(ctx, base)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to check if we win next round") return nil, errors.Wrap(err, "failed to check if we win next round")
} }
@ -133,33 +143,154 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*chain.BlockMsg,
return b, nil return b, nil
} }
func (m *Miner) submitNullTicket(base *MiningBase, ticket types.Ticket) { func (m *Miner) submitNullTicket(base *MiningBase, ticket *types.Ticket) {
base.tickets = append(base.tickets, ticket) base.tickets = append(base.tickets, ticket)
m.lastWork = base m.lastWork = base
} }
func (m *Miner) isWinnerNextRound(base *MiningBase) (bool, types.ElectionProof, error) { func (m *Miner) computeVRF(ctx context.Context, input []byte) ([]byte, error) {
w, err := m.getMinerWorker(ctx, m.address, nil)
if err != nil {
return nil, err
}
return gen.ComputeVRF(ctx, m.api.WalletSign, w, input)
}
func (m *Miner) getMinerWorker(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
ret, err := m.api.ChainCall(ctx, &types.Message{
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
}
func (m *Miner) isWinnerNextRound(ctx context.Context, base *MiningBase) (bool, types.ElectionProof, error) {
r, err := m.api.ChainGetRandomness(context.TODO(), base.ts) r, err := m.api.ChainGetRandomness(context.TODO(), base.ts)
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
_ = r // TODO: use this to properly compute the election proof vrfout, err := m.computeVRF(ctx, r)
if err != nil {
return false, nil, xerrors.Errorf("failed to compute VRF: %w", err)
}
return true, []byte("election prooooof"), nil mpow, totpow, err := m.getPowerForTipset(ctx, m.address, base.ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to check power: %w", err)
}
return powerCmp(vrfout, mpow, totpow), vrfout, nil
} }
func (m *Miner) scratchTicket(ctx context.Context, base *MiningBase) (types.Ticket, error) { func powerCmp(vrfout []byte, mpow, totpow types.BigInt) bool {
/*
Need to check that
h(vrfout) / 2^256 < minerPower / totalPower
*/
h := sha256.Sum256(vrfout)
// 2^256
rden := types.BigInt{big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil)}
top := types.BigMul(rden, mpow)
out := types.BigDiv(top, totpow)
return types.BigCmp(types.BigFromBytes(h[:]), out) < 0
}
func (m *Miner) getPowerForTipset(ctx context.Context, maddr address.Address, ts *types.TipSet) (types.BigInt, types.BigInt, error) {
var err error
enc, err := actors.SerializeParams(&actors.PowerLookupParams{maddr})
if err != nil {
return types.EmptyInt, types.EmptyInt, err
}
ret, err := m.api.ChainCall(ctx, &types.Message{
From: maddr,
To: actors.StorageMarketAddress,
Method: actors.SMAMethods.PowerLookup,
Params: enc,
}, ts)
if err != nil {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get miner power from chain: %w", err)
}
if ret.ExitCode != 0 {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get miner power from chain (exit code %d)", ret.ExitCode)
}
mpow := types.BigFromBytes(ret.Return)
ret, err = m.api.ChainCall(ctx, &types.Message{
From: maddr,
To: actors.StorageMarketAddress,
Method: actors.SMAMethods.GetTotalStorage,
}, ts)
if err != nil {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get total power from chain: %w", err)
}
if ret.ExitCode != 0 {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get total power from chain (exit code %d)", ret.ExitCode)
}
tpow := types.BigFromBytes(ret.Return)
return mpow, tpow, nil
}
func (m *Miner) runVDF(ctx context.Context, input []byte) ([]byte, []byte, error) {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return nil, nil, ctx.Err()
case <-time.After(m.Delay): case <-time.After(m.Delay):
} }
return []byte("this is a ticket"), nil return vdf.Run(input)
} }
func (m *Miner) createBlock(base *MiningBase, ticket types.Ticket, proof types.ElectionProof) (*chain.BlockMsg, error) { func (m *Miner) scratchTicket(ctx context.Context, base *MiningBase) (*types.Ticket, error) {
var lastTicket *types.Ticket
if len(base.tickets) > 0 {
lastTicket = base.tickets[len(base.tickets)-1]
} else {
lastTicket = base.ts.MinTicket()
}
vrfOut, err := m.computeVRF(ctx, lastTicket.VDFResult)
if err != nil {
return nil, err
}
res, proof, err := m.runVDF(ctx, vrfOut)
if err != nil {
return nil, err
}
return &types.Ticket{
VRFProof: vrfOut,
VDFResult: res,
VDFProof: proof,
}, nil
}
func (m *Miner) createBlock(base *MiningBase, ticket *types.Ticket, proof types.ElectionProof) (*chain.BlockMsg, error) {
pending, err := m.api.MpoolPending(context.TODO(), base.ts) pending, err := m.api.MpoolPending(context.TODO(), base.ts)
if err != nil { if err != nil {

View File

@ -291,7 +291,7 @@ func (a *FullNodeAPI) MinerStart(ctx context.Context, addr address.Address) erro
return nil return nil
} }
func (a *FullNodeAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) { func (a *FullNodeAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) {
fblk, err := gen.MinerCreateBlock(ctx, a.Chain, addr, parents, tickets, proof, msgs) fblk, err := gen.MinerCreateBlock(ctx, a.Chain, addr, parents, tickets, proof, msgs)
if err != nil { if err != nil {
return nil, err return nil, err
@ -326,7 +326,7 @@ func (a *FullNodeAPI) WalletBalance(ctx context.Context, addr address.Address) (
} }
func (a *FullNodeAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) { func (a *FullNodeAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) {
return a.Wallet.Sign(k, msg) return a.Wallet.Sign(ctx, k, msg)
} }
func (a *FullNodeAPI) WalletSignMessage(ctx context.Context, k address.Address, msg *types.Message) (*types.SignedMessage, error) { func (a *FullNodeAPI) WalletSignMessage(ctx context.Context, k address.Address, msg *types.Message) (*types.SignedMessage, error) {

View File

@ -2,6 +2,7 @@ package testing
import ( import (
"context" "context"
"fmt"
"io" "io"
"os" "os"
@ -27,7 +28,20 @@ func MakeGenesisMem(out io.Writer) func(bs dtypes.ChainBlockstore, w *wallet.Wal
return func() (*types.BlockHeader, error) { return func() (*types.BlockHeader, error) {
glog.Warn("Generating new random genesis block, note that this SHOULD NOT happen unless you are setting up new network") glog.Warn("Generating new random genesis block, note that this SHOULD NOT happen unless you are setting up new network")
// TODO: make an address allocation // TODO: make an address allocation
b, err := gen.MakeGenesisBlock(bs, nil) w, err := w.GenerateKey(types.KTBLS)
if err != nil {
return nil, err
}
gmc := &gen.GenMinerCfg{
Owner: w,
Worker: w,
}
alloc := map[address.Address]types.BigInt{
w: types.NewInt(100000),
}
b, err := gen.MakeGenesisBlock(bs, alloc, gmc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -48,20 +62,27 @@ func MakeGenesis(outFile string) func(bs dtypes.ChainBlockstore, w *wallet.Walle
return func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis { return func(bs dtypes.ChainBlockstore, w *wallet.Wallet) modules.Genesis {
return func() (*types.BlockHeader, error) { return func() (*types.BlockHeader, error) {
glog.Warn("Generating new random genesis block, note that this SHOULD NOT happen unless you are setting up new network") glog.Warn("Generating new random genesis block, note that this SHOULD NOT happen unless you are setting up new network")
minerAddr, err := w.GenerateKey(types.KTSecp256k1) minerAddr, err := w.GenerateKey(types.KTBLS)
if err != nil { if err != nil {
return nil, err return nil, err
} }
gmc := &gen.GenMinerCfg{
Owner: minerAddr,
Worker: minerAddr,
}
addrs := map[address.Address]types.BigInt{ addrs := map[address.Address]types.BigInt{
minerAddr: types.NewInt(50000000), minerAddr: types.NewInt(50000000),
} }
b, err := gen.MakeGenesisBlock(bs, addrs) b, err := gen.MakeGenesisBlock(bs, addrs, gmc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
fmt.Println("GENESIS MINER ADDRESS: ", gmc.MinerAddr.String())
f, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY, 0644) f, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
return nil, err return nil, err