Skip to content

Commit ce61a37

Browse files
committed
Consistently use Merkle Tree root as ECChain key
Remove the custom ECChain key generation inside `gpbft` in favor of consistently using Merkle Tree root of chain as key. This key is already used for signature payload generation, and in the new chain exchange as a key to identify a chain. Doing so enables signature validation of messages without having to know the chain. Consistent use of Merkle Tree root hash as key enables a number of simplifications across the repo: * unblocks the unification of validation logic between partial and full validators, since caching can use a consistent way to identify messages whether they're partial or not. * reduce various type gymnastics across root and chain exchange packages by repurposing gpbft ECChain Key. The work here also introduces lazy-loading for the ECChain, which requires ECChain type to be converted to `struct`, and be passed by pointer. As part of this work, TipSet is also turned into a pointer consistently across various interfaces. The ECChain receiver functions are adopted to accommodate potential nil values for a chain. Fixes #825
1 parent 7e86edb commit ce61a37

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+888
-691
lines changed

cbor_gen.go

Lines changed: 7 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

certchain/certchain.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var (
2121
type FinalityCertificateProvider func(context.Context, uint64) (*certs.FinalityCertificate, error)
2222

2323
type tipSetWithPowerTable struct {
24-
gpbft.TipSet
24+
*gpbft.TipSet
2525
Beacon []byte
2626
PowerTable *gpbft.PowerTable
2727
}
@@ -31,7 +31,7 @@ type CertChain struct {
3131

3232
rng *rand.Rand
3333
certificates []*certs.FinalityCertificate
34-
generateProposal func(context.Context, uint64) (gpbft.ECChain, error)
34+
generateProposal func(context.Context, uint64) (*gpbft.ECChain, error)
3535
}
3636

3737
func New(o ...Option) (*CertChain, error) {
@@ -63,7 +63,7 @@ func (cc *CertChain) GetCommittee(instance uint64) (*gpbft.Committee, error) {
6363
return cc.getCommittee(tspt)
6464
}
6565

66-
func (cc *CertChain) GetProposal(instance uint64) (*gpbft.SupplementalData, gpbft.ECChain, error) {
66+
func (cc *CertChain) GetProposal(instance uint64) (*gpbft.SupplementalData, *gpbft.ECChain, error) {
6767
//TODO refactor ProposalProvider in gpbft to take context.
6868
ctx := context.TODO()
6969
proposal, err := cc.generateProposal(ctx, instance)
@@ -120,7 +120,7 @@ func (cc *CertChain) getTipSetWithPowerTableByEpoch(ctx context.Context, epoch i
120120
return nil, err
121121
}
122122
return &tipSetWithPowerTable{
123-
TipSet: gpbft.TipSet{
123+
TipSet: &gpbft.TipSet{
124124
Epoch: epoch,
125125
Key: ts.Key(),
126126
PowerTable: ptCid,
@@ -130,12 +130,12 @@ func (cc *CertChain) getTipSetWithPowerTableByEpoch(ctx context.Context, epoch i
130130
}, nil
131131
}
132132

133-
func (cc *CertChain) generateRandomProposal(ctx context.Context, base gpbft.TipSet, len int) (gpbft.ECChain, error) {
133+
func (cc *CertChain) generateRandomProposal(ctx context.Context, base *gpbft.TipSet, len int) (*gpbft.ECChain, error) {
134134
if len == 0 {
135135
return gpbft.NewChain(base)
136136
}
137137

138-
suffix := make([]gpbft.TipSet, len-1)
138+
suffix := make([]*gpbft.TipSet, len-1)
139139
for i := range suffix {
140140
epoch := base.Epoch + 1 + int64(i)
141141
gTS, err := cc.getTipSetWithPowerTableByEpoch(ctx, epoch)
@@ -250,7 +250,7 @@ func (cc *CertChain) sign(ctx context.Context, committee *gpbft.Committee, paylo
250250
func (cc *CertChain) Generate(ctx context.Context, length uint64) ([]*certs.FinalityCertificate, error) {
251251
cc.certificates = make([]*certs.FinalityCertificate, 0, length)
252252

253-
cc.generateProposal = func(ctx context.Context, instance uint64) (gpbft.ECChain, error) {
253+
cc.generateProposal = func(ctx context.Context, instance uint64) (*gpbft.ECChain, error) {
254254
var baseEpoch int64
255255
if instance == cc.m.InitialInstance {
256256
baseEpoch = cc.m.BootstrapEpoch - cc.m.EC.Finality

certexchange/polling/common_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const TestNetworkName gpbft.NetworkName = "testnet"
1717

1818
func MakeCertificate(t *testing.T, rng *rand.Rand, tsg *sim.TipSetGenerator, backend signing.Backend, base *gpbft.TipSet, instance uint64, powerTable, nextPowerTable gpbft.PowerEntries) *certs.FinalityCertificate {
1919
chainLen := rng.Intn(23) + 1
20-
chain, err := gpbft.NewChain(*base)
20+
chain, err := gpbft.NewChain(base)
2121
require.NoError(t, err)
2222

2323
for i := 0; i < chainLen; i++ {

certexchange/protocol_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,23 @@ func TestClientServer(t *testing.T) {
5555
cs, err := certstore.CreateStore(ctx, ds, 0, pt)
5656
require.NoError(t, err)
5757

58-
cert := &certs.FinalityCertificate{GPBFTInstance: 0, SupplementalData: supp, ECChain: gpbft.ECChain{{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: pcid}}}
58+
cert := &certs.FinalityCertificate{GPBFTInstance: 0, SupplementalData: supp,
59+
ECChain: &gpbft.ECChain{
60+
TipSets: []*gpbft.TipSet{
61+
{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: pcid},
62+
},
63+
},
64+
}
5965
err = cs.Put(ctx, cert)
6066
require.NoError(t, err)
6167

62-
cert = &certs.FinalityCertificate{GPBFTInstance: 1, SupplementalData: supp, ECChain: gpbft.ECChain{{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: pcid}}}
68+
cert = &certs.FinalityCertificate{GPBFTInstance: 1, SupplementalData: supp,
69+
ECChain: &gpbft.ECChain{
70+
TipSets: []*gpbft.TipSet{
71+
{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: pcid},
72+
},
73+
},
74+
}
6375
err = cs.Put(ctx, cert)
6476
require.NoError(t, err)
6577

@@ -149,7 +161,13 @@ func TestClientServer(t *testing.T) {
149161
}
150162

151163
// Until we've added a new certificate.
152-
cert = &certs.FinalityCertificate{GPBFTInstance: 2, SupplementalData: supp, ECChain: gpbft.ECChain{{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: pcid}}}
164+
cert = &certs.FinalityCertificate{GPBFTInstance: 2, SupplementalData: supp,
165+
ECChain: &gpbft.ECChain{
166+
TipSets: []*gpbft.TipSet{
167+
{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: pcid},
168+
},
169+
},
170+
}
153171
require.NoError(t, cs.Put(ctx, cert))
154172

155173
{

certs/cbor_gen.go

Lines changed: 16 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

certs/certs.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type FinalityCertificate struct {
3636
GPBFTInstance uint64
3737
// The ECChain finalized during this instance, starting with the last tipset finalized in
3838
// the previous instance.
39-
ECChain gpbft.ECChain
39+
ECChain *gpbft.ECChain
4040
// Additional data signed by the participants in this instance. Currently used to certify
4141
// the power table used in the next instance.
4242
SupplementalData gpbft.SupplementalData
@@ -89,7 +89,7 @@ func NewFinalityCertificate(powerDelta PowerTableDiff, justification *gpbft.Just
8989
// finalized, the instance of the first invalid finality certificate, and the power table that
9090
// should be used to validate that finality certificate, along with the error encountered.
9191
func ValidateFinalityCertificates(verifier gpbft.Verifier, network gpbft.NetworkName, prevPowerTable gpbft.PowerEntries, nextInstance uint64, base *gpbft.TipSet,
92-
certs ...*FinalityCertificate) (_nextInstance uint64, chain gpbft.ECChain, newPowerTable gpbft.PowerEntries, err error) {
92+
certs ...*FinalityCertificate) (_nextInstance uint64, chain *gpbft.ECChain, newPowerTable gpbft.PowerEntries, err error) {
9393
for _, cert := range certs {
9494
if cert.GPBFTInstance != nextInstance {
9595
return nextInstance, chain, prevPowerTable, fmt.Errorf("expected instance %d, found instance %d", nextInstance, cert.GPBFTInstance)
@@ -133,7 +133,7 @@ func ValidateFinalityCertificates(verifier gpbft.Verifier, network gpbft.Network
133133
cert.GPBFTInstance, cert.SupplementalData.PowerTable, powerTableCid)
134134
}
135135
nextInstance++
136-
chain = append(chain, cert.ECChain.Suffix()...)
136+
chain = chain.Append(cert.ECChain.Suffix()...)
137137
prevPowerTable = newPowerTable
138138
base = cert.ECChain.Head()
139139
}

certs/certs_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ func TestFinalityCertificates(t *testing.T) {
212212

213213
rng := rand.New(rand.NewSource(1234))
214214
tsg := sim.NewTipSetGenerator(rng.Uint64())
215-
base := gpbft.TipSet{Epoch: 0, Key: tsg.Sample(), PowerTable: tableCid}
215+
base := &gpbft.TipSet{Epoch: 0, Key: tsg.Sample(), PowerTable: tableCid}
216216

217217
certificates := make([]*certs.FinalityCertificate, 10)
218218
powerTables := make([]gpbft.PowerEntries, 10)
@@ -224,14 +224,14 @@ func TestFinalityCertificates(t *testing.T) {
224224
cert, err := certs.NewFinalityCertificate(certs.MakePowerTableDiff(powerTables[i], powerTable), justification)
225225
require.NoError(t, err)
226226
certificates[i] = cert
227-
base = *justification.Vote.Value.Head()
227+
base = justification.Vote.Value.Head()
228228
}
229229

230230
// Validate one.
231231
nextInstance, chain, newPowerTable, err := certs.ValidateFinalityCertificates(backend, networkName, powerTables[0], 0, certificates[0].ECChain.Base(), certificates[0])
232232
require.NoError(t, err)
233233
require.EqualValues(t, 1, nextInstance)
234-
require.True(t, chain.Eq(certificates[0].ECChain.Suffix()))
234+
require.Equal(t, chain.TipSets, certificates[0].ECChain.Suffix())
235235
require.Equal(t, powerTables[1], newPowerTable)
236236

237237
// Validate multiple
@@ -240,14 +240,14 @@ func TestFinalityCertificates(t *testing.T) {
240240
require.EqualValues(t, 4, nextInstance)
241241
require.Equal(t, powerTables[4], newPowerTable)
242242
require.True(t, certificates[3].ECChain.Head().Equal(chain.Head()))
243-
require.True(t, certificates[0].ECChain[1].Equal(chain.Base()))
243+
require.True(t, certificates[0].ECChain.TipSets[1].Equal(chain.Base()))
244244

245245
nextInstance, chain, newPowerTable, err = certs.ValidateFinalityCertificates(backend, networkName, powerTables[nextInstance], nextInstance, nil, certificates[nextInstance:]...)
246246
require.NoError(t, err)
247247
require.EqualValues(t, len(certificates), nextInstance)
248248
require.Equal(t, powerTable, newPowerTable)
249249
require.True(t, certificates[len(certificates)-1].ECChain.Head().Equal(chain.Head()))
250-
require.True(t, certificates[4].ECChain[1].Equal(chain.Base()))
250+
require.True(t, certificates[4].ECChain.TipSets[1].Equal(chain.Base()))
251251
}
252252

253253
func TestBadFinalityCertificates(t *testing.T) {
@@ -257,7 +257,7 @@ func TestBadFinalityCertificates(t *testing.T) {
257257
tsg := sim.NewTipSetGenerator(rng.Uint64())
258258
tableCid, err := certs.MakePowerTableCID(powerTable)
259259
require.NoError(t, err)
260-
base := gpbft.TipSet{Epoch: 0, Key: tsg.Sample(), PowerTable: tableCid}
260+
base := &gpbft.TipSet{Epoch: 0, Key: tsg.Sample(), PowerTable: tableCid}
261261

262262
nextPowerTable, _ := randomizePowerTable(rng, backend, 200, powerTable, nil)
263263

@@ -393,8 +393,10 @@ func TestBadFinalityCertificates(t *testing.T) {
393393
// Chain is invalid.
394394
{
395395
certCpy := *certificate
396-
certCpy.ECChain = slices.Clone(certCpy.ECChain)
397-
slices.Reverse(certCpy.ECChain)
396+
certCpy.ECChain = &gpbft.ECChain{
397+
TipSets: slices.Clone(certificate.ECChain.TipSets),
398+
}
399+
slices.Reverse(certCpy.ECChain.TipSets)
398400
nextInstance, chain, newPowerTable, err := certs.ValidateFinalityCertificates(backend, networkName, powerTable, 1, nil, &certCpy)
399401
require.ErrorContains(t, err, "chain must have increasing epochs")
400402
require.EqualValues(t, 1, nextInstance)
@@ -476,7 +478,7 @@ func randomPowerTable(backend signing.Backend, entries int64) gpbft.PowerEntries
476478
return powerTable
477479
}
478480

479-
func makeJustification(t *testing.T, rng *rand.Rand, tsg *sim.TipSetGenerator, backend signing.Backend, base gpbft.TipSet, instance uint64, powerTable, nextPowerTable gpbft.PowerEntries) *gpbft.Justification {
481+
func makeJustification(t *testing.T, rng *rand.Rand, tsg *sim.TipSetGenerator, backend signing.Backend, base *gpbft.TipSet, instance uint64, powerTable, nextPowerTable gpbft.PowerEntries) *gpbft.Justification {
480482
chainLen := rng.Intn(23) + 1
481483
chain, err := gpbft.NewChain(base)
482484
require.NoError(t, err)

certstore/certstore_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ func makeCert(instance uint64, supp gpbft.SupplementalData) *certs.FinalityCerti
1919
return &certs.FinalityCertificate{
2020
GPBFTInstance: instance,
2121
SupplementalData: supp,
22-
ECChain: gpbft.ECChain{{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: supp.PowerTable}},
22+
ECChain: &gpbft.ECChain{
23+
TipSets: []*gpbft.TipSet{
24+
{Epoch: 0, Key: gpbft.TipSetKey("tsk0"), PowerTable: supp.PowerTable},
25+
},
26+
},
2327
}
2428
}
2529

0 commit comments

Comments
 (0)