Commit 893bf27b authored by vicotor's avatar vicotor

update for withdrawal

parent a5ab73f9
...@@ -39,52 +39,50 @@ func (e *ExChainAPI) FetchReceipts(ctx context.Context, blockHash common.Hash) ( ...@@ -39,52 +39,50 @@ func (e *ExChainAPI) FetchReceipts(ctx context.Context, blockHash common.Hash) (
return nil, nil, errors.New("FetchReceipts is not implemented in exchain currently") return nil, nil, errors.New("FetchReceipts is not implemented in exchain currently")
} }
func (e *ExChainAPI) GetWDTRoot(ctx context.Context, blockHash common.Hash) (common.Hash, error) { func (e *ExChainAPI) GetWDTRoot(ctx context.Context, height uint64) (common.Hash, error) {
block := e.chain.BlockByHash(blockHash) tree, err := e.chain.GetWDT(height)
if block == nil {
return common.Hash{}, errors.New("block not found in exchain")
}
tree, err := e.chain.GetWDT(*uint256.NewInt(block.Header.Height))
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
return common.BytesToHash(tree.Root()), nil return tree.Root(), nil
} }
func (e *ExChainAPI) WithdrawalProof(ctx context.Context, txHash common.Hash) (*eth.WithdrawalProof, error) { func (e *ExChainAPI) WithdrawalProof(ctx context.Context, txHash common.Hash) (*eth.WithdrawalProof, error) {
block := e.chain.BlockByHash(txHash)
if block == nil {
return nil, errors.New("block not found in exchain")
}
wblk := wrapper.NewBlkWrapper(block)
var res = &eth.WithdrawalProof{} var res = &eth.WithdrawalProof{}
tx, _ := e.chain.GetTransaction(txHash) tx, _ := e.chain.GetTransaction(txHash)
if tx == nil {
return nil, errors.New("transaction not found")
}
wtx := tx.GetWithdrawTx() wtx := tx.GetWithdrawTx()
if wtx == nil { if wtx == nil {
return nil, errors.New("transaction is not withdrawal tx") return nil, errors.New("transaction is not withdrawal tx")
} }
receipt := e.chain.GetReceipt(txHash) receipt := e.chain.GetReceipt(txHash)
if receipt == nil { if receipt == nil {
return nil, errors.New("not found tx receipt") return nil, errors.New("not found tx receipt")
} }
tree, err := e.chain.GetWDT(*uint256.NewInt(receipt.BlockHeight)) tree, err := e.chain.GetWDT(receipt.BlockHeight)
if err != nil { if err != nil {
// generate wdt for block failed.
return nil, err return nil, err
} }
item := wrapper.NewTxWrapper(tx).WithdrawalHash() item := wrapper.NewTxWrapper(tx).WithdrawalHash()
proof, err := tree.MerkleProof(item.Bytes()) proof, err := tree.Proof(item.Bytes())
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("failed to get proof (%s)", err.Error())) return nil, errors.New(fmt.Sprintf("failed to get proof (%s)", err.Error()))
} }
res.Proof = make([]eth.Bytes32, len(proof)) res.Proof = make([]string, len(proof))
for i, p := range proof { for i, p := range proof {
copy(res.Proof[i][:], p[:]) res.Proof[i] = p
} }
res.Value = new(big.Int).SetBytes(wtx.Amount) res.Value = new(big.Int).SetBytes(wtx.Amount)
res.User = common.BytesToAddress(wtx.User) res.User = common.BytesToAddress(wtx.User)
res.Coin = []byte(wtx.Coin) res.Coin = []byte(wtx.Coin)
oo, err := e.OutputV0AtBlock(ctx, wblk.Hash()) oo, err := e.OutputV0AtBlock(ctx, receipt.BlockHeight)
if err != nil { if err != nil {
log.WithField("error", err).Error("failed to get output for withdrawal proof") log.WithField("error", err).Error("failed to get output for withdrawal proof")
return nil, err return nil, err
...@@ -111,22 +109,20 @@ func (e *ExChainAPI) WithdrawalTxs(ctx context.Context, blockNum uint64) ([]comm ...@@ -111,22 +109,20 @@ func (e *ExChainAPI) WithdrawalTxs(ctx context.Context, blockNum uint64) ([]comm
return txHashes, nil return txHashes, nil
} }
func (e *ExChainAPI) OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) { func (e *ExChainAPI) OutputV0AtBlock(ctx context.Context, number uint64) (*eth.OutputV0, error) {
blk := e.chain.BlockByHash(blockHash) blk := e.chain.GetBlock(uint256.NewInt(number))
if blk == nil { if blk == nil {
return nil, errors.New("block not found in exchain") return nil, errors.New("block not found in exchain")
} }
wblk := wrapper.NewBlkWrapper(blk) wblk := wrapper.NewBlkWrapper(blk)
root := eth.Bytes32{} root := eth.Bytes32{}
wdtroot, err := e.GetWDTRoot(ctx, blockHash) wdtroot, err := e.GetWDTRoot(ctx, number)
if err != nil { if err != nil {
log.WithField("error", err).Error("failed to get wdt root") log.WithField("error", err).Error("failed to get wdt root")
return nil, err return nil, err
} }
storageRoot := eth.Bytes32(wdtroot[:]) storageRoot := eth.Bytes32(wdtroot[:])
// todo: vicotor implement this.
copy(root[:], wblk.Header().AppRoot) copy(root[:], wblk.Header().AppRoot)
return &eth.OutputV0{ return &eth.OutputV0{
......
...@@ -3,7 +3,6 @@ package chaindb ...@@ -3,7 +3,6 @@ package chaindb
import ( import (
"errors" "errors"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/exchain/go-exchain/exchain" "github.com/exchain/go-exchain/exchain"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1" nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
...@@ -26,7 +25,7 @@ type ChainReader interface { ...@@ -26,7 +25,7 @@ type ChainReader interface {
BlockByHash(hash common.Hash) *nebulav1.Block BlockByHash(hash common.Hash) *nebulav1.Block
GetTransaction(hash common.Hash) (*nebulav1.Transaction, error) GetTransaction(hash common.Hash) (*nebulav1.Transaction, error)
GetReceipt(hash common.Hash) *nebulav1.TransactionReceipt GetReceipt(hash common.Hash) *nebulav1.TransactionReceipt
GetWDT(number uint256.Int) (*wdt.MerkleTree, error) GetWDT(number uint64) (*wdt.WDT, error)
} }
func NewChainReader(log log.Logger, database metadb.Database) ChainReader { func NewChainReader(log log.Logger, database metadb.Database) ChainReader {
...@@ -196,14 +195,14 @@ func (m *chainReader) GetBlockReceipts(num *uint256.Int) *nebulav1.TransactionRe ...@@ -196,14 +195,14 @@ func (m *chainReader) GetBlockReceipts(num *uint256.Int) *nebulav1.TransactionRe
return m.getBlockReceipts(num) return m.getBlockReceipts(num)
} }
func (m *chainReader) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) { func (m *chainReader) GetWDT(number uint64) (*wdt.WDT, error) {
cp := exchain.ToCheckpoint(number) cp := exchain.ToCheckpoint(number)
latest := m.CurrentHeight() latest := m.CurrentHeight()
if latest.Cmp(cp.End()) < 0 { if latest.Uint64() < number {
return nil, errors.New("checkpoint not ready") return nil, errors.New("checkpoint not ready")
} }
if t, _ := m.wdtCache.Get(cp); t != nil { if t, _ := m.wdtCache.Get(cp); t != nil {
return t.(*wdt.MerkleTree), nil return t.(*wdt.WDT), nil
} }
nt, err := m.generateWDT(cp) nt, err := m.generateWDT(cp)
if err != nil { if err != nil {
...@@ -213,11 +212,12 @@ func (m *chainReader) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) { ...@@ -213,11 +212,12 @@ func (m *chainReader) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) {
return nt, nil return nt, nil
} }
func (m *chainReader) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error) { func (m *chainReader) generateWDT(cp exchain.Checkpoint) (*wdt.WDT, error) {
withdrawalTxs := &nebulav1.TransactionList{ withdrawalTxs := &nebulav1.TransactionList{
Txs: make([]*nebulav1.Transaction, 0), Txs: make([]*nebulav1.Transaction, 0),
} }
for i := cp.Start().Uint64(); i <= cp.End().Uint64(); i++ { trie := wdt.NewWdt()
for i := cp.Start(); i <= cp.End(); i++ {
blk := m.GetBlock(uint256.NewInt(i)) blk := m.GetBlock(uint256.NewInt(i))
if blk == nil { if blk == nil {
return nil, errors.New("block not found in exchain") return nil, errors.New("block not found in exchain")
...@@ -225,22 +225,13 @@ func (m *chainReader) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error ...@@ -225,22 +225,13 @@ func (m *chainReader) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error
wblk := wrapper.NewBlkWrapper(blk) wblk := wrapper.NewBlkWrapper(blk)
withdrawalTxs.Txs = append(withdrawalTxs.Txs, wblk.FilterTransactions(wrapper.TxTypeFilter(nebulav1.TxType_WithdrawTx))...) withdrawalTxs.Txs = append(withdrawalTxs.Txs, wblk.FilterTransactions(wrapper.TxTypeFilter(nebulav1.TxType_WithdrawTx))...)
} }
leaves := make([][]byte, len(withdrawalTxs.Txs))
for _, tx := range withdrawalTxs.Txs { for _, tx := range withdrawalTxs.Txs {
content := tx.GetWithdrawTx() if err := trie.AddTx(tx); err != nil {
data := make([]byte, 0)
data = append(data, content.User...)
data = append(data, content.Coin...)
data = append(data, content.Amount...)
leaves = append(leaves, crypto.Keccak256Hash(data).Bytes())
}
tree, err := wdt.GenerateTreeFromHashedItems(leaves)
if err != nil {
m.log.Error("failed to generate wdt tree", "err", err)
return nil, err return nil, err
} }
return tree, nil }
return trie, nil
} }
func (m *chainReader) blockNumberByHash(hash common.Hash) *uint256.Int { func (m *chainReader) blockNumberByHash(hash common.Hash) *uint256.Int {
......
...@@ -4,7 +4,6 @@ import ( ...@@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/exchain/go-exchain/exchain" "github.com/exchain/go-exchain/exchain"
...@@ -41,7 +40,7 @@ type ChainDB interface { ...@@ -41,7 +40,7 @@ type ChainDB interface {
SubscribeChainEvent(ch chan<- exchain.ChainEvent) event.Subscription SubscribeChainEvent(ch chan<- exchain.ChainEvent) event.Subscription
EmitChain(block *nebulav1.Block, hash common.Hash) EmitChain(block *nebulav1.Block, hash common.Hash)
ResetHeight(*uint256.Int, bool) error ResetHeight(*uint256.Int, bool) error
GetWDT(number uint256.Int) (*wdt.MerkleTree, error) GetWDT(number uint64) (*wdt.WDT, error)
} }
var ( var (
...@@ -123,14 +122,14 @@ func (m *chaindb) ChainId() (*uint256.Int, error) { ...@@ -123,14 +122,14 @@ func (m *chaindb) ChainId() (*uint256.Int, error) {
} }
} }
func (m *chaindb) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) { func (m *chaindb) GetWDT(number uint64) (*wdt.WDT, error) {
cp := exchain.ToCheckpoint(number) cp := exchain.ToCheckpoint(number)
latest := m.CurrentHeight() latest := m.CurrentHeight()
if latest.Cmp(cp.End()) < 0 { if latest.Uint64() < cp.End() {
return nil, errors.New("checkpoint not ready") return nil, errors.New("checkpoint not ready")
} }
if t, _ := m.wdtCache.Get(cp); t != nil { if t, _ := m.wdtCache.Get(cp); t != nil {
return t.(*wdt.MerkleTree), nil return t.(*wdt.WDT), nil
} }
nt, err := m.generateWDT(cp) nt, err := m.generateWDT(cp)
if err != nil { if err != nil {
...@@ -140,11 +139,12 @@ func (m *chaindb) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) { ...@@ -140,11 +139,12 @@ func (m *chaindb) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) {
return nt, nil return nt, nil
} }
func (m *chaindb) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error) { func (m *chaindb) generateWDT(cp exchain.Checkpoint) (*wdt.WDT, error) {
withdrawalTxs := &nebulav1.TransactionList{ withdrawalTxs := &nebulav1.TransactionList{
Txs: make([]*nebulav1.Transaction, 0), Txs: make([]*nebulav1.Transaction, 0),
} }
for i := cp.Start().Uint64(); i <= cp.End().Uint64(); i++ { trie := wdt.NewWdt()
for i := cp.Start(); i <= cp.End(); i++ {
blk := m.GetBlock(uint256.NewInt(i)) blk := m.GetBlock(uint256.NewInt(i))
if blk == nil { if blk == nil {
return nil, errors.New("block not found in exchain") return nil, errors.New("block not found in exchain")
...@@ -152,22 +152,13 @@ func (m *chaindb) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error) { ...@@ -152,22 +152,13 @@ func (m *chaindb) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error) {
wblk := wrapper.NewBlkWrapper(blk) wblk := wrapper.NewBlkWrapper(blk)
withdrawalTxs.Txs = append(withdrawalTxs.Txs, wblk.FilterTransactions(wrapper.TxTypeFilter(nebulav1.TxType_WithdrawTx))...) withdrawalTxs.Txs = append(withdrawalTxs.Txs, wblk.FilterTransactions(wrapper.TxTypeFilter(nebulav1.TxType_WithdrawTx))...)
} }
leaves := make([][]byte, len(withdrawalTxs.Txs))
for _, tx := range withdrawalTxs.Txs { for _, tx := range withdrawalTxs.Txs {
content := tx.GetWithdrawTx() if err := trie.AddTx(tx); err != nil {
data := make([]byte, 0)
data = append(data, content.User...)
data = append(data, content.Coin...)
data = append(data, content.Amount...)
leaves = append(leaves, crypto.Keccak256Hash(data).Bytes())
}
tree, err := wdt.GenerateTreeFromHashedItems(leaves)
if err != nil {
m.log.Error("failed to generate wdt tree", "err", err)
return nil, err return nil, err
} }
return tree, nil }
return trie, nil
} }
func (m *chaindb) SaveChainId(chainid *uint256.Int) error { func (m *chaindb) SaveChainId(chainid *uint256.Int) error {
......
...@@ -3,7 +3,7 @@ package exchain ...@@ -3,7 +3,7 @@ package exchain
import "github.com/holiman/uint256" import "github.com/holiman/uint256"
const ( const (
ChainCheckpointInterval = 120 // 1 checkpoint every 120 blocks, from 1 to 120, 121 to 240, etc. ChainCheckpointInterval = uint64(120) // 1 checkpoint every 120 blocks, from 1 to 120, 121 to 240, etc.
) )
...@@ -13,22 +13,22 @@ var ( ...@@ -13,22 +13,22 @@ var (
type Checkpoint uint64 type Checkpoint uint64
func (c Checkpoint) Start() *uint256.Int { func (c Checkpoint) Start() uint64 {
// start = interval * cp + 1 // start = interval * cp + 1
start := new(uint256.Int).Add(big1, new(uint256.Int).Mul(uint256.NewInt(uint64(c)), uint256.NewInt(ChainCheckpointInterval))) start := ChainCheckpointInterval*uint64(c) + 1
return start return start
} }
func (c Checkpoint) End() *uint256.Int { func (c Checkpoint) End() uint64 {
// end = interval * (cp + 1) // end = interval * (cp + 1)
end := new(uint256.Int).Mul(new(uint256.Int).Add(big1, uint256.NewInt(uint64(c))), uint256.NewInt(ChainCheckpointInterval)) end := ChainCheckpointInterval * (uint64(c) + 1)
return end return end
} }
func ToCheckpoint(number uint256.Int) Checkpoint { func ToCheckpoint(number uint64) Checkpoint {
if number.IsZero() { if number == 0 {
return 0 return 0
} }
cp := new(uint256.Int).Div(new(uint256.Int).SubUint64(&number, 1), uint256.NewInt(ChainCheckpointInterval)) cp := (number - 1) / ChainCheckpointInterval
return Checkpoint(cp.Uint64()) return Checkpoint(cp)
} }
...@@ -111,12 +111,12 @@ func (s *server) GetWithdrawalProof(ctx context.Context, request *nodev1.Withdra ...@@ -111,12 +111,12 @@ func (s *server) GetWithdrawalProof(ctx context.Context, request *nodev1.Withdra
if receipt == nil { if receipt == nil {
return nil, errors.New("not found tx receipt") return nil, errors.New("not found tx receipt")
} }
tree, err := s.chain.GetWDT(*uint256.NewInt(receipt.BlockHeight)) tree, err := s.chain.GetWDT(receipt.BlockHeight)
if err != nil { if err != nil {
return nil, err return nil, err
} }
item := wrapper.NewTxWrapper(tx).WithdrawalHash() item := wrapper.NewTxWrapper(tx).WithdrawalHash()
proof, err := tree.MerkleProof(item.Bytes()) proof, err := tree.Proof(item.Bytes())
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("failed to get proof (%s)", err.Error())) return nil, errors.New(fmt.Sprintf("failed to get proof (%s)", err.Error()))
} }
......
...@@ -38,7 +38,7 @@ func (e *ExClient) BlockByNumber(ctx context.Context, number *big.Int) (*wrapper ...@@ -38,7 +38,7 @@ func (e *ExClient) BlockByNumber(ctx context.Context, number *big.Int) (*wrapper
return wrapper.NewBlkWrapper(res.Block), nil return wrapper.NewBlkWrapper(res.Block), nil
} }
func (e *ExClient) GetWithdrawalProof(ctx context.Context, param exchain.ExChainWithdrawalParam) ([][]byte, error) { func (e *ExClient) GetWithdrawalProof(ctx context.Context, param exchain.ExChainWithdrawalParam) ([]string, error) {
req := &nodev1.WithdrawalProofRequest{ req := &nodev1.WithdrawalProofRequest{
Txhash: param.TxHash.String(), Txhash: param.TxHash.String(),
} }
......
...@@ -914,7 +914,7 @@ type WithdrawalProofResponse struct { ...@@ -914,7 +914,7 @@ type WithdrawalProofResponse struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Proof [][]byte `protobuf:"bytes,1,rep,name=proof,proto3" json:"proof,omitempty"` Proof []string `protobuf:"bytes,1,rep,name=proof,proto3" json:"proof,omitempty"`
} }
func (x *WithdrawalProofResponse) Reset() { func (x *WithdrawalProofResponse) Reset() {
...@@ -949,7 +949,7 @@ func (*WithdrawalProofResponse) Descriptor() ([]byte, []int) { ...@@ -949,7 +949,7 @@ func (*WithdrawalProofResponse) Descriptor() ([]byte, []int) {
return file_node_v1_node_proto_rawDescGZIP(), []int{19} return file_node_v1_node_proto_rawDescGZIP(), []int{19}
} }
func (x *WithdrawalProofResponse) GetProof() [][]byte { func (x *WithdrawalProofResponse) GetProof() []string {
if x != nil { if x != nil {
return x.Proof return x.Proof
} }
...@@ -1052,7 +1052,7 @@ var file_node_v1_node_proto_rawDesc = []byte{ ...@@ -1052,7 +1052,7 @@ var file_node_v1_node_proto_rawDesc = []byte{
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x68, 0x61, 0x73, 0x68, 0x22, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, 0x68, 0x61, 0x73, 0x68, 0x22,
0x2f, 0x0a, 0x17, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x2f, 0x0a, 0x17, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x50, 0x72, 0x6f,
0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72,
0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66,
0x32, 0xf6, 0x0b, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x75, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x32, 0xf6, 0x0b, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x75, 0x0a, 0x0a, 0x47, 0x65, 0x74,
0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x2e, 0x65, 0x78, 0x63, 0x68, 0x61, 0x69, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x2e, 0x65, 0x78, 0x63, 0x68, 0x61, 0x69,
0x6e, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x6e, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
......
...@@ -141,5 +141,5 @@ message WithdrawalProofRequest { ...@@ -141,5 +141,5 @@ message WithdrawalProofRequest {
} }
message WithdrawalProofResponse { message WithdrawalProofResponse {
repeated bytes proof = 1; repeated string proof = 1;
} }
package wdt
import "bytes"
// Sorts2Bytes by contents.
func Sort2Bytes(i []byte, j []byte) ([]byte, []byte) {
if lessThanBytes(i, j) {
return i, j
} else {
return j, i
}
}
func lessThanBytes(i []byte, j []byte) bool {
return bytes.Compare(i, j) <= 0
}
func safeCopyBytes(cp []byte) []byte {
if cp != nil {
copied := make([]byte, len(cp))
copy(copied, cp)
return copied
}
return nil
}
func copy2dBytes(ary [][]byte) [][]byte {
if ary != nil {
copied := make([][]byte, len(ary))
for i, a := range ary {
copied[i] = safeCopyBytes(a)
}
return copied
}
return nil
}
func padTo(b []byte, size int) []byte {
if len(b) > size {
return b
}
return append(b, make([]byte, size-len(b))...)
}
// Find the next power of 2 unless n is already a power of 2.
func nextPowerOf2(n uint64) uint64 {
var count uint64 = 0
if isPowerOfTwo(n) {
return n
}
for n != 0 {
n >>= 1
count += 1
}
return 1 << count
}
func isPowerOfTwo(n uint64) bool {
return (n & (n - 1)) == 0
}
package wdt package wdt
import ( import (
"bytes" "github.com/ethereum/go-ethereum/common"
"errors" "github.com/ethereum/go-ethereum/common/hexutil"
"fmt" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"math" "github.com/ethereum/go-ethereum/log"
"sort" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
"github.com/exchain/go-exchain/exchain/wrapper"
) )
// WithdrawalTree (wdt) is a merkle tree that stores all the withdrawal information. // proofList implements ethdb.KeyValueWriter and collects the proofs as
type WithdrawalTree struct { // hex-strings for delivery to rpc-caller.
} type proofList []string
// MerkleTree implements a general purpose Merkle tree.
type MerkleTree struct {
branches [][][]byte
depth uint64
}
func GenerateTreeFromItems(items [][]byte) (*MerkleTree, error) {
// Pad all items to 32 bytes.
leaves := copy2dBytes(items)
for i := range leaves {
leaves[i] = hash(padTo(leaves[i], 32))
}
return GenerateTreeFromHashedItems(leaves)
}
// GenerateTreeFromItems constructs a Merkle tree from a sequence of byte slices.
func GenerateTreeFromHashedItems(items [][]byte) (*MerkleTree, error) {
if len(items) == 0 {
return nil, errors.New("no items provided to generate Merkle tree")
}
// Clone the slice to prevent mutation.
leaves := copy2dBytes(items)
// Sort by byte contents. func (n *proofList) Put(key []byte, value []byte) error {
sort.Slice(leaves, func(i, j int) bool { *n = append(*n, hexutil.Encode(value))
return lessThanBytes(leaves[i], leaves[j]) return nil
})
// Even out if uneven.
if len(leaves)%2 == 1 {
duplicate := safeCopyBytes(leaves[len(leaves)-1])
leaves = append(leaves, duplicate)
}
// Append duplicate nodes until even.
nextPowOfItems := nextPowerOf2(uint64(len(leaves)))
for len(leaves) < int(nextPowOfItems) {
leaves = append(leaves, leaves[len(leaves)-2], leaves[len(leaves)-1])
}
depth := uint64(math.Log2(float64(len(leaves)) + 1))
layers := make([][][]byte, depth+1)
layers[0] = leaves
for i := uint64(0); i < depth; i++ {
var updatedValues [][]byte
for j := 0; j < len(layers[i]); j += 2 {
concat := SortAndHash(layers[i][j], layers[i][j+1])
updatedValues = append(updatedValues, concat[:])
}
layers[i+1] = updatedValues
}
return &MerkleTree{
branches: layers,
depth: depth,
}, nil
} }
// Items returns the original items passed in when creating the Merkle tree. func (n *proofList) Delete(key []byte) error {
func (m *MerkleTree) Items() [][]byte { panic("not supported")
return m.branches[0]
} }
// Root returns the top-most, Merkle root of the tree. type WDT struct {
func (m *MerkleTree) Root() []byte { id *trie.ID
return m.branches[len(m.branches)-1][0] st *trie.StateTrie
} }
// MerkleProof computes a Proof for a leaf from a tree's branches. func NewWdt() *WDT {
func (m *MerkleTree) MerkleProof(leaf []byte) ([][]byte, error) { virAccount := common.HexToAddress("0xffee")
nextLeaf := leaf id := trie.StorageTrieID(common.Hash{}, crypto.Keccak256Hash(virAccount.Bytes()), common.Hash{})
proof := make([][]byte, m.depth) trieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
for i := uint64(0); i < m.depth; i++ { st, err := trie.NewStateTrie(id, trieDB)
leftLeaf, rightLeaf, err := leafPair(m.branches[i], nextLeaf)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not find pair: %v", err) log.Error("failed to create state trie", "err", err)
} return nil
if bytes.Equal(leftLeaf, nextLeaf) {
proof[i] = rightLeaf
} else {
proof[i] = leftLeaf
} }
nextLeaf = hash(leftLeaf, rightLeaf)
}
return proof, nil
}
func (m *MerkleTree) MerkleProofOfIndex(indexOfLeaf uint64) ([][]byte, error) {
if int(indexOfLeaf) > len(m.branches[0]) {
return nil, fmt.Errorf("could not find index %d, greater than length %d", indexOfLeaf, m.branches[0])
}
return m.MerkleProof(m.branches[0][indexOfLeaf])
}
// VerifyMerkleBranch verifies a Merkle branch against a root of a tree. return &WDT{
func VerifyMerkleBranch(root, item []byte, proof [][]byte) bool { id: id,
node := safeCopyBytes(item) st: st,
for i := 0; i < len(proof); i++ {
if lessThanBytes(node, proof[i]) {
node = hash(node[:], proof[i])
} else {
node = hash(proof[i], node[:])
}
} }
return bytes.Equal(root, node[:])
} }
func leafPair(leaves [][]byte, leaf []byte) ([]byte, []byte, error) { func (w *WDT) AddTx(tx *nebulav1.Transaction) error {
var found bool // key is withdrawalHash
var indexOfLeaf int // value is []byte("1")
for i, item := range leaves { item := wrapper.NewTxWrapper(tx).WithdrawalHash()
if bytes.Equal(item, leaf) { addr := common.Address{}
indexOfLeaf = i return w.st.UpdateStorage(addr, item.Bytes(), []byte("1"))
found = true
break
}
}
if !found {
return nil, nil, fmt.Errorf("could not find leaf %#x", leaf)
}
var otherLeaf []byte
// Chcek if the leaf is on the left side.
if indexOfLeaf%2 == 0 {
otherLeaf = safeCopyBytes(leaves[indexOfLeaf+1])
} else {
otherLeaf = safeCopyBytes(leaves[indexOfLeaf-1])
}
leftLeaf, rightLeaf := Sort2Bytes(leaf, otherLeaf)
return leftLeaf, rightLeaf, nil
} }
// SortAndHash sorts the 2 bytes and keccak256 hashes them. func (w *WDT) Proof(key []byte) ([]string, error) {
func SortAndHash(i []byte, j []byte) []byte { var proof proofList
sorted1, sorted2 := Sort2Bytes(i, j) if err := w.st.Prove(key, &proof); err != nil {
return hash(sorted1, sorted2) return nil, err
}
return proof, nil
} }
func hash(data ...[]byte) []byte { func (w *WDT) Root() common.Hash {
return crypto.Keccak256(data...) // root is the root hash of the trie
return w.st.Hash()
} }
package wrapper package wrapper
import ( import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/exchain/go-exchain/exchain"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1" nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
log "github.com/sirupsen/logrus"
"math/big" "math/big"
) )
...@@ -40,6 +38,32 @@ func (t *TxWrapper) calcHash() common.Hash { ...@@ -40,6 +38,32 @@ func (t *TxWrapper) calcHash() common.Hash {
} }
func withdrawalHash(user common.Address, coin string, amount *big.Int, txHash common.Hash) common.Hash {
uint256Ty, _ := abi.NewType("uint256", "", nil)
bytes32Ty, _ := abi.NewType("bytes32", "", nil)
addressTy, _ := abi.NewType("address", "", nil)
bytesTy, _ := abi.NewType("bytes", "", nil)
arguments := abi.Arguments{
{
Type: addressTy,
},
{
Type: bytesTy,
},
{
Type: uint256Ty,
},
{
Type: bytes32Ty,
},
}
data, err := arguments.Pack(user, []byte(coin), amount, txHash)
if err != nil {
return common.Hash{}
}
return crypto.Keccak256Hash(data)
}
func (t *TxWrapper) WithdrawalHash() common.Hash { func (t *TxWrapper) WithdrawalHash() common.Hash {
if t.tx.TxType != nebulav1.TxType_WithdrawTx { if t.tx.TxType != nebulav1.TxType_WithdrawTx {
return common.Hash{} return common.Hash{}
...@@ -48,19 +72,11 @@ func (t *TxWrapper) WithdrawalHash() common.Hash { ...@@ -48,19 +72,11 @@ func (t *TxWrapper) WithdrawalHash() common.Hash {
if wtx == nil { if wtx == nil {
return common.Hash{} return common.Hash{}
} }
param := exchain.ExChainWithdrawalParam{ user := common.BytesToAddress(wtx.User)
Value: new(big.Int).SetBytes(wtx.Amount), coin := string(wtx.Coin)
User: common.BytesToAddress(wtx.User), amount := new(big.Int).SetBytes(wtx.Amount)
Coin: wtx.Coin, txHash := t.Hash()
} return withdrawalHash(user, coin, amount, txHash)
// todo: vicotor check rlp encode?
data, err := rlp.EncodeToBytes(param)
if err != nil {
log.WithField("err", err).Error("rlp encode withdrawal param failed")
return common.Hash{}
}
hash := crypto.Keccak256Hash(data)
return hash
} }
func (t *TxWrapper) Bytes() ([]byte, error) { func (t *TxWrapper) Bytes() ([]byte, error) {
......
package wrapper
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"math/big"
"testing"
)
func TestWhash(t *testing.T) {
user := common.HexToAddress("0x000000000000000000000000000000000000dead")
coin := "ETH"
value := big.NewInt(1e18)
//ethers.keccak256(ethers.toUtf8Bytes("test transaction"));
hash := crypto.Keccak256Hash([]byte("test transaction"))
whash := Whash(user, coin, value, hash)
expected := common.HexToHash("0xe7251ba12a5199190894e1c38c2bc80324901f054ccd2523640e47032630c560")
if whash != expected {
t.Errorf("expected %s, got %s", expected.Hex(), whash.Hex())
} else {
t.Logf("success")
}
}
...@@ -20,7 +20,7 @@ type l2Client interface { ...@@ -20,7 +20,7 @@ type l2Client interface {
// GetProof returns a proof of the account, it may return a nil result without error if the address was not found. // GetProof returns a proof of the account, it may return a nil result without error if the address was not found.
// Optionally keys of the account storage trie can be specified to include with corresponding values in the proof. // Optionally keys of the account storage trie can be specified to include with corresponding values in the proof.
//GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error) //GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error)
OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) OutputV0AtBlock(ctx context.Context, number uint64) (*eth.OutputV0, error)
WithdrawalProof(ctx context.Context, txHash common.Hash) (*eth.WithdrawalProof, error) WithdrawalProof(ctx context.Context, txHash common.Hash) (*eth.WithdrawalProof, error)
WithdrawalTxs(ctx context.Context, blockNumber uint64) ([]common.Hash, error) WithdrawalTxs(ctx context.Context, blockNumber uint64) ([]common.Hash, error)
} }
...@@ -135,7 +135,7 @@ func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*et ...@@ -135,7 +135,7 @@ func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*et
return nil, fmt.Errorf("failed to get L2 block ref with sync status: %w", err) return nil, fmt.Errorf("failed to get L2 block ref with sync status: %w", err)
} }
output, err := n.client.OutputV0AtBlock(ctx, ref.Hash) output, err := n.client.OutputV0AtBlock(ctx, ref.Number)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get L2 output at block %s: %w", ref, err) return nil, fmt.Errorf("failed to get L2 output at block %s: %w", ref, err)
} }
......
...@@ -25,7 +25,7 @@ type L2Source interface { ...@@ -25,7 +25,7 @@ type L2Source interface {
L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error) L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error)
BlockRefByNumber(ctx context.Context, num uint64) (eth.BlockRef, error) BlockRefByNumber(ctx context.Context, num uint64) (eth.BlockRef, error)
FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error)
OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) OutputV0AtBlock(ctx context.Context, num uint64) (*eth.OutputV0, error)
} }
type L1Source interface { type L1Source interface {
...@@ -290,7 +290,7 @@ func (m *ManagedMode) OutputV0AtTimestamp(ctx context.Context, timestamp uint64) ...@@ -290,7 +290,7 @@ func (m *ManagedMode) OutputV0AtTimestamp(ctx context.Context, timestamp uint64)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return m.l2.OutputV0AtBlock(ctx, ref.Hash) return m.l2.OutputV0AtBlock(ctx, ref.Number)
} }
func (m *ManagedMode) PendingOutputV0AtTimestamp(ctx context.Context, timestamp uint64) (*eth.OutputV0, error) { func (m *ManagedMode) PendingOutputV0AtTimestamp(ctx context.Context, timestamp uint64) (*eth.OutputV0, error) {
...@@ -305,5 +305,5 @@ func (m *ManagedMode) PendingOutputV0AtTimestamp(ctx context.Context, timestamp ...@@ -305,5 +305,5 @@ func (m *ManagedMode) PendingOutputV0AtTimestamp(ctx context.Context, timestamp
// TODO: Once interop reorgs are supported (see #13645), replace with the output root preimage of an actual pending // TODO: Once interop reorgs are supported (see #13645), replace with the output root preimage of an actual pending
// block contained in the optimistic block deposited transaction - https://github.com/ethereum-optimism/specs/pull/489 // block contained in the optimistic block deposited transaction - https://github.com/ethereum-optimism/specs/pull/489
// For now, we use the output at timestamp as-if it didn't contain invalid messages for happy path testing. // For now, we use the output at timestamp as-if it didn't contain invalid messages for happy path testing.
return m.l2.OutputV0AtBlock(ctx, ref.Hash) return m.l2.OutputV0AtBlock(ctx, ref.Number)
} }
...@@ -285,7 +285,7 @@ func (l *Operator) DoOperator(ctx context.Context) { ...@@ -285,7 +285,7 @@ func (l *Operator) DoOperator(ctx context.Context) {
TxHash: common.HexToHash(tx.TxHash), TxHash: common.HexToHash(tx.TxHash),
} }
for i, p := range proof.Proof { for i, p := range proof.Proof {
params.WithdrawalProof[i] = p[:] params.WithdrawalProof[i] = common.HexToHash(p).Bytes()
} }
ooProof := bindings.TypesOutputRootProof{ ooProof := bindings.TypesOutputRootProof{
StateRoot: proof.Output.StateRoot, StateRoot: proof.Output.StateRoot,
...@@ -359,7 +359,7 @@ func (l *Operator) DoOperator(ctx context.Context) { ...@@ -359,7 +359,7 @@ func (l *Operator) DoOperator(ctx context.Context) {
TxHash: common.HexToHash(tx.TxHash), TxHash: common.HexToHash(tx.TxHash),
} }
for i, p := range proof.Proof { for i, p := range proof.Proof {
params.WithdrawalProof[i] = p[:] params.WithdrawalProof[i] = common.HexToHash(p).Bytes()
} }
ooProof := bindings.TypesOutputRootProof{ ooProof := bindings.TypesOutputRootProof{
StateRoot: proof.Output.StateRoot, StateRoot: proof.Output.StateRoot,
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
type WithdrawalProof struct { type WithdrawalProof struct {
Output OutputV0 Output OutputV0
Proof []Bytes32 Proof []string
Value *big.Int Value *big.Int
User common.Address User common.Address
Coin []byte Coin []byte
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment