Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions cmd/f3/fake_ec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package main

import (
"context"
"encoding/binary"
"time"

"golang.org/x/crypto/blake2b"
"golang.org/x/xerrors"

"github.com/filecoin-project/go-f3"
"github.com/filecoin-project/go-f3/gpbft"
)

type FakeEC struct {
seed []byte
initialPowerTable gpbft.PowerEntries

ecPeriod time.Duration
ecStart time.Time
}

type Tipset struct {
tsk []byte
epoch int64
timestamp time.Time
}

func (ts *Tipset) Key() gpbft.TipSetKey {
return ts.tsk
}

func (ts *Tipset) Epoch() int64 {
return ts.epoch
}
func (ts *Tipset) Beacon() []byte {
h, err := blake2b.New256([]byte("beacon"))
if err != nil {
panic(err)
}
h.Write(ts.tsk)
return h.Sum(nil)
}

func (ts *Tipset) Timestamp() time.Time {
return ts.timestamp
}

func NewFakeEC(seed uint64, m f3.Manifest) *FakeEC {
return &FakeEC{
seed: binary.BigEndian.AppendUint64(nil, seed),
initialPowerTable: m.InitialPowerTable,

ecPeriod: m.ECPeriod,
ecStart: m.ECBoostrapTimestamp,
}
}

func (ec *FakeEC) genTipset(epoch int64) *Tipset {
h, err := blake2b.New256(ec.seed)
if err != nil {
panic(err)
}
h.Write(binary.BigEndian.AppendUint64(nil, uint64(epoch)))
rng := h.Sum(nil)
var size uint8
size, rng = rng[0]%8, rng[1:]
_ = rng
tsk := make([]byte, 0, size*gpbft.CID_MAX_LEN)

if size == 0 {
return nil
}

for i := uint8(0); i < size; i++ {
h.Write([]byte{1})
digest := h.Sum(nil)
if i == 0 {
//encode epoch in the first block hash
binary.BigEndian.PutUint64(digest[32-8:], uint64(epoch))
}
tsk = append(tsk, gpbft.DigestToCid(digest)...)
}
return &Tipset{
tsk: tsk,
epoch: epoch,
timestamp: ec.ecStart.Add(time.Duration(epoch) * ec.ecPeriod),
}
}

func (ec *FakeEC) currentEpoch() int64 {
return int64(time.Since(ec.ecStart) / ec.ecPeriod)
}

// GetTipsetByHeight should return a tipset or nil/empty byte array if it does not exists
func (ec *FakeEC) GetTipsetByEpoch(ctx context.Context, epoch int64) (f3.TipSet, error) {
if ec.currentEpoch() < epoch {
return nil, xerrors.Errorf("does not yet exist")
}
ts := ec.genTipset(epoch)
for ts == nil {
epoch--
ts = ec.genTipset(epoch - 1)
}
return ts, nil
}

func (ec *FakeEC) GetParent(ctx context.Context, ts f3.TipSet) (f3.TipSet, error) {

for epoch := ts.Epoch() - 1; epoch > 0; epoch-- {
ts, err := ec.GetTipsetByEpoch(ctx, epoch)
if err != nil {
return nil, xerrors.Errorf("walking back tipsets: %w", err)
}
if ts != nil {
return ts, nil
}
}
return nil, xerrors.Errorf("parent not found")
}

func (ec *FakeEC) GetHead(ctx context.Context) (f3.TipSet, error) {
return ec.GetTipsetByEpoch(ctx, ec.currentEpoch())
}

func (ec *FakeEC) GetPowerTable(ctx context.Context, tsk gpbft.TipSetKey) (gpbft.PowerEntries, error) {
return ec.initialPowerTable, nil
}

func (ec *FakeEC) GetTipset(ctx context.Context, tsk gpbft.TipSetKey) (f3.TipSet, error) {
epoch := binary.BigEndian.Uint64(tsk[6+32-8 : 6+32])
return ec.genTipset(int64(epoch)), nil
}
12 changes: 5 additions & 7 deletions cmd/f3/manifest.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package main

import (
"crypto/rand"
"encoding/json"
"fmt"
"math/big"
"os"

Expand All @@ -30,22 +28,22 @@ var manifestGenCmd = cli.Command{
Value: 2,
},
},

