mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-05-02 03:02:54 -04:00
Add In New Core Methods for Batch Verify (#6482)
* add in new core methods * clean up * add verification method * add tests * add 1 more test * lint * Update beacon-chain/core/blocks/block_operations.go Co-authored-by: terence tsao <terence@prysmaticlabs.com> * Update beacon-chain/core/blocks/block_operations.go Co-authored-by: terence tsao <terence@prysmaticlabs.com> * Update beacon-chain/core/state/transition.go Co-authored-by: terence tsao <terence@prysmaticlabs.com> * terences review * Update beacon-chain/core/state/transition.go Co-authored-by: terence tsao <terence@prysmaticlabs.com> * fmt Co-authored-by: terence tsao <terence@prysmaticlabs.com>
This commit is contained in:
@@ -97,21 +97,25 @@ func verifyDepositDataWithDomain(ctx context.Context, deps []*ethpb.Deposit, dom
|
||||
}
|
||||
msgs[i] = ctrRoot
|
||||
}
|
||||
as := bls.AggregateSignatures(sigs)
|
||||
if !as.AggregateVerify(pks, msgs) {
|
||||
return errors.New("one or more deposit data signatures did not verify")
|
||||
verify, err := bls.VerifyMultipleSignatures(sigs, msgs, pks)
|
||||
if err != nil {
|
||||
return errors.Errorf("could not verify multiple signatures: %v", err)
|
||||
}
|
||||
if !verify {
|
||||
return errors.New("one or more deposit signatures did not verify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifySignature(signedData []byte, pub []byte, signature []byte, domain []byte) error {
|
||||
// retrieves the signature set from the raw data, public key,signature and domain provided.
|
||||
func retrieveSignatureSet(signedData []byte, pub []byte, signature []byte, domain []byte) (*bls.SignatureSet, error) {
|
||||
publicKey, err := bls.PublicKeyFromBytes(pub)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not convert bytes to public key")
|
||||
return nil, errors.Wrap(err, "could not convert bytes to public key")
|
||||
}
|
||||
sig, err := bls.SignatureFromBytes(signature)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not convert bytes to signature")
|
||||
return nil, errors.Wrap(err, "could not convert bytes to signature")
|
||||
}
|
||||
signingData := &pb.SigningData{
|
||||
ObjectRoot: signedData,
|
||||
@@ -119,8 +123,28 @@ func verifySignature(signedData []byte, pub []byte, signature []byte, domain []b
|
||||
}
|
||||
root, err := ssz.HashTreeRoot(signingData)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash container")
|
||||
return nil, errors.Wrap(err, "could not hash container")
|
||||
}
|
||||
return &bls.SignatureSet{
|
||||
Signatures: []bls.Signature{sig},
|
||||
PublicKeys: []bls.PublicKey{publicKey},
|
||||
Messages: [][32]byte{root},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// verifies the signature from the raw data, public key and domain provided.
|
||||
func verifySignature(signedData []byte, pub []byte, signature []byte, domain []byte) error {
|
||||
set, err := retrieveSignatureSet(signedData, pub, signature, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(set.Signatures) != 1 {
|
||||
return errors.Errorf("signature set contains %d signatures instead of 1", len(set.Signatures))
|
||||
}
|
||||
// We assume only one signature set is returned here.
|
||||
sig := set.Signatures[0]
|
||||
publicKey := set.PublicKeys[0]
|
||||
root := set.Messages[0]
|
||||
if !sig.Verify(publicKey, root[:]) {
|
||||
return helpers.ErrSigFailedToVerify
|
||||
}
|
||||
@@ -248,6 +272,22 @@ func VerifyBlockSignature(beaconState *stateTrie.BeaconState, block *ethpb.Signe
|
||||
return helpers.VerifyBlockSigningRoot(block.Block, proposerPubKey[:], block.Signature, domain)
|
||||
}
|
||||
|
||||
// BlockSignatureSet retrieves the block signature set from the provided block and its corresponding state.
|
||||
func BlockSignatureSet(beaconState *stateTrie.BeaconState, block *ethpb.SignedBeaconBlock) (*bls.SignatureSet, error) {
|
||||
proposer, err := beaconState.ValidatorAtIndex(block.Block.ProposerIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
currentEpoch := helpers.SlotToEpoch(beaconState.Slot())
|
||||
domain, err := helpers.Domain(beaconState.Fork(), currentEpoch, params.BeaconConfig().DomainBeaconProposer, beaconState.GenesisValidatorRoot())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proposerPubKey := proposer.PublicKey
|
||||
return helpers.RetrieveBlockSignatureSet(block.Block, proposerPubKey, block.Signature, domain)
|
||||
}
|
||||
|
||||
// ProcessBlockHeaderNoVerify validates a block by its header but skips proposer
|
||||
// signature verification.
|
||||
//
|
||||
@@ -347,17 +387,7 @@ func ProcessRandao(
|
||||
beaconState *stateTrie.BeaconState,
|
||||
body *ethpb.BeaconBlockBody,
|
||||
) (*stateTrie.BeaconState, error) {
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(beaconState)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get beacon proposer index")
|
||||
}
|
||||
proposerPub := beaconState.PubkeyAtIndex(proposerIdx)
|
||||
|
||||
currentEpoch := helpers.SlotToEpoch(beaconState.Slot())
|
||||
buf := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(buf, currentEpoch)
|
||||
|
||||
domain, err := helpers.Domain(beaconState.Fork(), currentEpoch, params.BeaconConfig().DomainRandao, beaconState.GenesisValidatorRoot())
|
||||
buf, proposerPub, domain, err := randaoSigningData(beaconState)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -372,6 +402,41 @@ func ProcessRandao(
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// RandaoSignatureSet retrieves the relevant randao specific signature set object
|
||||
// from a block and its corresponding state.
|
||||
func RandaoSignatureSet(beaconState *stateTrie.BeaconState,
|
||||
body *ethpb.BeaconBlockBody,
|
||||
) (*bls.SignatureSet, *stateTrie.BeaconState, error) {
|
||||
buf, proposerPub, domain, err := randaoSigningData(beaconState)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
set, err := retrieveSignatureSet(buf, proposerPub[:], body.RandaoReveal, domain)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return set, beaconState, nil
|
||||
}
|
||||
|
||||
// retrieves the randao related signing data from the state.
|
||||
func randaoSigningData(beaconState *stateTrie.BeaconState) ([]byte, []byte, []byte, error) {
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(beaconState)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "could not get beacon proposer index")
|
||||
}
|
||||
proposerPub := beaconState.PubkeyAtIndex(proposerIdx)
|
||||
|
||||
currentEpoch := helpers.SlotToEpoch(beaconState.Slot())
|
||||
buf := make([]byte, 32)
|
||||
binary.LittleEndian.PutUint64(buf, currentEpoch)
|
||||
|
||||
domain, err := helpers.Domain(beaconState.Fork(), currentEpoch, params.BeaconConfig().DomainRandao, beaconState.GenesisValidatorRoot())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return buf, proposerPub[:], domain, nil
|
||||
}
|
||||
|
||||
// ProcessRandaoNoVerify generates a new randao mix to update
|
||||
// in the beacon state's latest randao mixes slice.
|
||||
//
|
||||
@@ -887,12 +952,84 @@ func VerifyAttestations(ctx context.Context, beaconState *stateTrie.BeaconState,
|
||||
return verifyAttestationsWithDomain(ctx, beaconState, postForkAtts, currDomain)
|
||||
}
|
||||
|
||||
// RetrieveAttestationSignatureSet retrieves all the related attestation signature data such as the relevant public keys,
|
||||
// signatures and attestation signing data and collate it into a signature set object.
|
||||
func RetrieveAttestationSignatureSet(ctx context.Context, beaconState *stateTrie.BeaconState, atts []*ethpb.Attestation) (*bls.SignatureSet, error) {
|
||||
if len(atts) == 0 {
|
||||
return bls.NewSet(), nil
|
||||
}
|
||||
|
||||
fork := beaconState.Fork()
|
||||
gvr := beaconState.GenesisValidatorRoot()
|
||||
dt := params.BeaconConfig().DomainBeaconAttester
|
||||
|
||||
// Split attestations by fork. Note: the signature domain will differ based on the fork.
|
||||
var preForkAtts []*ethpb.Attestation
|
||||
var postForkAtts []*ethpb.Attestation
|
||||
for _, a := range atts {
|
||||
if helpers.SlotToEpoch(a.Data.Slot) < fork.Epoch {
|
||||
preForkAtts = append(preForkAtts, a)
|
||||
} else {
|
||||
postForkAtts = append(postForkAtts, a)
|
||||
}
|
||||
}
|
||||
set := bls.NewSet()
|
||||
|
||||
// Check attestations from before the fork.
|
||||
if fork.Epoch > 0 { // Check to prevent underflow.
|
||||
prevDomain, err := helpers.Domain(fork, fork.Epoch-1, dt, gvr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aSet, err := createAttestationSignatureSet(ctx, beaconState, preForkAtts, prevDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set.Join(aSet)
|
||||
} else if len(preForkAtts) > 0 {
|
||||
// This is a sanity check that preForkAtts were not ignored when fork.Epoch == 0. This
|
||||
// condition is not possible, but it doesn't hurt to check anyway.
|
||||
return nil, errors.New("some attestations were not verified from previous fork before genesis")
|
||||
}
|
||||
|
||||
// Then check attestations from after the fork.
|
||||
currDomain, err := helpers.Domain(fork, fork.Epoch, dt, gvr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aSet, err := createAttestationSignatureSet(ctx, beaconState, postForkAtts, currDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return set.Join(aSet), nil
|
||||
}
|
||||
|
||||
// Inner method to verify attestations. This abstraction allows for the domain to be provided as an
|
||||
// argument.
|
||||
func verifyAttestationsWithDomain(ctx context.Context, beaconState *stateTrie.BeaconState, atts []*ethpb.Attestation, domain []byte) error {
|
||||
if len(atts) == 0 {
|
||||
return nil
|
||||
}
|
||||
set, err := createAttestationSignatureSet(ctx, beaconState, atts, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verify, err := bls.VerifyMultipleSignatures(set.Signatures, set.Messages, set.PublicKeys)
|
||||
if err != nil {
|
||||
return errors.Errorf("got error in multiple verification: %v", err)
|
||||
}
|
||||
if !verify {
|
||||
return errors.New("one or more attestation signatures did not verify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Method to break down attestations of the same domain and collect them into a single signature set.
|
||||
func createAttestationSignatureSet(ctx context.Context, beaconState *stateTrie.BeaconState, atts []*ethpb.Attestation, domain []byte) (*bls.SignatureSet, error) {
|
||||
if len(atts) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sigs := make([]bls.Signature, len(atts))
|
||||
pks := make([]bls.PublicKey, len(atts))
|
||||
@@ -900,12 +1037,12 @@ func verifyAttestationsWithDomain(ctx context.Context, beaconState *stateTrie.Be
|
||||
for i, a := range atts {
|
||||
sig, err := bls.SignatureFromBytes(a.Signature)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
sigs[i] = sig
|
||||
c, err := helpers.BeaconCommitteeFromState(beaconState, a.Data.Slot, a.Data.CommitteeIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
ia := attestationutil.ConvertToIndexed(ctx, a, c)
|
||||
indices := ia.AttestingIndices
|
||||
@@ -914,7 +1051,7 @@ func verifyAttestationsWithDomain(ctx context.Context, beaconState *stateTrie.Be
|
||||
pubkeyAtIdx := beaconState.PubkeyAtIndex(indices[i])
|
||||
p, err := bls.PublicKeyFromBytes(pubkeyAtIdx[:])
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not deserialize validator public key")
|
||||
return nil, errors.Wrap(err, "could not deserialize validator public key")
|
||||
}
|
||||
if pk == nil {
|
||||
pk = p
|
||||
@@ -926,15 +1063,15 @@ func verifyAttestationsWithDomain(ctx context.Context, beaconState *stateTrie.Be
|
||||
|
||||
root, err := helpers.ComputeSigningRoot(ia.Data, domain)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get signing root of object")
|
||||
return nil, errors.Wrap(err, "could not get signing root of object")
|
||||
}
|
||||
msgs[i] = root
|
||||
}
|
||||
as := bls.AggregateSignatures(sigs)
|
||||
if !as.AggregateVerify(pks, msgs) {
|
||||
return errors.New("one or more attestation signatures did not verify")
|
||||
}
|
||||
return nil
|
||||
return &bls.SignatureSet{
|
||||
Signatures: sigs,
|
||||
PublicKeys: pks,
|
||||
Messages: msgs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProcessDeposits is one of the operations performed on each processed
|
||||
|
||||
@@ -349,6 +349,85 @@ func TestProcessBlockHeader_OK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockSignatureSet_OK(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
Slashed: true,
|
||||
}
|
||||
}
|
||||
|
||||
state, err := stateTrie.InitializeFromProto(&pb.BeaconState{
|
||||
Validators: validators,
|
||||
Slot: 10,
|
||||
LatestBlockHeader: ðpb.BeaconBlockHeader{Slot: 9},
|
||||
Fork: &pb.Fork{
|
||||
PreviousVersion: []byte{0, 0, 0, 0},
|
||||
CurrentVersion: []byte{0, 0, 0, 0},
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
latestBlockSignedRoot, err := stateutil.BlockHeaderRoot(state.LatestBlockHeader())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
currentEpoch := helpers.CurrentEpoch(state)
|
||||
dt, err := helpers.Domain(state.Fork(), currentEpoch, params.BeaconConfig().DomainBeaconProposer, state.GenesisValidatorRoot())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get domain form state: %v", err)
|
||||
}
|
||||
priv := bls.RandKey()
|
||||
pID, err := helpers.BeaconProposerIndex(state)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
block := ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
ProposerIndex: pID,
|
||||
Slot: 10,
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: []byte{'A', 'B', 'C'},
|
||||
},
|
||||
ParentRoot: latestBlockSignedRoot[:],
|
||||
},
|
||||
}
|
||||
signingRoot, err := helpers.ComputeSigningRoot(block.Block, dt)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get signing root of block: %v", err)
|
||||
}
|
||||
blockSig := priv.Sign(signingRoot[:])
|
||||
block.Signature = blockSig.Marshal()[:]
|
||||
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(state)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
validators[proposerIdx].Slashed = false
|
||||
validators[proposerIdx].PublicKey = priv.PublicKey().Marshal()
|
||||
err = state.UpdateValidatorAtIndex(proposerIdx, validators[proposerIdx])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
set, err := blocks.BlockSignatureSet(state, block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
verified, err := set.Verify()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !verified {
|
||||
t.Error("Block signature set returned a set which was unable to be verified")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBlockHeader_ImproperBlockSlot(t *testing.T) {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
@@ -489,6 +568,34 @@ func TestProcessRandao_SignatureVerifiesAndUpdatesLatestStateMixes(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandaoSignatureSet_OK(t *testing.T) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisState(t, 100)
|
||||
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
epochSignature, err := testutil.RandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
block := ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: epochSignature,
|
||||
},
|
||||
}
|
||||
|
||||
set, _, err := blocks.RandaoSignatureSet(beaconState, block.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
verified, err := set.Verify()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !verified {
|
||||
t.Error("Unable to verify randao signature set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessEth1Data_SetsCorrectly(t *testing.T) {
|
||||
beaconState, err := stateTrie.InitializeFromProto(&pb.BeaconState{
|
||||
Eth1DataVotes: []*ethpb.Eth1Data{},
|
||||
@@ -2219,6 +2326,96 @@ func TestVerifyAttestations_HandlesPlannedFork(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveAttestationSignatureSet_VerifiesMultipleAttestations(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
numOfValidators := 4 * params.BeaconConfig().SlotsPerEpoch
|
||||
validators := make([]*ethpb.Validator, numOfValidators)
|
||||
_, keys, err := testutil.DeterministicDepositsAndKeys(numOfValidators)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
PublicKey: keys[i].PublicKey().Marshal(),
|
||||
}
|
||||
}
|
||||
|
||||
st, err := stateTrie.InitializeFromProto(&pb.BeaconState{
|
||||
Slot: 5,
|
||||
Validators: validators,
|
||||
Fork: &pb.Fork{
|
||||
Epoch: 0,
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
|
||||
comm1, err := helpers.BeaconCommitteeFromState(st, 1 /*slot*/, 0 /*committeeIndex*/)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
att1 := ðpb.Attestation{
|
||||
AggregationBits: bitfield.NewBitlist(uint64(len(comm1))),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 1,
|
||||
CommitteeIndex: 0,
|
||||
},
|
||||
Signature: nil,
|
||||
}
|
||||
domain, err := helpers.Domain(st.Fork(), st.Fork().Epoch, params.BeaconConfig().DomainBeaconAttester, st.GenesisValidatorRoot())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root, err := helpers.ComputeSigningRoot(att1.Data, domain)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var sigs []bls.Signature
|
||||
for i, u := range comm1 {
|
||||
att1.AggregationBits.SetBitAt(uint64(i), true)
|
||||
sigs = append(sigs, keys[u].Sign(root[:]))
|
||||
}
|
||||
att1.Signature = bls.AggregateSignatures(sigs).Marshal()
|
||||
|
||||
comm2, err := helpers.BeaconCommitteeFromState(st, 1 /*slot*/, 1 /*committeeIndex*/)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
att2 := ðpb.Attestation{
|
||||
AggregationBits: bitfield.NewBitlist(uint64(len(comm2))),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 1,
|
||||
CommitteeIndex: 1,
|
||||
},
|
||||
Signature: nil,
|
||||
}
|
||||
root, err = helpers.ComputeSigningRoot(att2.Data, domain)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sigs = nil
|
||||
for i, u := range comm2 {
|
||||
att2.AggregationBits.SetBitAt(uint64(i), true)
|
||||
sigs = append(sigs, keys[u].Sign(root[:]))
|
||||
}
|
||||
att2.Signature = bls.AggregateSignatures(sigs).Marshal()
|
||||
|
||||
set, err := blocks.RetrieveAttestationSignatureSet(ctx, st, []*ethpb.Attestation{att1, att2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
verified, err := set.Verify()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !verified {
|
||||
t.Error("Multiple signatures were unable to be verified.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAreEth1DataEqual(t *testing.T) {
|
||||
type args struct {
|
||||
a *ethpb.Eth1Data
|
||||
|
||||
@@ -83,25 +83,44 @@ func VerifySigningRoot(obj interface{}, pub []byte, signature []byte, domain []b
|
||||
|
||||
// VerifyBlockSigningRoot verifies the signing root of a block given it's public key, signature and domain.
|
||||
func VerifyBlockSigningRoot(blk *ethpb.BeaconBlock, pub []byte, signature []byte, domain []byte) error {
|
||||
set, err := RetrieveBlockSignatureSet(blk, pub, signature, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// We assume only one signature set is returned here.
|
||||
sig := set.Signatures[0]
|
||||
publicKey := set.PublicKeys[0]
|
||||
root := set.Messages[0]
|
||||
|
||||
if !sig.Verify(publicKey, root[:]) {
|
||||
return ErrSigFailedToVerify
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetrieveBlockSignatureSet retrieves the relevant signature, message and pubkey data from a block and collating it
|
||||
// into a signature set object.
|
||||
func RetrieveBlockSignatureSet(blk *ethpb.BeaconBlock, pub []byte, signature []byte, domain []byte) (*bls.SignatureSet, error) {
|
||||
publicKey, err := bls.PublicKeyFromBytes(pub)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not convert bytes to public key")
|
||||
return nil, errors.Wrap(err, "could not convert bytes to public key")
|
||||
}
|
||||
sig, err := bls.SignatureFromBytes(signature)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not convert bytes to signature")
|
||||
return nil, errors.Wrap(err, "could not convert bytes to signature")
|
||||
}
|
||||
root, err := signingData(func() ([32]byte, error) {
|
||||
// utilize custom block hashing function
|
||||
return stateutil.BlockRoot(blk)
|
||||
}, domain)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute signing root")
|
||||
return nil, errors.Wrap(err, "could not compute signing root")
|
||||
}
|
||||
if !sig.Verify(publicKey, root[:]) {
|
||||
return ErrSigFailedToVerify
|
||||
}
|
||||
return nil
|
||||
return &bls.SignatureSet{
|
||||
Signatures: []bls.Signature{sig},
|
||||
PublicKeys: []bls.PublicKey{publicKey},
|
||||
Messages: [][32]byte{root},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifyBlockHeaderSigningRoot verifies the signing root of a block header given it's public key, signature and domain.
|
||||
|
||||
@@ -31,6 +31,7 @@ go_library(
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/stateutil:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/traceutil:go_default_library",
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state/interop"
|
||||
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/traceutil"
|
||||
@@ -129,6 +130,52 @@ func ExecuteStateTransitionNoVerifyAttSigs(
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// ExecuteStateTransitionNoVerify defines the procedure for a state transition function.
|
||||
// This does not validate any BLS signatures of attestations, block proposer signature, randao signature,
|
||||
// it is used for performing a state transition as quickly as possible. This function also returns a signature
|
||||
// set of all signatures not verified, so that they can be stored and verified later.
|
||||
//
|
||||
// WARNING: This method does not validate any signatures in a block. This method also modifies the passed in state.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root: bool=False) -> BeaconState:
|
||||
// # Process slots (including those with no blocks) since block
|
||||
// process_slots(state, block.slot)
|
||||
// # Process block
|
||||
// process_block(state, block)
|
||||
// # Return post-state
|
||||
// return state
|
||||
func ExecuteStateTransitionNoVerify(
|
||||
ctx context.Context,
|
||||
state *stateTrie.BeaconState,
|
||||
signed *ethpb.SignedBeaconBlock,
|
||||
) (*bls.SignatureSet, *stateTrie.BeaconState, error) {
|
||||
if ctx.Err() != nil {
|
||||
return nil, nil, ctx.Err()
|
||||
}
|
||||
if signed == nil || signed.Block == nil {
|
||||
return nil, nil, errors.New("nil block")
|
||||
}
|
||||
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.ExecuteStateTransitionNoVerifyAttSigs")
|
||||
defer span.End()
|
||||
var err error
|
||||
|
||||
// Execute per slots transition.
|
||||
state, err = ProcessSlots(ctx, state, signed.Block.Slot)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not process slot")
|
||||
}
|
||||
|
||||
// Execute per block transition.
|
||||
set, state, err := ProcessBlockNoVerify(ctx, state, signed)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not process block")
|
||||
}
|
||||
|
||||
return set, state, nil
|
||||
}
|
||||
|
||||
// CalculateStateRoot defines the procedure for a state transition function.
|
||||
// This does not validate any BLS signatures in a block, it is used for calculating the
|
||||
// state root of the state for the block proposer to use.
|
||||
@@ -427,6 +474,68 @@ func ProcessBlockNoVerifyAttSigs(
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// ProcessBlockNoVerify creates a new, modified beacon state by applying block operation
|
||||
// transformations as defined in the Ethereum Serenity specification. It does not validate
|
||||
// any block signature except for deposit and slashing signatures. It also returns the relevant
|
||||
// signature set from all the respective methods.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
// def process_block(state: BeaconState, block: BeaconBlock) -> None:
|
||||
// process_block_header(state, block)
|
||||
// process_randao(state, block.body)
|
||||
// process_eth1_data(state, block.body)
|
||||
// process_operations(state, block.body)
|
||||
func ProcessBlockNoVerify(
|
||||
ctx context.Context,
|
||||
state *stateTrie.BeaconState,
|
||||
signed *ethpb.SignedBeaconBlock,
|
||||
) (*bls.SignatureSet, *stateTrie.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessBlock")
|
||||
defer span.End()
|
||||
|
||||
// Empty signature set.
|
||||
set := bls.NewSet()
|
||||
|
||||
state, err := b.ProcessBlockHeaderNoVerify(state, signed.Block)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not process block header")
|
||||
}
|
||||
bSet, err := b.BlockSignatureSet(state, signed)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not retrieve block signature set")
|
||||
}
|
||||
rSet, state, err := b.RandaoSignatureSet(state, signed.Block.Body)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not retrieve randao signature set")
|
||||
}
|
||||
state, err = b.ProcessRandaoNoVerify(state, signed.Block.Body)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not verify and process randao")
|
||||
}
|
||||
|
||||
state, err = b.ProcessEth1DataInBlock(state, signed.Block)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not process eth1 data")
|
||||
}
|
||||
|
||||
aSet, state, err := ProcessOperationsNoVerifySignatureSet(ctx, state, signed.Block.Body)
|
||||
if err != nil {
|
||||
traceutil.AnnotateError(span, err)
|
||||
return nil, nil, errors.Wrap(err, "could not process block operation")
|
||||
}
|
||||
|
||||
// Merge all signature sets
|
||||
set.Join(bSet).Join(rSet).Join(aSet)
|
||||
|
||||
return set, state, nil
|
||||
}
|
||||
|
||||
// ProcessOperations processes the operations in the beacon block and updates beacon state
|
||||
// with the operations in block.
|
||||
//
|
||||
@@ -547,6 +656,71 @@ func ProcessOperationsNoVerify(
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// ProcessOperationsNoVerifySignatureSet processes the operations in the beacon block and updates beacon state
|
||||
// with the operations in block. It does not verify attestation signatures. It instead
|
||||
// returns the relevant signature set for each of the operations
|
||||
//
|
||||
// WARNING: This method does not verify attestation signatures.
|
||||
// This is used to perform the block operations as fast as possible.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
//
|
||||
// def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
// # Verify that outstanding deposits are processed up to the maximum number of deposits
|
||||
// assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
|
||||
// # Verify that there are no duplicate transfers
|
||||
// assert len(body.transfers) == len(set(body.transfers))
|
||||
//
|
||||
// all_operations = (
|
||||
// (body.proposer_slashings, process_proposer_slashing),
|
||||
// (body.attester_slashings, process_attester_slashing),
|
||||
// (body.attestations, process_attestation),
|
||||
// (body.deposits, process_deposit),
|
||||
// (body.voluntary_exits, process_voluntary_exit),
|
||||
// (body.transfers, process_transfer),
|
||||
// ) # type: Sequence[Tuple[List, Callable]]
|
||||
// for operations, function in all_operations:
|
||||
// for operation in operations:
|
||||
// function(state, operation)
|
||||
func ProcessOperationsNoVerifySignatureSet(
|
||||
ctx context.Context,
|
||||
state *stateTrie.BeaconState,
|
||||
body *ethpb.BeaconBlockBody) (*bls.SignatureSet, *stateTrie.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessOperations")
|
||||
defer span.End()
|
||||
|
||||
if err := verifyOperationLengths(state, body); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not verify operation lengths")
|
||||
}
|
||||
|
||||
state, err := b.ProcessProposerSlashings(ctx, state, body)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not process block proposer slashings")
|
||||
}
|
||||
state, err = b.ProcessAttesterSlashings(ctx, state, body)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not process block attester slashings")
|
||||
}
|
||||
state, err = b.ProcessAttestationsNoVerify(ctx, state, body)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not process block attestations")
|
||||
}
|
||||
aSet, err := b.RetrieveAttestationSignatureSet(ctx, state, body.Attestations)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not retrieve attestation signature set")
|
||||
}
|
||||
state, err = b.ProcessDeposits(ctx, state, body.Deposits)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not process block validator deposits")
|
||||
}
|
||||
state, err = b.ProcessVoluntaryExits(ctx, state, body)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not process validator exits")
|
||||
}
|
||||
|
||||
return aSet, state, nil
|
||||
}
|
||||
|
||||
func verifyOperationLengths(state *stateTrie.BeaconState, body *ethpb.BeaconBlockBody) error {
|
||||
if uint64(len(body.ProposerSlashings)) > params.BeaconConfig().MaxProposerSlashings {
|
||||
return fmt.Errorf(
|
||||
|
||||
@@ -144,6 +144,91 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteStateTransitionNoVerify_FullProcess(t *testing.T) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisState(t, 100)
|
||||
|
||||
eth1Data := ðpb.Eth1Data{
|
||||
DepositCount: 100,
|
||||
DepositRoot: []byte{2},
|
||||
}
|
||||
if err := beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch - 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
e := beaconState.Eth1Data()
|
||||
e.DepositCount = 100
|
||||
if err := beaconState.SetEth1Data(e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconState.SetLatestBlockHeader(ðpb.BeaconBlockHeader{Slot: beaconState.Slot()}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconState.SetEth1DataVotes([]*ethpb.Eth1Data{eth1Data}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parentRoot, err := stateutil.BlockHeaderRoot(beaconState.LatestBlockHeader())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := beaconState.SetSlot(beaconState.Slot() + 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
epoch := helpers.CurrentEpoch(beaconState)
|
||||
randaoReveal, err := testutil.RandaoReveal(beaconState, epoch, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconState.SetSlot(beaconState.Slot() - 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nextSlotState := beaconState.Copy()
|
||||
if err := nextSlotState.SetSlot(beaconState.Slot() + 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proposerIdx, err := helpers.BeaconProposerIndex(nextSlotState)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
block := ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
ProposerIndex: proposerIdx,
|
||||
Slot: beaconState.Slot() + 1,
|
||||
ParentRoot: parentRoot[:],
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: randaoReveal,
|
||||
Eth1Data: eth1Data,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stateRoot, err := state.CalculateStateRoot(context.Background(), beaconState, block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
block.Block.StateRoot = stateRoot[:]
|
||||
|
||||
sig, err := testutil.BlockSignature(beaconState, block.Block, privKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.Signature = sig.Marshal()
|
||||
|
||||
set, beaconState, err := state.ExecuteStateTransitionNoVerify(context.Background(), beaconState, block)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
verified, err := set.Verify()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !verified {
|
||||
t.Error("Could not verify signature set")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProcessBlock_IncorrectProposerSlashing(t *testing.T) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisState(t, 100)
|
||||
|
||||
@@ -347,7 +432,8 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
func createFullBlockWithOperations(t *testing.T) (*beaconstate.BeaconState,
|
||||
*ethpb.SignedBeaconBlock, []*ethpb.Attestation, []*ethpb.ProposerSlashing, []*ethpb.SignedVoluntaryExit) {
|
||||
beaconState, privKeys := testutil.DeterministicGenesisState(t, 32)
|
||||
genesisBlock := blocks.NewGenesisBlock([]byte{})
|
||||
bodyRoot, err := stateutil.BlockRoot(genesisBlock.Block)
|
||||
@@ -587,7 +673,14 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
if beaconState.SetSlot(block.Block.Slot) != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
beaconState, err = state.ProcessBlock(context.Background(), beaconState, block)
|
||||
return beaconState, block, []*ethpb.Attestation{blockAtt}, proposerSlashings, []*ethpb.SignedVoluntaryExit{exit}
|
||||
}
|
||||
|
||||
func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
beaconState, block, _, proposerSlashings, exits := createFullBlockWithOperations(t)
|
||||
exit := exits[0]
|
||||
|
||||
beaconState, err := state.ProcessBlock(context.Background(), beaconState, block)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected block to pass processing conditions: %v", err)
|
||||
}
|
||||
@@ -618,6 +711,23 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBlockNoVerify_PassesProcessingConditions(t *testing.T) {
|
||||
beaconState, block, _, _, _ := createFullBlockWithOperations(t)
|
||||
set, _, err := state.ProcessBlockNoVerify(context.Background(), beaconState, block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Test Signature set verifies.
|
||||
verified, err := set.Verify()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !verified {
|
||||
t.Error("Could not verify signature set.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProcessEpochPrecompute_CanProcess(t *testing.T) {
|
||||
epoch := uint64(1)
|
||||
|
||||
|
||||
@@ -9,9 +9,24 @@ type SignatureSet struct {
|
||||
Messages [][32]byte
|
||||
}
|
||||
|
||||
// NewSet constructs an empty signature set object.
|
||||
func NewSet() *SignatureSet {
|
||||
return &SignatureSet{
|
||||
Signatures: []Signature{},
|
||||
PublicKeys: []PublicKey{},
|
||||
Messages: [][32]byte{},
|
||||
}
|
||||
}
|
||||
|
||||
// Join merges the provided signature set to out current one.
|
||||
func (s *SignatureSet) Join(set *SignatureSet) {
|
||||
func (s *SignatureSet) Join(set *SignatureSet) *SignatureSet {
|
||||
s.Signatures = append(s.Signatures, set.Signatures...)
|
||||
s.PublicKeys = append(s.PublicKeys, set.PublicKeys...)
|
||||
s.Messages = append(s.Messages, set.Messages...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Verify the current signature set using the batch verify algorithm.
|
||||
func (s *SignatureSet) Verify() (bool, error) {
|
||||
return VerifyMultipleSignatures(s.Signatures, s.Messages, s.PublicKeys)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user