Skip to content

Instantly share code, notes, and snippets.

@mossid
Created July 18, 2017 08:38
Show Gist options
  • Save mossid/88ef8c4516fe408d82a40d0cd041edce to your computer and use it in GitHub Desktop.
Save mossid/88ef8c4516fe408d82a40d0cd041edce to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"os"
"errors"
"path/filepath"
"context"
"bytes"
"gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/trie"
// "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/rlp"
// "github.com/ethereum/go-ethereum"
// "github.com/ethereum/go-ethereum"
// "github.com/ethereum/go-ethereum"
cmn "github.com/tendermint/tmlibs/common"
)
type Gateway struct {
client *ethclient.Client
query ethereum.FilterQuery
}
type LogProof struct {
ReceiptHash common.Hash
Receipt *types.Receipt
TxIndex uint
Index uint
Proof []rlp.RawValue
}
func main() {
app := cli.NewApp()
app.Name = "etgateway"
app.Usage = "Relay ethereum logs to tendermint blockchain"
app.Flags = []cli.Flag {
cli.BoolFlag {
Name: "testnet",
Usage: "Ropsten network: pre-configured test network",
},
cli.StringFlag {
Name: "datadir",
Value: filepath.Join(os.Getenv("HOME"), ".ethereum"),
Usage: "Data directory for the databases and keystore",
},
cli.StringFlag {
Name: "ipcpath",
Value: "geth.ipc",
Usage: "Filename for IPC socket/pipe within the datadir",
},
}
app.Action = action
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func action(c *cli.Context) error {
var datadir string
if c.NArg() < 1 {
return errors.New("Usage: etgateway [--testnet] [--datadir ~/.ethereum] [--ipcpath geth.ipc] address")
}
addr := c.Args()[0]
if c.Bool("testnet") {
datadir = filepath.Join(c.String("datadir"), "testnet")
} else {
datadir = c.String("datadir")
}
if !common.IsHexAddress(addr) {
return errors.New("Invalid address format")
}
gateway, err := newGateway(datadir, c.String("ipcpath"), common.HexToAddress(addr))
if err != nil {
return err
}
gateway.start()
cmn.TrapSignal(func() {})
return nil
}
func newGateway(datadir, ipcpath string, addr common.Address) (*Gateway, error) {
client, err := ethclient.Dial(filepath.Join(datadir, ipcpath))
if err != nil {
return nil, err
}
fmt.Printf("%+v\n", addr)
query := ethereum.FilterQuery {
// Addresses: []common.Address{addr},
}
fmt.Printf("%+v\n", query)
return &Gateway{client: client, query: query,}, nil
}
func (g *Gateway) start() {
go g.loop()
}
func (g *Gateway) loop() {
logs := make(chan types.Log)
sub, err := g.client.SubscribeFilterLogs(context.Background(), g.query, logs)
if err != nil {
fmt.Println("Failed to subscribe to log events")
}
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
fmt.Println("log received")
proof, err := g.NewLogProof(log)
if err != nil {
fmt.Println("Error in NewLogProof: ", err)
continue
}
fmt.Println(proof.isValid())
}
}
}
func (g *Gateway) NewLogProof(log types.Log) (*LogProof, error) {
var logReceipt *types.Receipt
keybuf := new(bytes.Buffer)
trie := new(trie.Trie)
count, err := g.client.TransactionCount(context.Background(), log.BlockHash)
if err != nil {
return nil, err
}
for i := 0; i < int(count); i++ {
tx, err := g.client.TransactionInBlock(context.Background(), log.BlockHash, uint(i))
if err != nil {
return nil, err
}
receipt, err := g.client.TransactionReceipt(context.Background(), tx.Hash())
if err != nil {
return nil, err
}
keybuf.Reset()
rlp.Encode(keybuf, uint(i))
bytes, err := rlp.EncodeToBytes(receipt)
if err != nil {
return nil, err
}
trie.Update(keybuf.Bytes(), bytes)
if log.TxIndex == uint(i) {
logReceipt = receipt
}
}
if logReceipt == nil {
return nil, errors.New("Error in NewLogProof")
}
keybuf.Reset()
rlp.Encode(keybuf, log.TxIndex)
header, err := g.client.HeaderByHash(context.Background(), log.BlockHash)
if err != nil {
return nil, err
}
fmt.Printf("Trie Hash: %+v\n", trie.Hash())
return &LogProof {
ReceiptHash: header.ReceiptHash,
Receipt: logReceipt,
TxIndex: log.TxIndex,
Index: log.Index,
Proof: trie.Prove(keybuf.Bytes()),
}, nil
}
func (proof *LogProof) Log() (types.Log) {
return *proof.Receipt.Logs[proof.Index]
}
func (proof *LogProof) isValid() bool {
fmt.Printf("ReceiptHash: %+v\n", proof.ReceiptHash)
keybuf := new(bytes.Buffer)
rlp.Encode(keybuf, proof.TxIndex)
res, err := trie.VerifyProof(proof.ReceiptHash, keybuf.Bytes(), proof.Proof)
if err != nil {
fmt.Println("Error in isValid, VerifyProof: ", err)
fmt.Printf("log: %+v\n", proof.Log())
fmt.Printf("proof: %+v\n", proof)
return false
}
rec, err := rlp.EncodeToBytes(proof.Receipt)
if err != nil || !bytes.Equal(rec, res) {
fmt.Println("Error in isValid, EncodeToBytes: ", err)
return false
}
return true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment