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:
Nishant Das
2020-07-06 11:09:29 +08:00
committed by GitHub
parent 359b9bef49
commit fa3da9a6a2
7 changed files with 690 additions and 37 deletions

View File

@@ -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

View File

@@ -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] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: true,
}
}
state, err := stateTrie.InitializeFromProto(&pb.BeaconState{
Validators: validators,
Slot: 10,
LatestBlockHeader: &ethpb.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 := &ethpb.SignedBeaconBlock{
Block: &ethpb.BeaconBlock{
ProposerIndex: pID,
Slot: 10,
Body: &ethpb.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 := &ethpb.BeaconBlock{
Body: &ethpb.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] = &ethpb.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 := &ethpb.Attestation{
AggregationBits: bitfield.NewBitlist(uint64(len(comm1))),
Data: &ethpb.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 := &ethpb.Attestation{
AggregationBits: bitfield.NewBitlist(uint64(len(comm2))),
Data: &ethpb.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

View File

@@ -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.

View File

@@ -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",

View File

@@ -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(

View File

@@ -144,6 +144,91 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) {
}
}
func TestExecuteStateTransitionNoVerify_FullProcess(t *testing.T) {
beaconState, privKeys := testutil.DeterministicGenesisState(t, 100)
eth1Data := &ethpb.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(&ethpb.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 := &ethpb.SignedBeaconBlock{
Block: &ethpb.BeaconBlock{
ProposerIndex: proposerIdx,
Slot: beaconState.Slot() + 1,
ParentRoot: parentRoot[:],
Body: &ethpb.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)

View File

@@ -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)
}