Skip to content

Commit

Permalink
Merge pull request openrelayxyz#23 from openrelayxyz/feature/consensu…
Browse files Browse the repository at this point in the history
…s-engine

Feature/consensus engine
  • Loading branch information
philip-morlier committed Jun 12, 2023
2 parents cee6a9b + 13375d3 commit 0deaa33
Show file tree
Hide file tree
Showing 11 changed files with 1,853 additions and 337 deletions.
6 changes: 6 additions & 0 deletions core/interface.go
Expand Up @@ -114,6 +114,12 @@ type StateDB interface {

AddressInAccessList(addr Address) bool
SlotInAccessList(addr Address, slot Hash) (addressOk bool, slotOk bool)

IntermediateRoot(deleteEmptyObjects bool) Hash
}

type RWStateDB interface {
StateDB
}

type ScopeContext interface {
Expand Down
102 changes: 102 additions & 0 deletions restricted/consensus/consensus.go
@@ -0,0 +1,102 @@
package consensus

import (
"math/big"
"github.com/openrelayxyz/plugeth-utils/restricted/params"
"github.com/openrelayxyz/plugeth-utils/restricted/types"
"github.com/openrelayxyz/plugeth-utils/core"
)

// ChainHeaderReader defines a small collection of methods needed to access the local
// blockchain during header verification.
type ChainHeaderReader interface {
// Config retrieves the blockchain's chain configuration.
Config() *params.ChainConfig

// CurrentHeader retrieves the current header from the local chain.
CurrentHeader() *types.Header

// GetHeader retrieves a block header from the database by hash and number.
GetHeader(hash core.Hash, number uint64) *types.Header

// GetHeaderByNumber retrieves a block header from the database by number.
GetHeaderByNumber(number uint64) *types.Header

// GetHeaderByHash retrieves a block header from the database by its hash.
GetHeaderByHash(hash core.Hash) *types.Header

// GetTd retrieves the total difficulty from the database by hash and number.
GetTd(hash core.Hash, number uint64) *big.Int
}

// ChainReader defines a small collection of methods needed to access the local
// blockchain during header and/or uncle verification.
type ChainReader interface {
ChainHeaderReader

// GetBlock retrieves a block from the database by hash and number.
GetBlock(hash core.Hash, number uint64) *types.Block
}

// Engine is an algorithm agnostic consensus engine.
type Engine interface {
// Author retrieves the Ethereum address of the account that minted the given
// block, which may be different from the header's coinbase if a consensus
// engine is based on signatures.
Author(header *types.Header) (core.Address, error)

// VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
VerifyHeader(chain ChainHeaderReader, header *types.Header, seal bool) error

// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications (the order is that of
// the input slice).
VerifyHeaders(chain ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)

// VerifyUncles verifies that the given block's uncles conform to the consensus
// rules of a given engine.
VerifyUncles(chain ChainReader, block *types.Block) error

// Prepare initializes the consensus fields of a block header according to the
// rules of a particular engine. The changes are executed inline.
Prepare(chain ChainHeaderReader, header *types.Header) error

// Finalize runs any post-transaction state modifications (e.g. block rewards
// or process withdrawals) but does not assemble the block.
//
// Note: The state database might be updated to reflect any consensus rules
// that happen at finalization (e.g. block rewards).
Finalize(chain ChainHeaderReader, header *types.Header, state core.RWStateDB, txs []*types.Transaction,
uncles []*types.Header, withdrawals []*types.Withdrawal)

// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
// rewards or process withdrawals) and assembles the final block.
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state core.RWStateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error)

// Seal generates a new sealing request for the given input block and pushes
// the result into the given channel.
//
// Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorithm.
Seal(chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error

// SealHash returns the hash of a block prior to it being sealed.
SealHash(header *types.Header) core.Hash

// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have.
CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int

// APIs returns the RPC APIs this consensus engine provides.
APIs(chain ChainHeaderReader) []core.API

// Close terminates any background threads maintained by the consensus engine.
Close() error
}
Expand Up @@ -14,46 +14,55 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// +build nacl js !cgo gofuzz

package crypto

import (
"crypto/ecdsa"
"crypto/elliptic"
"errors"
"fmt"
"math/big"

"github.com/btcsuite/btcd/btcec/v2"
btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
)

// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) {
pub, err := SigToPub(hash, sig)
pub, err := sigToPub(hash, sig)
if err != nil {
return nil, err
}
bytes := (*btcec.PublicKey)(pub).SerializeUncompressed()
bytes := pub.SerializeUncompressed()
return bytes, err
}

// SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) {
if len(sig) != SignatureLength {
return nil, errors.New("invalid signature")
}
// Convert to btcec input format with 'recovery id' v at the beginning.
btcsig := make([]byte, SignatureLength)
btcsig[0] = sig[64] + 27
btcsig[0] = sig[RecoveryIDOffset] + 27
copy(btcsig[1:], sig)

pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash)
return (*ecdsa.PublicKey)(pub), err
pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash)
return pub, err
}

// SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
pub, err := sigToPub(hash, sig)
if err != nil {
return nil, err
}
return pub.ToECDSA(), nil
}

// Sign calculates an ECDSA signature.
//
// This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must
// be aware that the given hash cannot be chosen by an adversery. Common
// be aware that the given hash cannot be chosen by an adversary. Common
// solution is to hash any input before calculating the signature.
//
// The produced signature is in the [R || S || V] format where V is 0 or 1.
Expand All @@ -64,14 +73,20 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
if prv.Curve != btcec.S256() {
return nil, fmt.Errorf("private key curve is not secp256k1")
}
sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false)
// ecdsa.PrivateKey -> btcec.PrivateKey
var priv btcec.PrivateKey
if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() {
return nil, fmt.Errorf("invalid private key")
}
defer priv.Zero()
sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
if err != nil {
return nil, err
}
// Convert to Ethereum signature format with 'recovery id' v at the end.
v := sig[0] - 27
copy(sig, sig[1:])
sig[64] = v
sig[RecoveryIDOffset] = v
return sig, nil
}

Expand All @@ -82,13 +97,20 @@ func VerifySignature(pubkey, hash, signature []byte) bool {
if len(signature) != 64 {
return false
}
sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
var r, s btcec.ModNScalar
if r.SetByteSlice(signature[:32]) {
return false // overflow
}
if s.SetByteSlice(signature[32:]) {
return false
}
sig := btc_ecdsa.NewSignature(&r, &s)
key, err := btcec.ParsePubKey(pubkey)
if err != nil {
return false
}
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
if sig.S.Cmp(secp256k1halfN) > 0 {
if s.IsOverHalfOrder() {
return false
}
return sig.Verify(hash, key)
Expand All @@ -99,16 +121,26 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
if len(pubkey) != 33 {
return nil, errors.New("invalid compressed public key length")
}
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
key, err := btcec.ParsePubKey(pubkey)
if err != nil {
return nil, err
}
return key.ToECDSA(), nil
}

// CompressPubkey encodes a public key to the 33-byte compressed format.
// CompressPubkey encodes a public key to the 33-byte compressed format. The
// provided PublicKey must be valid. Namely, the coordinates must not be larger
// than 32 bytes each, they must be less than the field prime, and it must be a
// point on the secp256k1 curve. This is the case for a PublicKey constructed by
// elliptic.Unmarshal (see UnmarshalPubkey), or by ToECDSA and ecdsa.GenerateKey
// when constructing a PrivateKey.
func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
return (*btcec.PublicKey)(pubkey).SerializeCompressed()
// NOTE: the coordinates may be validated with
// btcec.ParsePubKey(FromECDSAPub(pubkey))
var x, y btcec.FieldVal
x.SetByteSlice(pubkey.X.Bytes())
y.SetByteSlice(pubkey.Y.Bytes())
return btcec.NewPublicKey(&x, &y).SerializeCompressed()
}

// S256 returns an instance of the secp256k1 curve.
Expand Down
86 changes: 0 additions & 86 deletions restricted/crypto/signature_cgo.go

This file was deleted.

0 comments on commit 0deaa33

Please sign in to comment.