Skip to content

Commit

Permalink
Merge pull request #8606 from filecoin-project/asr/fix-drand-round
Browse files Browse the repository at this point in the history
Fix: drand: calculation of round from Filecoin epochs
  • Loading branch information
arajasek committed May 25, 2022
2 parents cd8a6b5 + 6924a3d commit 06279b5
Show file tree
Hide file tree
Showing 17 changed files with 135 additions and 115 deletions.
13 changes: 5 additions & 8 deletions api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,6 @@ type FullNode interface {
// ChainBlockstoreInfo returns some basic information about the blockstore
ChainBlockstoreInfo(context.Context) (map[string]interface{}, error) //perm:read

// MethodGroup: Beacon
// The Beacon method group contains methods for interacting with the random beacon (DRAND)

// BeaconGetEntry returns the beacon entry for the given filecoin epoch. If
// the entry has not yet been produced, the call will block until the entry
// becomes available
BeaconGetEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) //perm:read

// GasEstimateFeeCap estimates gas fee cap
GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) //perm:read

Expand Down Expand Up @@ -591,6 +583,11 @@ type FullNode interface {
// StateGetRandomnessFromBeacon is used to sample the beacon for randomness.
StateGetRandomnessFromBeacon(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte, tsk types.TipSetKey) (abi.Randomness, error) //perm:read

// StateGetBeaconEntry returns the beacon entry for the given filecoin epoch. If
// the entry has not yet been produced, the call will block until the entry
// becomes available
StateGetBeaconEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) //perm:read

// MethodGroup: Msig
// The Msig methods are used to interact with multisig wallets on the
// filecoin network
Expand Down
30 changes: 15 additions & 15 deletions api/mocks/mock_full.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions api/proxy_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions api/v0api/v1_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,4 +352,8 @@ func (w *WrapperV1Full) ClientQueryAsk(ctx context.Context, p peer.ID, miner add
return a.Response, nil
}

func (w *WrapperV1Full) BeaconGetEntry(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) {
return w.StateGetBeaconEntry(ctx, epoch)
}

