Skip to content

Commit

Permalink
Adds SNARK-friendly epoch encoding (ethereum#780)
Browse files Browse the repository at this point in the history
* switches g1/g2

* temporarily changes ci

* removes epoch data

* adds epoch data struct

* removes unused encoding/hex

* updates bls-zexe

* wip

* uses fixed array size for bls public key

* wip

* wip

* brings back deleted line

* adds parentheses

* adds epoch data encoding

* wip

* adds working epoch data

* lint

* change to monorepo branch

* fixes contract call

* Handles review comments

* lint

* Uses GetEpochNumber instead of calculating

* Handles reviews

* lint

* Adds verifyEpochValidatorSetSeal

* lint

* Ignores epoch validator set on blocks that are not last

* Updates bls-zexe

* handles Asa's comments

* lint
  • Loading branch information
kobigurk committed Jan 29, 2020
1 parent 45859e5 commit bb6b17b
Show file tree
Hide file tree
Showing 61 changed files with 656 additions and 335 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ end-to-end-defaults: &end-to-end-defaults
docker:
- image: celohq/node10-gcloud
environment:
CELO_MONOREPO_BRANCH_TO_TEST: master
CELO_MONOREPO_BRANCH_TO_TEST: kobigurk/epoch_data

jobs:
unit-tests:
Expand Down
5 changes: 3 additions & 2 deletions accounts/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
"github.com/ethereum/go-ethereum/event"
)

Expand Down Expand Up @@ -103,8 +104,8 @@ type Wallet interface {
// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
// the account in a keystore).
SignHash(account Account, hash []byte) ([]byte, error)
SignHashBLS(account Account, hash []byte) ([]byte, error)
SignMessageBLS(account Account, msg []byte, extraData []byte) ([]byte, error)
SignHashBLS(account Account, hash []byte) (blscrypto.SerializedSignature, error)
SignMessageBLS(account Account, msg []byte, extraData []byte) (blscrypto.SerializedSignature, error)
GenerateProofOfPossession(account Account, address common.Address) ([]byte, []byte, error)
GenerateProofOfPossessionBLS(account Account, address common.Address) ([]byte, []byte, error)
GetPublicKey(account Account) (*ecdsa.PublicKey, error)
Expand Down
28 changes: 14 additions & 14 deletions accounts/keystore/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,72 +287,72 @@ func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) {
return crypto.Sign(hash, unlockedKey.PrivateKey)
}

func (ks *KeyStore) SignHashBLS(a accounts.Account, hash []byte) ([]byte, error) {
func (ks *KeyStore) SignHashBLS(a accounts.Account, hash []byte) (blscrypto.SerializedSignature, error) {
// Look up the key to sign with and abort if it cannot be found
ks.mu.RLock()
defer ks.mu.RUnlock()

unlockedKey, found := ks.unlocked[a.Address]
if !found {
return nil, ErrLocked
return blscrypto.SerializedSignature{}, ErrLocked
}

privateKeyBytes, err := blscrypto.ECDSAToBLS(unlockedKey.PrivateKey)
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}

privateKey, err := bls.DeserializePrivateKey(privateKeyBytes)
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}
defer privateKey.Destroy()

signature, err := privateKey.SignMessage(hash, []byte{}, false)
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}
defer signature.Destroy()
signatureBytes, err := signature.Serialize()
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}

return signatureBytes, nil
return blscrypto.SerializedSignatureFromBytes(signatureBytes)
}

func (ks *KeyStore) SignMessageBLS(a accounts.Account, msg []byte, extraData []byte) ([]byte, error) {
func (ks *KeyStore) SignMessageBLS(a accounts.Account, msg []byte, extraData []byte) (blscrypto.SerializedSignature, error) {
// Look up the key to sign with and abort if it cannot be found
ks.mu.RLock()
defer ks.mu.RUnlock()

unlockedKey, found := ks.unlocked[a.Address]
if !found {
return nil, ErrLocked
return blscrypto.SerializedSignature{}, ErrLocked
}

privateKeyBytes, err := blscrypto.ECDSAToBLS(unlockedKey.PrivateKey)
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}

privateKey, err := bls.DeserializePrivateKey(privateKeyBytes)
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}
defer privateKey.Destroy()

