Skip to content

Commit 9e080ad

Browse files
committed
feat: implement FIP-0063
1 parent 1324822 commit 9e080ad

14 files changed

+182
-139
lines changed

build/drand.go

+19
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,18 @@ const (
2727
DrandDevnet
2828
DrandLocalnet
2929
DrandIncentinet
30+
DrandQuicknet
3031
)
3132

3233
var DrandConfigs = map[DrandEnum]dtypes.DrandConfig{
3334
DrandMainnet: {
35+
//FUCK: add storswift endpoint
3436
Servers: []string{
3537
"https://api.drand.sh",
3638
"https://api2.drand.sh",
3739
"https://api3.drand.sh",
3840
"https://drand.cloudflare.com",
41+
//"https://api.drand.secureweb3.com:6875", // Storswift
3942
},
4043
Relays: []string{
4144
"/dnsaddr/api.drand.sh/",
@@ -44,6 +47,22 @@ var DrandConfigs = map[DrandEnum]dtypes.DrandConfig{
4447
},
4548
ChainInfoJSON: `{"public_key":"868f005eb8e6e4ca0a47c8a77ceaa5309a47978a7c71bc5cce96366b5d7a569937c529eeda66c7293784a9402801af31","period":30,"genesis_time":1595431050,"hash":"8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce","groupHash":"176f93498eac9ca337150b46d21dd58673ea4e3581185f869672e59fa4cb390a"}`,
4649
},
50+
DrandQuicknet: {
51+
//FUCK: add storswift endpoint
52+
Servers: []string{
53+
"https://api.drand.sh",
54+
"https://api2.drand.sh",
55+
"https://api3.drand.sh",
56+
"https://drand.cloudflare.com",
57+
//"https://api.drand.secureweb3.com:6875", // Storswift
58+
},
59+
Relays: []string{
60+
"/dnsaddr/api.drand.sh/",
61+
"/dnsaddr/api2.drand.sh/",
62+
"/dnsaddr/api3.drand.sh/",
63+
},
64+
ChainInfoJSON: `{"public_key":"83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a","period":3,"genesis_time":1692803367,"hash":"52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971","groupHash":"f477d5c89f21a17c863a7f937c6a6d15859414d2be09cd448d4279af331c5d3e","schemeID":"bls-unchained-g1-rfc9380","metadata":{"beaconID":"quicknet"}}`,
65+
},
4766
DrandTestnet: {
4867
Servers: []string{
4968
"https://pl-eu.testnet.drand.sh",

build/params_2k.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ const UpgradeWatermelonFixHeight = -100
7676
const UpgradeWatermelonFix2Height = -101
7777

7878
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
79-
0: DrandMainnet,
79+
0: DrandMainnet,
80+
UpgradePineappleHeight: DrandQuicknet,
8081
}
8182

8283
var SupportedProofTypes = []abi.RegisteredSealProof{

build/params_butterfly.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import (
1616
)
1717

1818
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
19-
0: DrandMainnet,
19+
0: DrandMainnet,
20+
UpgradePineappleHeight: DrandQuicknet,
2021
}
2122

2223
const GenesisNetworkVersion = network.Version20

build/params_calibnet.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import (
1919
)
2020

2121
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
22-
0: DrandMainnet,
22+
0: DrandMainnet,
23+
UpgradePineappleHeight: DrandQuicknet,
2324
}
2425

2526
const GenesisNetworkVersion = network.Version0

build/params_interop.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ const UpgradeWatermelonFixHeight = -1
6464
const UpgradeWatermelonFix2Height = -2
6565

6666
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
67-
0: DrandMainnet,
67+
0: DrandMainnet,
68+
UpgradePineappleHeight: DrandQuicknet,
6869
}
6970

7071
var SupportedProofTypes = []abi.RegisteredSealProof{

build/params_mainnet.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import (
1515
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
1616
)
1717

18+
// FUCK: add a block to update to the new network
1819
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
19-
0: DrandIncentinet,
20-
UpgradeSmokeHeight: DrandMainnet,
20+
0: DrandIncentinet,
21+
UpgradeSmokeHeight: DrandMainnet,
22+
UpgradePineappleHeight: DrandQuicknet,
2123
}
2224

2325
var NetworkBundle = "mainnet"

build/params_testground.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ var (
115115
UpgradePineappleHeight abi.ChainEpoch = -26
116116

117117
DrandSchedule = map[abi.ChainEpoch]DrandEnum{
118-
0: DrandMainnet,
118+
0: DrandMainnet,
119+
UpgradePineappleHeight: DrandQuicknet,
119120
}
120121

121122
GenesisNetworkVersion = network.Version0

chain/beacon/beacon.go

+69-41
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ type Schedule []BeaconPoint
2525
func (bs Schedule) BeaconForEpoch(e abi.ChainEpoch) RandomBeacon {
2626
for i := len(bs) - 1; i >= 0; i-- {
2727
bp := bs[i]
28-
if e >= bp.Start {
28+
// The new network version kicks in at one _after_ the upgrade height itself,
29+
// so we switch to the new beacon when _strictly_ greater than bp.Start
30+
if e > bp.Start {
2931
return bp.Beacon
3032
}
3133
}
@@ -43,31 +45,31 @@ type BeaconPoint struct {
4345
// been posted on chain.
4446
type RandomBeacon interface {
4547
Entry(context.Context, uint64) <-chan Response
46-
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
48+
VerifyEntry(entry types.BeaconEntry, prevEntrySig []byte) error
4749
MaxBeaconRoundForEpoch(network.Version, abi.ChainEpoch) uint64
4850
}
4951

5052
func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockHeader, parentEpoch abi.ChainEpoch,
5153
prevEntry types.BeaconEntry) error {
52-
{
53-
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
54-
currBeacon := bSchedule.BeaconForEpoch(h.Height)
55-
if parentBeacon != currBeacon {
56-
if len(h.BeaconEntries) != 2 {
57-
return xerrors.Errorf("expected two beacon entries at beacon fork, got %d", len(h.BeaconEntries))
58-
}
59-
err := currBeacon.VerifyEntry(h.BeaconEntries[1], h.BeaconEntries[0])
60-
if err != nil {
61-
return xerrors.Errorf("beacon at fork point invalid: (%v, %v): %w",
62-
h.BeaconEntries[1], h.BeaconEntries[0], err)
63-
}
64-
return nil
65-
}
66-
}
54+
//{
55+
// parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
56+
// currBeacon := bSchedule.BeaconForEpoch(h.Height)
57+
// if parentBeacon != currBeacon {
58+
// if len(h.BeaconEntries) != 2 {
59+
// return xerrors.Errorf("expected two beacon entries at beacon fork, got %d", len(h.BeaconEntries))
60+
// }
61+
// err := currBeacon.VerifyEntry(h.BeaconEntries[1], h.BeaconEntries[0])
62+
// if err != nil {
63+
// return xerrors.Errorf("beacon at fork point invalid: (%v, %v): %w",
64+
// h.BeaconEntries[1], h.BeaconEntries[0], err)
65+
// }
66+
// return nil
67+
// }
68+
//}
6769

68-
// TODO: fork logic
6970
b := bSchedule.BeaconForEpoch(h.Height)
7071
maxRound := b.MaxBeaconRoundForEpoch(nv, h.Height)
72+
// We don't expect to ever actually meet this condition
7173
if maxRound == prevEntry.Round {
7274
if len(h.BeaconEntries) != 0 {
7375
return xerrors.Errorf("expected not to have any beacon entries in this block, got %d", len(h.BeaconEntries))
@@ -79,13 +81,23 @@ func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockH
7981
return xerrors.Errorf("expected to have beacon entries in this block, but didn't find any")
8082
}
8183

84+
if nv >= network.Version22 && len(h.BeaconEntries) != 1 {
85+
return xerrors.Errorf("exactly one beacon entry expected for network version %d, got %d entries", nv, len(h.BeaconEntries))
86+
}
87+
88+
if nv < network.Version22 && prevEntry.Round == 0 {
89+
// This basically means that the drand entry of the first non-genesis tipset isn't verified IF we are starting on Drand mainnet (the "chained" drand)
90+
// Networks that start on drand quicknet, or other unchained randomness sources, will still verify it
91+
return nil
92+
}
93+
8294
last := h.BeaconEntries[len(h.BeaconEntries)-1]
8395
if last.Round != maxRound {
8496
return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round)
8597
}
8698

8799
for i, e := range h.BeaconEntries {
88-
if err := b.VerifyEntry(e, prevEntry); err != nil {
100+
if err := b.VerifyEntry(e, prevEntry.Data); err != nil {
89101
return xerrors.Errorf("beacon entry %d (%d - %x (%d)) was invalid: %w", i, e.Round, e.Data, len(e.Data), err)
90102
}
91103
prevEntry = e
@@ -95,34 +107,35 @@ func ValidateBlockValues(bSchedule Schedule, nv network.Version, h *types.BlockH
95107
}
96108

97109
func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.Version, epoch abi.ChainEpoch, parentEpoch abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
98-
{
99-
parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
100-
currBeacon := bSchedule.BeaconForEpoch(epoch)
101-
if parentBeacon != currBeacon {
102-
// Fork logic
103-
round := currBeacon.MaxBeaconRoundForEpoch(nv, epoch)
104-
out := make([]types.BeaconEntry, 2)
105-
rch := currBeacon.Entry(ctx, round-1)
106-
res := <-rch
107-
if res.Err != nil {
108-
return nil, xerrors.Errorf("getting entry %d returned error: %w", round-1, res.Err)
109-
}
110-
out[0] = res.Entry
111-
rch = currBeacon.Entry(ctx, round)
112-
res = <-rch
113-
if res.Err != nil {
114-
return nil, xerrors.Errorf("getting entry %d returned error: %w", round, res.Err)
115-
}
116-
out[1] = res.Entry
117-
return out, nil
118-
}
119-
}
110+
//{
111+
// parentBeacon := bSchedule.BeaconForEpoch(parentEpoch)
112+
// currBeacon := bSchedule.BeaconForEpoch(epoch)
113+
// if parentBeacon != currBeacon {
114+
// // Fork logic
115+
// round := currBeacon.MaxBeaconRoundForEpoch(nv, epoch)
116+
// out := make([]types.BeaconEntry, 2)
117+
// rch := currBeacon.Entry(ctx, prev.Round)
118+
// res := <-rch
119+
// if res.Err != nil {
120+
// return nil, xerrors.Errorf("getting entry %d returned error: %w", round-1, res.Err)
121+
// }
122+
// out[0] = res.Entry
123+
// rch = currBeacon.Entry(ctx, round)
124+
// res = <-rch
125+
// if res.Err != nil {
126+
// return nil, xerrors.Errorf("getting entry %d returned error: %w", round, res.Err)
127+
// }
128+
// out[1] = res.Entry
129+
// return out, nil
130+
// }
131+
//}
120132

121133
beacon := bSchedule.BeaconForEpoch(epoch)
122134

123135
start := build.Clock.Now()
124136

125137
maxRound := beacon.MaxBeaconRoundForEpoch(nv, epoch)
138+
// We don't expect this to ever be the case
126139
if maxRound == prev.Round {
127140
return nil, nil
128141
}
@@ -132,6 +145,21 @@ func BeaconEntriesForBlock(ctx context.Context, bSchedule Schedule, nv network.V
132145
prev.Round = maxRound - 1
133146
}
134147

148+
// We only ever need one entry after nv22 (FIP-0063)
149+
if nv >= network.Version22 {
150+
rch := beacon.Entry(ctx, maxRound)
151+
select {
152+
case resp := <-rch:
153+
if resp.Err != nil {
154+
return nil, xerrors.Errorf("beacon entry request returned error: %w", resp.Err)
155+
}
156+
157+
return []types.BeaconEntry{resp.Entry}, nil
158+
case <-ctx.Done():
159+
return nil, xerrors.Errorf("context timed out waiting on beacon entry to come back for epoch %d: %w", epoch, ctx.Err())
160+
}
161+
}
162+
135163
cur := maxRound
136164
var out []types.BeaconEntry
137165
for cur > prev.Round {

chain/beacon/drand/drand.go

+29-20
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
dchain "github.com/drand/drand/chain"
99
dclient "github.com/drand/drand/client"
1010
hclient "github.com/drand/drand/client/http"
11-
"github.com/drand/drand/common/scheme"
11+
dcrypto "github.com/drand/drand/crypto"
1212
dlog "github.com/drand/drand/log"
1313
gclient "github.com/drand/drand/lp2p/client"
1414
"github.com/drand/kyber"
@@ -47,6 +47,7 @@ type DrandBeacon struct {
4747
drandGenTime uint64
4848
filGenTime uint64
4949
filRoundTime uint64
50+
scheme *dcrypto.Scheme
5051

5152
localCache *lru.Cache[uint64, *types.BeaconEntry]
5253
}
@@ -68,6 +69,10 @@ func (l *logger) Named(s string) dlog.Logger {
6869
return &logger{l.SugaredLogger.Named(s)}
6970
}
7071

72+
func (l *logger) AddCallerSkip(skip int) dlog.Logger {
73+
return l
74+
}
75+
7176
func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub, config dtypes.DrandConfig) (*DrandBeacon, error) {
7277
if genesisTs == 0 {
7378
panic("what are you doing this cant be zero")
@@ -116,6 +121,11 @@ func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub, config dtypes
116121
localCache: lc,
117122
}
118123

124+
sch, err := dcrypto.GetSchemeByIDWithDefault(drandChain.Scheme)
125+
if err != nil {
126+
return nil, err
127+
}
128+
db.scheme = sch
119129
db.pubkey = drandChain.PublicKey
120130
db.interval = drandChain.Period
121131
db.drandGenTime = uint64(drandChain.GenesisTime)
@@ -164,33 +174,28 @@ func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry {
164174
return v
165175
}
166176

167-
func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntry) error {
168-
if prev.Round == 0 {
169-
// TODO handle genesis better
170-
return nil
171-
}
172-
173-
if curr.Round != prev.Round+1 {
174-
return xerrors.Errorf("invalid beacon entry: cur (%d) != prev (%d) + 1", curr.Round, prev.Round)
175-
}
176-
177-
if be := db.getCachedValue(curr.Round); be != nil {
178-
if !bytes.Equal(curr.Data, be.Data) {
177+
func (db *DrandBeacon) VerifyEntry(entry types.BeaconEntry, prevEntrySig []byte) error {
178+
if be := db.getCachedValue(entry.Round); be != nil {
179+
if !bytes.Equal(entry.Data, be.Data) {
179180
return xerrors.New("invalid beacon value, does not match cached good value")
180181
}
181182
// return no error if the value is in the cache already
182183
return nil
183184
}
184185
b := &dchain.Beacon{
185-
PreviousSig: prev.Data,
186-
Round: curr.Round,
187-
Signature: curr.Data,
186+
PreviousSig: prevEntrySig,
187+
Round: entry.Round,
188+
Signature: entry.Data,
188189
}
189-
err := dchain.NewVerifier(scheme.GetSchemeFromEnv()).VerifyBeacon(*b, db.pubkey)
190-
if err == nil {
191-
db.cacheValue(curr)
190+
191+
err := db.scheme.VerifyBeacon(b, db.pubkey)
192+
if err != nil {
193+
return xerrors.Errorf("failed to verify beacon: %w", err)
192194
}
193-
return err
195+
196+
db.cacheValue(entry)
197+
198+
return nil
194199
}
195200

196201
func (db *DrandBeacon) MaxBeaconRoundForEpoch(nv network.Version, filEpoch abi.ChainEpoch) uint64 {
@@ -218,6 +223,10 @@ func (db *DrandBeacon) maxBeaconRoundV2(latestTs uint64) uint64 {
218223
// we take the time from genesis divided by the periods in seconds, that
219224
// gives us the number of periods since genesis. We also add +1 because
220225
// round 1 starts at genesis time.
226+
// TODO: Confirm that we are still fetching the right round for Filecoin
227+
// It should:
228+
// - be available at LEAST 30 (?) seconds before block creation
229+
// - not have been available any more than 30 (???) seconds before block creation
221230
return fromGenesis/uint64(db.interval.Seconds()) + 1
222231
}
223232

chain/beacon/mock.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func (mb *mockBeacon) Entry(ctx context.Context, index uint64) <-chan Response {
4747
return out
4848
}
4949

50-
func (mb *mockBeacon) VerifyEntry(from types.BeaconEntry, to types.BeaconEntry) error {
50+
func (mb *mockBeacon) VerifyEntry(from types.BeaconEntry, _prevEntrySig []byte) error {
5151
// TODO: cache this, especially for bls
5252
oe := mb.entryForIndex(from.Round)
5353
if !bytes.Equal(from.Data, oe.Data) {

chain/consensus/filcns/filecoin.go

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock)
129129
return xerrors.Errorf("failed to get lookback tipset for block: %w", err)
130130
}
131131

132+
// TODO: Optimization: We don't need to get latest beacon entry after nv22 and can supply null instead
132133
prevBeacon, err := filec.store.GetLatestBeaconEntry(ctx, baseTs)
133134
if err != nil {
134135
return xerrors.Errorf("failed to get latest beacon entry: %w", err)

0 commit comments

Comments
 (0)