Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: generic Header and Proof #1145

Merged
merged 16 commits into from
Sep 20, 2023
990 changes: 908 additions & 82 deletions common/common.pb.go

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions common/headers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package common

import (
"bytes"
"encoding/hex"
"errors"
"fmt"

ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)

// NewEthereumHeader returns a new HeaderData containing an Ethereum header
func NewEthereumHeader(header []byte) HeaderData {
return HeaderData{
Data: &HeaderData_EthereumHeader{
EthereumHeader: header,
},
}
}

// ParentHash extracts the parent hash from the header
func (h HeaderData) ParentHash() ([]byte, error) {
switch data := h.Data.(type) {
case *HeaderData_EthereumHeader:
var header ethtypes.Header
if err := rlp.DecodeBytes(data.EthereumHeader, &header); err != nil {
return nil, err
}
return header.ParentHash.Bytes(), nil
default:
return nil, errors.New("unrecognized header type")
}
}

// Validate performs a basic validation of the HeaderData
func (h HeaderData) Validate(blockHash []byte, height int64) error {
switch data := h.Data.(type) {
case *HeaderData_EthereumHeader:
return validateEthereumHeader(data.EthereumHeader, blockHash, height)
default:
return errors.New("unrecognized header type")
}
}

// validateEthereumHeader performs a basic validation of the Ethereum header
func validateEthereumHeader(headerBytes []byte, blockHash []byte, height int64) error {
// on ethereum the block header is ~538 bytes in RLP encoding
if len(headerBytes) > 1024 {
return fmt.Errorf("header too long (%d)", len(headerBytes))
}

// RLP encoded block header
var header ethtypes.Header
if err := rlp.DecodeBytes(headerBytes, &header); err != nil {
return fmt.Errorf("cannot decode RLP (%s)", err)
}
if err := header.SanityCheck(); err != nil {
return fmt.Errorf("sanity check failed (%s)", err)
}
if bytes.Compare(blockHash, header.Hash().Bytes()) != 0 {
return fmt.Errorf("tx hash mismatch (%s) vs (%s)", hex.EncodeToString(blockHash), header.Hash().Hex())
}
if height != header.Number.Int64() {
return fmt.Errorf("height mismatch (%d) vs (%d)", height, header.Number.Int64())
}
return nil
}
61 changes: 61 additions & 0 deletions common/proof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package common

import (
"errors"

ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/zeta-chain/zetacore/common/ethereum"
)

// ErrInvalidProof is a error type for invalid proofs embedding the underlying error
type ErrInvalidProof struct {
Err error
}

func NewErrInvalidProof(err error) ErrInvalidProof {
return ErrInvalidProof{
Err: err,
}
}

func (e ErrInvalidProof) Error() string {
return e.Err.Error()
}

// IsErrorInvalidProof returns true if the error is an ErrInvalidProof
func IsErrorInvalidProof(err error) bool {
return errors.As(err, &ErrInvalidProof{})
}

// NewEthereumProof returns a new Proof containing an Ethereum proof
func NewEthereumProof(proof ethereum.Proof) *Proof {
lumtis marked this conversation as resolved.
Show resolved Hide resolved
return &Proof{
Proof: &Proof_EthereumProof{
EthereumProof: &proof,
},
}
}

// Verify verifies the proof against the header
func (p Proof) Verify(headerData HeaderData, txIndex int) ([]byte, error) {
switch proof := p.Proof.(type) {
case *Proof_EthereumProof:
ethHeaderBytes := headerData.GetEthereumHeader()
if ethHeaderBytes == nil {
return nil, errors.New("can't verify ethereum proof against non-ethereum header")
}
var ethHeader ethtypes.Header
err := rlp.DecodeBytes(ethHeaderBytes, &ethHeader)
if err != nil {
return nil, err
}
val, err := proof.EthereumProof.Verify(ethHeader.TxHash, txIndex)
if err != nil {
return nil, NewErrInvalidProof(err)
}
return val, nil
default:
return nil, errors.New("unrecognized proof type")
}
}
15 changes: 15 additions & 0 deletions common/proof_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package common_test

import (
"errors"
"testing"

"github.com/stretchr/testify/require"
"github.com/zeta-chain/zetacore/common"
)

