Wire up more proper ticket generation and verification logic
This commit is contained in:
parent
2efab559ec
commit
5a7f59498e
@ -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
|
||||
}
|
||||
|
||||
|
106
chain/sync.go
106
chain/sync.go
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
28
lib/vdf/vdf.go
Normal 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
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user