Wire up more proper ticket generation and verification logic

This commit is contained in:
whyrusleeping 2019-08-15 17:17:09 -07:00
parent 2efab559ec
commit 5a7f59498e
6 changed files with 182 additions and 38 deletions

View File

@ -15,6 +15,7 @@ import (
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/wallet"
"github.com/filecoin-project/go-lotus/lib/vdf"
"github.com/filecoin-project/go-lotus/node/repo"
block "github.com/ipfs/go-block-format"
@ -134,6 +135,10 @@ func NewGenerator() (*ChainGen, error) {
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{
bs: bs,
cs: cs,
@ -174,11 +179,19 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) {
}
func (cg *ChainGen) nextBlockProof() (address.Address, types.ElectionProof, []*types.Ticket, error) {
tick := &types.Ticket{
VRFProof: []byte("im a ticket, promise"),
VDFProof: []byte("vdf proof"),
VDFResult: []byte("verifiable and delayed"),
vrf := []byte("veee arrr efff")
out, proof, err := vdf.Run(vrf)
if err != nil {
return address.Undef, nil, nil, err
}
tick := &types.Ticket{
VRFProof: vrf,
VDFProof: proof,
VDFResult: out,
}
return cg.miner, []byte("cat in a box"), []*types.Ticket{tick}, nil
}

View File

@ -8,9 +8,11 @@ import (
"golang.org/x/xerrors"
"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/types"
"github.com/filecoin-project/go-lotus/chain/vm"
"github.com/filecoin-project/go-lotus/lib/vdf"
"github.com/ipfs/go-cid"
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 {
return err
return xerrors.Errorf("collectChain failed: %w", err)
}
if err := syncer.store.PutTipSet(maybeHead); err != nil {
@ -311,6 +313,89 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
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 {
h := b.Header
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)
}
baseTs, err := syncer.store.LoadTipSet(b.Header.Parents)
baseTs, err := syncer.store.LoadTipSet(h.Parents)
if err != nil {
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 {
return err
}
@ -549,7 +647,7 @@ func (syncer *Syncer) collectChain(fts *store.FullTipSet) error {
}
if err := syncer.syncMessagesAndCheckState(headers); err != nil {
return err
return xerrors.Errorf("collectChain syncMessages: %w", err)
}
return nil

View File

@ -1,6 +1,7 @@
package types
import (
"bytes"
"encoding/json"
"fmt"
@ -89,3 +90,18 @@ func (ts *TipSet) Equals(ots *TipSet) bool {
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/store"
"github.com/filecoin-project/go-lotus/chain/types"
cid "github.com/ipfs/go-cid"
"golang.org/x/xerrors"
)
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
}
vmi, err := NewVM(state, ts.Height(), ts.Blocks()[0].Miner, cs)
func CallRaw(ctx context.Context, cs *store.ChainStore, msg *types.Message, bstate cid.Cid, bheight uint64) (*types.MessageReceipt, error) {
vmi, err := NewVM(bstate, bheight, actors.NetworkAddress, cs)
if err != nil {
return nil, xerrors.Errorf("failed to set up vm: %w", err)
}
@ -56,4 +49,17 @@ func Call(ctx context.Context, cs *store.ChainStore, msg *types.Message, ts *typ
log.Warnf("chain call failed: %s", ret.ActorErr)
}
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())
}

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

@ -1,7 +1,6 @@
package miner
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
@ -17,6 +16,7 @@ import (
"github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/lib/vdf"
)
var log = logging.Logger("miner")
@ -271,24 +271,7 @@ func (m *Miner) runVDF(ctx context.Context, input []byte) ([]byte, []byte, error
case <-time.After(m.Delay):
}
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 minTicket(ts *types.TipSet) *types.Ticket {
if len(ts.Blocks()) == 0 {
panic("tipset has no blocks!")
}
var minTicket *types.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
return vdf.Run(input)
}
func (m *Miner) scratchTicket(ctx context.Context, base *MiningBase) (*types.Ticket, error) {
@ -296,7 +279,7 @@ func (m *Miner) scratchTicket(ctx context.Context, base *MiningBase) (*types.Tic
if len(base.tickets) > 0 {
lastTicket = base.tickets[len(base.tickets)-1]
} else {
lastTicket = minTicket(base.ts)
lastTicket = base.ts.MinTicket()
}
vrfOut, err := m.computeVRF(ctx, lastTicket.VDFResult)