signature, err := privateKey.SignMessage(msg, extraData, true)
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}
defer signature.Destroy()
signatureBytes, err := signature.Serialize()
if err != nil {
return nil, err
return blscrypto.SerializedSignature{}, err
}

return signatureBytes, nil
return blscrypto.SerializedSignatureFromBytes(signatureBytes)
}

func (ks *KeyStore) GenerateProofOfPossession(a accounts.Account, address common.Address) ([]byte, []byte, error) {
Expand Down
9 changes: 5 additions & 4 deletions accounts/keystore/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package keystore

import (
"crypto/ecdsa"
blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
"github.com/ethereum/go-ethereum/log"
"math/big"

Expand Down Expand Up @@ -119,21 +120,21 @@ func (w *keystoreWallet) GetPublicKey(account accounts.Account) (*ecdsa.PublicKe
return w.keystore.GetPublicKey(account)
}

func (w *keystoreWallet) SignHashBLS(account accounts.Account, hash []byte) ([]byte, error) {
func (w *keystoreWallet) SignHashBLS(account accounts.Account, hash []byte) (blscrypto.SerializedSignature, error) {
// Make sure the requested account is contained within
if !w.Contains(account) {
log.Debug(accounts.ErrUnknownAccount.Error(), "account", account)
return nil, accounts.ErrUnknownAccount
return blscrypto.SerializedSignature{}, accounts.ErrUnknownAccount
}
// Account seems valid, request the keystore to sign
return w.keystore.SignHashBLS(account, hash)
}

func (w *keystoreWallet) SignMessageBLS(account accounts.Account, msg []byte, extraData []byte) ([]byte, error) {
func (w *keystoreWallet) SignMessageBLS(account accounts.Account, msg []byte, extraData []byte) (blscrypto.SerializedSignature, error) {
// Make sure the requested account is contained within
if !w.Contains(account) {
log.Debug(accounts.ErrUnknownAccount.Error(), "account", account)
return nil, accounts.ErrUnknownAccount
return blscrypto.SerializedSignature{}, accounts.ErrUnknownAccount
}
// Account seems valid, request the keystore to sign
return w.keystore.SignMessageBLS(account, msg, extraData)
Expand Down
9 changes: 5 additions & 4 deletions accounts/usbwallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"context"
"crypto/ecdsa"
"fmt"
blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
"io"
"math/big"
"sync"
Expand Down Expand Up @@ -510,12 +511,12 @@ func (w *wallet) GetPublicKey(account accounts.Account) (*ecdsa.PublicKey, error
return nil, accounts.ErrNotSupported
}

func (w *wallet) SignHashBLS(account accounts.Account, hash []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported
func (w *wallet) SignHashBLS(account accounts.Account, hash []byte) (blscrypto.SerializedSignature, error) {
return blscrypto.SerializedSignature{}, accounts.ErrNotSupported
}

func (w *wallet) SignMessageBLS(account accounts.Account, msg []byte, extraData []byte) ([]byte, error) {
return nil, accounts.ErrNotSupported
func (w *wallet) SignMessageBLS(account accounts.Account, msg []byte, extraData []byte) (blscrypto.SerializedSignature, error) {
return blscrypto.SerializedSignature{}, accounts.ErrNotSupported
}

func (w *wallet) GenerateProofOfPossession(account accounts.Account, address common.Address) ([]byte, []byte, error) {
Expand Down
15 changes: 11 additions & 4 deletions consensus/istanbul/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package istanbul

import (
blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
"math/big"
"time"

Expand All @@ -30,9 +31,13 @@ import (
// backing account.
type SignerFn func(accounts.Account, []byte) ([]byte, error)

// BLSSignerFn is a signer callback function to request a hash to be signed by a
// backing account using BLS.
type BLSSignerFn func(accounts.Account, []byte) (blscrypto.SerializedSignature, error)

// MessageSignerFn is a signer callback function to request a raw message to
// be signed by a backing account.
type MessageSignerFn func(accounts.Account, []byte, []byte) ([]byte, error)
type MessageSignerFn func(accounts.Account, []byte, []byte) (blscrypto.SerializedSignature, error)

// Backend provides application specific functions for Istanbul core
type Backend interface {
Expand All @@ -41,6 +46,7 @@ type Backend interface {

// Validators returns the validator set
Validators(proposal Proposal) ValidatorSet
NextBlockValidators(proposal Proposal) (ValidatorSet, error)

// EventMux returns the event mux in backend
EventMux() *event.TypeMux
Expand All @@ -53,15 +59,16 @@ type Backend interface {

// Commit delivers an approved proposal to backend.
// The delivered proposal will be put into blockchain.
Commit(proposal Proposal, aggregatedSeal types.IstanbulAggregatedSeal) error
Commit(proposal Proposal, aggregatedSeal types.IstanbulAggregatedSeal, aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal) error

// Verify verifies the proposal. If a consensus.ErrFutureBlock error is returned,
// the time difference of the proposal and current time is also returned.
Verify(Proposal) (time.Duration, error)

// Sign signs input data with the backend's private key
Sign([]byte) ([]byte, error)
SignBlockHeader([]byte) ([]byte, error)
SignBlockHeader([]byte) (blscrypto.SerializedSignature, error)
SignBLSWithCompositeHash([]byte) (blscrypto.SerializedSignature, error)

// CheckSignature verifies the signature by checking if it's signed by
// the given validator
Expand Down Expand Up @@ -89,5 +96,5 @@ type Backend interface {
RefreshValPeers(valset ValidatorSet)

// Authorize injects a private key into the consensus engine.
Authorize(address common.Address, signFn SignerFn, signHashBLSFn SignerFn, signMessageBLSFn MessageSignerFn)
Authorize(address common.Address, signFn SignerFn, signHashBLSFn BLSSignerFn, signMessageBLSFn MessageSignerFn)
}
64 changes: 57 additions & 7 deletions consensus/istanbul/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package backend

import (
"errors"
"fmt"
blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
"math/big"
"sync"
"time"
Expand Down Expand Up @@ -148,7 +150,7 @@ type Backend struct {

address common.Address // Ethereum address of the signing key
signFn istanbul.SignerFn // Signer function to authorize hashes with
signHashBLSFn istanbul.SignerFn // Signer function to authorize hashes using BLS with
signHashBLSFn istanbul.BLSSignerFn // Signer function to authorize hashes using BLS with
signMessageBLSFn istanbul.MessageSignerFn // Signer function to authorize messages using BLS with
signFnMu sync.RWMutex // Protects the signer fields

Expand Down Expand Up @@ -240,7 +242,7 @@ func (sb *Backend) SendDelegateSignMsgToProxiedValidator(msg []byte) error {
}

// Authorize implements istanbul.Backend.Authorize
func (sb *Backend) Authorize(address common.Address, signFn istanbul.SignerFn, signHashBLSFn istanbul.SignerFn, signMessageBLSFn istanbul.MessageSignerFn) {
func (sb *Backend) Authorize(address common.Address, signFn istanbul.SignerFn, signHashBLSFn istanbul.BLSSignerFn, signMessageBLSFn istanbul.MessageSignerFn) {
sb.signFnMu.Lock()
defer sb.signFnMu.Unlock()

Expand Down Expand Up @@ -272,6 +274,38 @@ func (sb *Backend) ParentBlockValidators(proposal istanbul.Proposal) istanbul.Va
return sb.getOrderedValidators(proposal.Number().Uint64()-1, proposal.ParentHash())
}

func (sb *Backend) NextBlockValidators(proposal istanbul.Proposal) (istanbul.ValidatorSet, error) {
istExtra, err := types.ExtractIstanbulExtra(proposal.Header())
if err != nil {
return nil, err
}

// There was no change
if len(istExtra.AddedValidators) == 0 && istExtra.RemovedValidators.BitLen() == 0 {
return sb.ParentBlockValidators(proposal), nil
}

snap, err := sb.snapshot(sb.chain, proposal.Number().Uint64()-1, common.Hash{}, nil)
if err != nil {
return nil, err
}
snap = snap.copy()

addedValidators, err := istanbul.CombineIstanbulExtraToValidatorData(istExtra.AddedValidators, istExtra.AddedValidatorsPublicKeys)
if err != nil {
return nil, err
}

if !snap.ValSet.RemoveValidators(istExtra.RemovedValidators) {
return nil, fmt.Errorf("could not obtain next block validators: failed at remove validators")
}
if !snap.ValSet.AddValidators(addedValidators) {
return nil, fmt.Errorf("could not obtain next block validators: failed at add validators")
}

return snap.ValSet, nil
}

func (sb *Backend) GetValidators(blockNumber *big.Int, headerHash common.Hash) []istanbul.Validator {
validatorSet := sb.getValidators(blockNumber.Uint64(), headerHash)
return validatorSet.List()
Expand Down Expand Up @@ -380,7 +414,7 @@ func (sb *Backend) Gossip(destAddresses []common.Address, payload []byte, ethMsg
}

// Commit implements istanbul.Backend.Commit
func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.IstanbulAggregatedSeal) error {
func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.IstanbulAggregatedSeal, aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal) error {
// Check if the proposal is a valid block
block := &types.Block{}
block, ok := proposal.(*types.Block)
Expand All @@ -397,6 +431,9 @@ func (sb *Backend) Commit(proposal istanbul.Proposal, aggregatedSeal types.Istan
}
// update block's header
block = block.WithSeal(h)
block = block.WithEpochSnarkData(&types.EpochSnarkData{
Signature: aggregatedEpochValidatorSetSeal.Signature,
})

sb.logger.Info("Committed", "address", sb.Address(), "round", aggregatedSeal.Round.Uint64(), "hash", proposal.Hash(), "number", proposal.Number().Uint64())
// - if the proposed and committed blocks are the same, send the proposed hash
Expand Down Expand Up @@ -459,7 +496,7 @@ func (sb *Backend) Verify(proposal istanbul.Proposal) (time.Duration, error) {

err = sb.VerifyHeader(sb.chain, block.Header(), false)

// ignore errEmptyCommittedSeals error because we don't have the committed seals yet
// ignore errEmptyAggregatedSeal error because we don't have the committed seals yet
if err != nil && err != errEmptyAggregatedSeal {
if err == consensus.ErrFutureBlock {
return time.Unix(block.Header().Time.Int64(), 0).Sub(now()), consensus.ErrFutureBlock
Expand Down Expand Up @@ -541,7 +578,7 @@ func (sb *Backend) verifyValSetDiff(proposal istanbul.Proposal, block *types.Blo
addedValidators, removedValidators := istanbul.ValidatorSetDiff(oldValSet, newValSet)

addedValidatorsAddresses := make([]common.Address, 0, len(addedValidators))
addedValidatorsPublicKeys := make([][]byte, 0, len(addedValidators))
addedValidatorsPublicKeys := make([]blscrypto.SerializedPublicKey, 0, len(addedValidators))
for _, val := range addedValidators {
addedValidatorsAddresses = append(addedValidatorsAddresses, val.Address)
addedValidatorsPublicKeys = append(addedValidatorsPublicKeys, val.BLSPublicKey)
Expand All @@ -567,15 +604,28 @@ func (sb *Backend) Sign(data []byte) ([]byte, error) {
return sb.signFn(accounts.Account{Address: sb.address}, hashData)
}

func (sb *Backend) SignBlockHeader(data []byte) ([]byte, error) {
func (sb *Backend) SignBlockHeader(data []byte) (blscrypto.SerializedSignature, error) {
if sb.signHashBLSFn == nil {
return nil, errInvalidSigningFn
return blscrypto.SerializedSignature{}, errInvalidSigningFn
}
sb.signFnMu.RLock()
defer sb.signFnMu.RUnlock()
return sb.signHashBLSFn(accounts.Account{Address: sb.address}, data)
}

func (sb *Backend) SignBLSWithCompositeHash(data []byte) (blscrypto.SerializedSignature, error) {
if sb.signMessageBLSFn == nil {
return blscrypto.SerializedSignature{}, errInvalidSigningFn
}
sb.signFnMu.RLock()
defer sb.signFnMu.RUnlock()
// Currently, ExtraData is unused. In the future, it could include data that could be used to introduce
// "firmware-level" protection. Such data could include data that the SNARK doesn't necessarily need,
// such as the block number, which can be used by a hardware wallet to see that the block number
// is incrementing, without having to perform the two-level hashing, just one-level fast hashing.
return sb.signMessageBLSFn(accounts.Account{Address: sb.address}, data, []byte{})
}

// CheckSignature implements istanbul.Backend.CheckSignature
func (sb *Backend) CheckSignature(data []byte, address common.Address, sig []byte) error {
signer, err := istanbul.GetSignatureAddress(data, sig)
Expand Down
Loading

0 comments on commit bb6b17b

Please sign in to comment.