var _ FullNode = &WrapperV1Full{}
Binary file modified build/openrpc/full.json.gz
Binary file not shown.
14 changes: 8 additions & 6 deletions chain/beacon/beacon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package beacon
import (
"context"

"github.com/filecoin-project/go-state-types/network"

"github.com/filecoin-project/go-state-types/abi"
logging "github.com/ipfs/go-log/v2"
"golang.org/x/xerrors"
Expand Down Expand Up @@ -42,10 +44,10 @@ type BeaconPoint struct {
type RandomBeacon interface {
Entry(context.Context, uint64) <-chan Response
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
MaxBeaconRoundForEpoch(abi.ChainEpoch) uint64
MaxBeaconRoundForEpoch(network.Version, abi.ChainEpoch) uint64
}

func ValidateBlockValues(bSchedule Schedule, h *types.BlockHeader, parentEpoch abi.ChainEpoch,
func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockHeader, parentEpoch abi.ChainEpoch,
prevEntry types.BeaconEntry) error {
{
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
Expand All @@ -65,7 +67,7 @@ func ValidateBlockValues(bSchedule Schedule, h *types.BlockHeader, parentEpoch a

// TODO: fork logic
b := bSchedule.BeaconForEpoch(h.Height)
maxRound := b.MaxBeaconRoundForEpoch(h.Height)
maxRound := b.MaxBeaconRoundForEpoch(nv, h.Height)
if maxRound == prevEntry.Round {
if len(h.BeaconEntries) != 0 {
return xerrors.Errorf("expected not to have any beacon entries in this block, got %d", len(h.BeaconEntries))
Expand All @@ -92,13 +94,13 @@ func ValidateBlockValues(bSchedule Schedule, h *types.BlockHeader, parentEpoch a
return nil
}

func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, epoch abi.ChainEpoch, parentEpoch abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.Version, epoch abi.ChainEpoch, parentEpoch abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
{
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
currBeacon := bSchedule.BeaconForEpoch(epoch)
if parentBeacon != currBeacon {
// Fork logic
round := currBeacon.MaxBeaconRoundForEpoch(epoch)
round := currBeacon.MaxBeaconRoundForEpoch(nv, epoch)
out := make([]types.BeaconEntry, 2)
rch := currBeacon.Entry(ctx, round-1)
res := <-rch
Expand All @@ -120,7 +122,7 @@ func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, epoch abi.Ch

start := build.Clock.Now()

maxRound := beacon.MaxBeaconRoundForEpoch(epoch)
maxRound := beacon.MaxBeaconRoundForEpoch(nv, epoch)
if maxRound == prev.Round {
return nil, nil
}
Expand Down
25 changes: 24 additions & 1 deletion chain/beacon/drand/drand.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"context"
"time"

"github.com/filecoin-project/go-state-types/network"

dchain "github.com/drand/drand/chain"
dclient "github.com/drand/drand/client"
hclient "github.com/drand/drand/client/http"
Expand Down Expand Up @@ -201,11 +203,32 @@ func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntr
return err
}

func (db *DrandBeacon) MaxBeaconRoundForEpoch(filEpoch abi.ChainEpoch) uint64 {
func (db *DrandBeacon) MaxBeaconRoundForEpoch(nv network.Version, filEpoch abi.ChainEpoch) uint64 {
// TODO: sometimes the genesis time for filecoin is zero and this goes negative
latestTs := ((uint64(filEpoch) * db.filRoundTime) + db.filGenTime) - db.filRoundTime

if nv <= network.Version15 {
return db.maxBeaconRoundV1(latestTs)
}

return db.maxBeaconRoundV2(latestTs)
}

func (db *DrandBeacon) maxBeaconRoundV1(latestTs uint64) uint64 {
dround := (latestTs - db.drandGenTime) / uint64(db.interval.Seconds())
return dround
}

func (db *DrandBeacon) maxBeaconRoundV2(latestTs uint64) uint64 {
if latestTs < db.drandGenTime {
return 1
}

fromGenesis := latestTs - db.drandGenTime
// we take the time from genesis divided by the periods in seconds, that
// gives us the number of periods since genesis. We also add +1 because
// round 1 starts at genesis time.
return fromGenesis/uint64(db.interval.Seconds()) + 1
}

var _ beacon.RandomBeacon = (*DrandBeacon)(nil)
11 changes: 11 additions & 0 deletions chain/beacon/drand/drand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"os"
"testing"

"github.com/filecoin-project/go-state-types/network"

dchain "github.com/drand/drand/chain"
hclient "github.com/drand/drand/client/http"
"github.com/stretchr/testify/assert"
Expand All @@ -25,3 +27,12 @@ func TestPrintGroupInfo(t *testing.T) {
err = chain.ToJSON(os.Stdout)
assert.NoError(t, err)
}

func TestMaxBeaconRoundForEpoch(t *testing.T) {
todayTs := uint64(1652222222)
db, err := NewDrandBeacon(todayTs, build.BlockDelaySecs, nil, build.DrandConfigs[build.DrandDevnet])
assert.NoError(t, err)
mbr15 := db.MaxBeaconRoundForEpoch(network.Version15, 100)
mbr16 := db.MaxBeaconRoundForEpoch(network.Version16, 100)
assert.Equal(t, mbr15+1, mbr16)
}
4 changes: 3 additions & 1 deletion chain/beacon/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"encoding/binary"
"time"

"github.com/filecoin-project/go-state-types/network"

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/chain/types"
"github.com/minio/blake2b-simd"
Expand Down Expand Up @@ -53,7 +55,7 @@ func (mb *mockBeacon) VerifyEntry(from types.BeaconEntry, to types.BeaconEntry)
return nil
}

func (mb *mockBeacon) MaxBeaconRoundForEpoch(epoch abi.ChainEpoch) uint64 {
func (mb *mockBeacon) MaxBeaconRoundForEpoch(nv network.Version, epoch abi.ChainEpoch) uint64 {
// offset for better testing
return uint64(epoch + 100)
}
Expand Down
5 changes: 3 additions & 2 deletions chain/consensus/filcns/filecoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock)
return nil
}

if err := beacon.ValidateBlockValues(filec.beacon, h, baseTs.Height(), *prevBeacon); err != nil {
nv := filec.sm.GetNetworkVersion(ctx, h.Height)
if err := beacon.ValidateBlockValues(filec.beacon, nv, h, baseTs.Height(), *prevBeacon); err != nil {
return xerrors.Errorf("failed to validate blocks random beacon values: %w", err)
}
return nil
Expand Down Expand Up @@ -488,7 +489,7 @@ func (filec *FilecoinEC) checkBlockMessages(ctx context.Context, b *types.FullBl
// Phase 2: (Partial) semantic validation:
// the sender exists and is an account actor, and the nonces make sense
var sender address.Address
if filec.sm.GetNetworkVersion(ctx, b.Header.Height) >= network.Version13 {
if nv >= network.Version13 {
sender, err = st.LookupID(m.From)
if err != nil {
return xerrors.Errorf("failed to lookup sender %s: %w", m.From, err)
Expand Down
4 changes: 3 additions & 1 deletion chain/rand/rand.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ func (sr *stateRand) extractBeaconEntryForEpoch(ctx context.Context, filecoinEpo
return nil, err
}

round := sr.beacon.BeaconForEpoch(filecoinEpoch).MaxBeaconRoundForEpoch(filecoinEpoch)
nv := sr.networkVersionGetter(ctx, filecoinEpoch)

round := sr.beacon.BeaconForEpoch(filecoinEpoch).MaxBeaconRoundForEpoch(nv, filecoinEpoch)

for i := 0; i < 20; i++ {
cbe := randTs.Blocks()[0].BeaconEntries
Expand Down
2 changes: 1 addition & 1 deletion chain/stmgr/actors.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule
prev = &types.BeaconEntry{}
}

entries, err := beacon.BeaconEntriesForBlock(ctx, bcs, round, ts.Height(), *prev)
entries, err := beacon.BeaconEntriesForBlock(ctx, bcs, sm.GetNetworkVersion(ctx, round), round, ts.Height(), *prev)
if err != nil {
return nil, err
}
Expand Down
53 changes: 24 additions & 29 deletions documentation/en/api-v1-unstable-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
* [Auth](#Auth)
* [AuthNew](#AuthNew)
* [AuthVerify](#AuthVerify)
* [Beacon](#Beacon)
* [BeaconGetEntry](#BeaconGetEntry)
* [Chain](#Chain)
* [ChainBlockstoreInfo](#ChainBlockstoreInfo)
* [ChainCheckBlockstore](#ChainCheckBlockstore)
Expand Down Expand Up @@ -175,6 +173,7 @@
* [StateDecodeParams](#StateDecodeParams)
* [StateEncodeParams](#StateEncodeParams)
* [StateGetActor](#StateGetActor)
* [StateGetBeaconEntry](#StateGetBeaconEntry)
* [StateGetRandomnessFromBeacon](#StateGetRandomnessFromBeacon)
* [StateGetRandomnessFromTickets](#StateGetRandomnessFromTickets)
* [StateListActors](#StateListActors)
Expand Down Expand Up @@ -340,33 +339,6 @@ Response:
]
```

## Beacon
The Beacon method group contains methods for interacting with the random beacon (DRAND)


### BeaconGetEntry
BeaconGetEntry returns the beacon entry for the given filecoin epoch. If
the entry has not yet been produced, the call will block until the entry
becomes available


Perms: read

Inputs:
```json
[
10101
]
```

Response:
```json
{
"Round": 42,
"Data": "Ynl0ZSBhcnJheQ=="
}
```

## Chain
The Chain method group contains methods for interacting with the
blockchain, but that do not require any form of state computation.
Expand Down Expand Up @@ -5641,6 +5613,29 @@ Response:
}
```

### StateGetBeaconEntry
StateGetBeaconEntry returns the beacon entry for the given filecoin epoch. If
the entry has not yet been produced, the call will block until the entry
becomes available


Perms: read

Inputs:
```json
[
10101
]
```

Response:
```json
{
"Round": 42,
"Data": "Ynl0ZSBhcnJheQ=="
}
```

### StateGetRandomnessFromBeacon
StateGetRandomnessFromBeacon is used to sample the beacon for randomness.

Expand Down
Loading

0 comments on commit 06279b5

Please sign in to comment.