func Test_IsErrorInvalidProof(t *testing.T) {
require.False(t, common.IsErrorInvalidProof(nil))
require.False(t, common.IsErrorInvalidProof(errors.New("foo")))
require.True(t, common.IsErrorInvalidProof(common.NewErrInvalidProof(errors.New("foo"))))
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (sm *SmokeTest) TestDepositEtherIntoZRC20() {
BlockHash: blockHash.Hex(),
TxIndex: int64(txIndex),
TxHash: txHash.Hex(),
Proof: txProof,
Proof: common.NewEthereumProof(*txProof),
ChainId: 0,
})
if err != nil {
Expand Down
57 changes: 34 additions & 23 deletions docs/openapi/openapi.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27590,15 +27590,15 @@ paths:
in: query
required: false
type: string
- name: proof.keys
- name: proof.ethereum_proof.keys
in: query
required: false
type: array
items:
type: string
format: byte
collectionFormat: multi
- name: proof.values
- name: proof.ethereum_proof.values
in: query
required: false
type: array
Expand Down Expand Up @@ -50182,6 +50182,24 @@ definitions:
- VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option.
- VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option.
- VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option.
commonBlockHeader:
type: object
properties:
height:
type: string
format: int64
hash:
type: string
format: byte
parent_hash:
type: string
format: byte
chain_id:
type: string
format: int64
header:
$ref: '#/definitions/commonHeaderData'
title: chain specific header
commonChain:
type: object
properties:
Expand Down Expand Up @@ -50227,6 +50245,18 @@ definitions:
- Gas: Ether, BNB, Matic, Klay, BTC, etc
- ERC20: ERC20 token
- Cmd: not a real coin, rather a command
commonHeaderData:
type: object
properties:
ethereum_header:
type: string
format: byte
title: binary encoded headers; RLP for ethereum
commonProof:
type: object
properties:
ethereum_proof:
$ref: '#/definitions/ethereumProof'
commonPubKeySet:
type: object
properties:
Expand Down Expand Up @@ -50824,25 +50854,6 @@ definitions:
items:
type: object
$ref: '#/definitions/observerNode'
observerBlockHeader:
type: object
properties:
header:
type: string
format: byte
title: binary encoded headers; RLP for ethereum
height:
type: string
format: int64
hash:
type: string
format: byte
parentHash:
type: string
format: byte
chain_id:
type: string
format: int64
observerCoreParams:
type: object
properties:
Expand Down Expand Up @@ -51022,7 +51033,7 @@ definitions:
type: array
items:
type: object
$ref: '#/definitions/observerBlockHeader'
$ref: '#/definitions/commonBlockHeader'
pagination:
$ref: '#/definitions/v1beta1PageResponse'
observerQueryAllNodeAccountResponse:
Expand Down Expand Up @@ -51066,7 +51077,7 @@ definitions:
type: object
properties:
block_header:
$ref: '#/definitions/observerBlockHeader'
$ref: '#/definitions/commonBlockHeader'
observerQueryGetCoreParamsForChainResponse:
type: object
properties:
Expand Down
13 changes: 5 additions & 8 deletions docs/spec/crosschain/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@

## MsgAddToOutTxTracker

Adds a new record to the outbound transaction tracker.

Only the admin policy account and the observer validators are authorized to
broadcast this message.
AddToOutTxTracker adds a new record to the outbound transaction tracker.
only the admin policy account and the observer validators are authorized to broadcast this message.

```proto
message MsgAddToOutTxTracker {
string creator = 1;
int64 chain_id = 2;
uint64 nonce = 3;
string tx_hash = 4;
ethereum.Proof proof = 5;
common.Proof proof = 5;
string block_hash = 6;
int64 tx_index = 7;
}
```

## MsgRemoveFromOutTxTracker

Removes a record from the outbound transaction tracker by chain ID and nonce.

Only the admin policy account is authorized to broadcast this message.
RemoveFromOutTxTracker removes a record from the outbound transaction tracker by chain ID and nonce.
only the admin policy account is authorized to broadcast this message.

```proto
message MsgRemoveFromOutTxTracker {
Expand Down
6 changes: 3 additions & 3 deletions docs/spec/observer/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ message MsgUpdateKeygen {

## MsgAddBlockHeader

MsgAddBlockHeader handles adding a block header to the store, through majority voting of observers
AddBlockHeader handles adding a block header to the store, through majority voting of observers

```proto
message MsgAddBlockHeader {
string creator = 1;
int64 chain_id = 2;
bytes block_hash = 3;
bytes block_header = 4;
int64 height = 5;
int64 height = 4;
common.HeaderData header = 5;
}
```

23 changes: 22 additions & 1 deletion proto/common/common.proto
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
syntax = "proto3";
package common;

import "common/ethereum/ethereum.proto";
//option (gogoproto.goproto_stringer_all) = false;
//option (gogoproto.stringer_all) = false;
//option (gogoproto.goproto_getters_all) = false;

import "gogoproto/gogo.proto";

option go_package = "github.com/zeta-chain/zetacore/common";
Expand Down Expand Up @@ -62,3 +62,24 @@ message Chain {
ChainName chain_name = 1;
int64 chain_id = 2;
}

message BlockHeader {
int64 height = 1;
bytes hash = 2;
bytes parent_hash = 3;
int64 chain_id = 4;
// chain specific header
HeaderData header = 5 [(gogoproto.nullable) = false];
}

message HeaderData {
oneof data {
bytes ethereum_header = 1; // binary encoded headers; RLP for ethereum
}
}

message Proof {
oneof proof {
ethereum.Proof ethereum_proof = 1;
}
}
4 changes: 2 additions & 2 deletions proto/crosschain/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ syntax = "proto3";
package zetachain.zetacore.crosschain;

import "common/common.proto";
import "common/ethereum/ethereum.proto";
import "gogoproto/gogo.proto";

option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types";
Expand All @@ -27,6 +26,7 @@ message MsgUpdateTssAddress {
}

message MsgUpdateTssAddressResponse {}

message MsgWhitelistERC20 {
string creator = 1;
string erc20_address = 2;
Expand All @@ -44,7 +44,7 @@ message MsgAddToOutTxTracker {
int64 chain_id = 2;
uint64 nonce = 3;
string tx_hash = 4;
ethereum.Proof proof = 5;
common.Proof proof = 5;
string block_hash = 6;
int64 tx_index = 7;
}
Expand Down
15 changes: 0 additions & 15 deletions proto/observer/headers.proto

This file was deleted.

Loading
Loading