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/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"
|
||||||
@ -134,6 +135,10 @@ func NewGenerator() (*ChainGen, error) {
|
|||||||
return nil, xerrors.Errorf("set genesis failed: %w", 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{
|
||||||
bs: bs,
|
bs: bs,
|
||||||
cs: cs,
|
cs: cs,
|
||||||
@ -174,11 +179,19 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cg *ChainGen) nextBlockProof() (address.Address, types.ElectionProof, []*types.Ticket, error) {
|
func (cg *ChainGen) nextBlockProof() (address.Address, types.ElectionProof, []*types.Ticket, error) {
|
||||||
tick := &types.Ticket{
|
vrf := []byte("veee arrr efff")
|
||||||
VRFProof: []byte("im a ticket, promise"),
|
|
||||||
VDFProof: []byte("vdf proof"),
|
out, proof, err := vdf.Run(vrf)
|
||||||
VDFResult: []byte("verifiable and delayed"),
|
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
|
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"
|
"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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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,17 @@ 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())
|
||||||
}
|
}
|
||||||
|
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
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -17,6 +16,7 @@ import (
|
|||||||
"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"
|
||||||
"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")
|
||||||
@ -271,24 +271,7 @@ func (m *Miner) runVDF(ctx context.Context, input []byte) ([]byte, []byte, error
|
|||||||
case <-time.After(m.Delay):
|
case <-time.After(m.Delay):
|
||||||
}
|
}
|
||||||
|
|
||||||
h := sha256.Sum256(input)
|
return vdf.Run(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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Miner) scratchTicket(ctx context.Context, base *MiningBase) (*types.Ticket, error) {
|
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 {
|
if len(base.tickets) > 0 {
|
||||||
lastTicket = base.tickets[len(base.tickets)-1]
|
lastTicket = base.tickets[len(base.tickets)-1]
|
||||||
} else {
|
} else {
|
||||||
lastTicket = minTicket(base.ts)
|
lastTicket = base.ts.MinTicket()
|
||||||
}
|
}
|
||||||
|
|
||||||
vrfOut, err := m.computeVRF(ctx, lastTicket.VDFResult)
|
vrfOut, err := m.computeVRF(ctx, lastTicket.VDFResult)
|
||||||
|
Loading…
Reference in New Issue
Block a user