Action: func(c *cli.Context) error {
path := c.String("manifest")
rng := make([]byte, 4)
_, _ = rand.Read(rng)
var m f3.Manifest
m.NetworkName = gpbft.NetworkName(fmt.Sprintf("localnet-%X", rng))
m := f3.LocalnetManifest()

fsig := signing.NewFakeBackend()
for i := 0; i < c.Int("N"); i++ {
pubkey, _ := fsig.GenerateKey()

m.InitialPowerTable = append(m.InitialPowerTable, gpbft.PowerEntry{
ID: gpbft.ActorID(i),
PubKey: pubkey,
Power: big.NewInt(1),
Power: big.NewInt(1000),
})
}

f, err := os.OpenFile(path, os.O_WRONLY, 0666)
if err != nil {
return xerrors.Errorf("opening manifest file for writing: %w", err)
Expand Down
5 changes: 4 additions & 1 deletion cmd/f3/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ var runCmd = cli.Command{
signingBackend := &fakeSigner{*signing.NewFakeBackend()}
id := c.Uint64("id")
signingBackend.Allow(int(id))
module, err := f3.New(ctx, gpbft.ActorID(id), m, ds, h, ps, signingBackend, signingBackend, nil, log)

ec := NewFakeEC(1, m)
module, err := f3.New(ctx, gpbft.ActorID(id), m, ds, h, ps,
signingBackend, signingBackend, ec, log)
if err != nil {
return xerrors.Errorf("creating module: %w", err)
}
Expand Down
21 changes: 20 additions & 1 deletion f3.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"errors"
"time"

"github.com/filecoin-project/go-f3/certstore"
"github.com/filecoin-project/go-f3/gpbft"
Expand Down Expand Up @@ -36,6 +37,7 @@ type client struct {
certstore *certstore.Store
id gpbft.ActorID
nn gpbft.NetworkName
ec ECBackend

gpbft.Verifier
gpbft.SignerWithMarshaler
Expand Down Expand Up @@ -110,6 +112,7 @@ func New(ctx context.Context, id gpbft.ActorID, manifest Manifest, ds datastore.

client: &client{
certstore: cs,
ec: ec,
nn: manifest.NetworkName,
id: id,
Verifier: verif,
Expand Down Expand Up @@ -231,7 +234,23 @@ loop:
return multierr.Append(err, ctx.Err())
}

type ECBackend interface{}
type ECBackend interface {
// GetTipsetByEpoch should return a tipset before the one requested if the requested
// tipset does not exist due to null epochs
GetTipsetByEpoch(ctx context.Context, epoch int64) (TipSet, error)
GetTipset(context.Context, gpbft.TipSetKey) (TipSet, error)
GetHead(context.Context) (TipSet, error)
GetParent(context.Context, TipSet) (TipSet, error)

GetPowerTable(context.Context, gpbft.TipSetKey) (gpbft.PowerEntries, error)
}

type TipSet interface {
Key() gpbft.TipSetKey
Beacon() []byte
Epoch() int64
Timestamp() time.Time
}

type Logger interface {
Debug(args ...interface{})
Expand Down
25 changes: 19 additions & 6 deletions gpbft/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package gpbft

import (
"bytes"
"encoding/base32"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"strings"
Expand Down Expand Up @@ -36,9 +36,17 @@ func MakeCid(data []byte) []byte {
// We construct this CID manually to avoid depending on go-cid (it's also a _bit_ faster).
digest := blake2b.Sum256(data)

out := make([]byte, 0, 38)
return DigestToCid(digest[:])
}

// DigestToCid turns a digest into CBOR + blake2b-256 CID
func DigestToCid(digest []byte) []byte {
if len(digest) != 32 {
panic(fmt.Sprintf("wrong length of digest, expected 32, got %d", len(digest)))
}
out := make([]byte, 0, CID_MAX_LEN)
out = append(out, cidPrefix...)
out = append(out, digest[:]...)
out = append(out, digest...)
return out
}

Expand Down Expand Up @@ -102,8 +110,9 @@ func (ts *TipSet) String() string {
if ts == nil {
return "<nil>"
}
encTs := base32.StdEncoding.EncodeToString(ts.Key)

return fmt.Sprintf("%d@%s", ts.Epoch, hex.EncodeToString(ts.Key))
return fmt.Sprintf("%s@%d", encTs[:max(16, len(encTs))], ts.Epoch)
}

// A chain of tipsets comprising a base (the last finalised tipset from which the chain extends).
Expand Down Expand Up @@ -263,7 +272,7 @@ func (c ECChain) Validate() error {
return fmt.Errorf("tipset %d: %w", i, err)
}
if ts.Epoch <= lastEpoch {
return errors.New("chain must have increasing epochs")
return fmt.Errorf("chain must have increasing epochs %d <= %d", ts.Epoch, lastEpoch)
}
lastEpoch = ts.Epoch
}
Expand Down Expand Up @@ -300,5 +309,9 @@ func (c ECChain) String() string {
}
}
b.WriteString("]")
return b.String()
str := b.String()
if len(str) > 77 {
str = str[:77] + "..."
}
return str
}
1 change: 0 additions & 1 deletion gpbft/powertable.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ func (p PowerEntries) Scaled() (scaled []uint16, total uint16, err error) {

// NewPowerTable creates a new PowerTable from a slice of PowerEntry .
// It is more efficient than Add, as it only needs to sort the entries once.
// Note that the function takes ownership of the slice - it must not be modified afterwards.
func NewPowerTable() *PowerTable {
return &PowerTable{
Lookup: make(map[ActorID]int),
Expand Down
Loading