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) (
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) {
block := e.chain.BlockByHash(blockHash)
if block == nil {
return common.Hash{}, errors.New("block not found in exchain")
}
tree, err := e.chain.GetWDT(*uint256.NewInt(block.Header.Height))
func (e *ExChainAPI) GetWDTRoot(ctx context.Context, height uint64) (common.Hash, error) {
tree, err := e.chain.GetWDT(height)
if err != nil {
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) {
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{}
tx, _ := e.chain.GetTransaction(txHash)
if tx == nil {
return nil, errors.New("transaction not found")
}
wtx := tx.GetWithdrawTx()
if wtx == nil {
return nil, errors.New("transaction is not withdrawal tx")
}
receipt := e.chain.GetReceipt(txHash)
if receipt == nil {
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 {
// generate wdt for block failed.
return nil, err
}
item := wrapper.NewTxWrapper(tx).WithdrawalHash()
proof, err := tree.MerkleProof(item.Bytes())
proof, err := tree.Proof(item.Bytes())
if err != nil {
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 {
copy(res.Proof[i][:], p[:])
res.Proof[i] = p
}
res.Value = new(big.Int).SetBytes(wtx.Amount)
res.User = common.BytesToAddress(wtx.User)
res.Coin = []byte(wtx.Coin)
oo, err := e.OutputV0AtBlock(ctx, wblk.Hash())
oo, err := e.OutputV0AtBlock(ctx, receipt.BlockHeight)
if err != nil {
log.WithField("error", err).Error("failed to get output for withdrawal proof")
return nil, err
......@@ -111,22 +109,20 @@ func (e *ExChainAPI) WithdrawalTxs(ctx context.Context, blockNum uint64) ([]comm
return txHashes, nil
}
func (e *ExChainAPI) OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) {
blk := e.chain.BlockByHash(blockHash)
func (e *ExChainAPI) OutputV0AtBlock(ctx context.Context, number uint64) (*eth.OutputV0, error) {
blk := e.chain.GetBlock(uint256.NewInt(number))
if blk == nil {
return nil, errors.New("block not found in exchain")
}
wblk := wrapper.NewBlkWrapper(blk)
root := eth.Bytes32{}
wdtroot, err := e.GetWDTRoot(ctx, blockHash)
wdtroot, err := e.GetWDTRoot(ctx, number)
if err != nil {
log.WithField("error", err).Error("failed to get wdt root")
return nil, err
}
storageRoot := eth.Bytes32(wdtroot[:])
// todo: vicotor implement this.
copy(root[:], wblk.Header().AppRoot)
return &eth.OutputV0{
......
......@@ -3,7 +3,6 @@ package chaindb
import (
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/exchain/go-exchain/exchain"
nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
......@@ -26,7 +25,7 @@ type ChainReader interface {
BlockByHash(hash common.Hash) *nebulav1.Block
GetTransaction(hash common.Hash) (*nebulav1.Transaction, error)
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 {
......@@ -196,14 +195,14 @@ func (m *chainReader) GetBlockReceipts(num *uint256.Int) *nebulav1.TransactionRe
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)
latest := m.CurrentHeight()
if latest.Cmp(cp.End()) < 0 {
if latest.Uint64() < number {
return nil, errors.New("checkpoint not ready")
}
if t, _ := m.wdtCache.Get(cp); t != nil {
return t.(*wdt.MerkleTree), nil
return t.(*wdt.WDT), nil
}
nt, err := m.generateWDT(cp)
if err != nil {
......@@ -213,11 +212,12 @@ func (m *chainReader) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) {
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{
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))
if blk == nil {
return nil, errors.New("block not found in exchain")
......@@ -225,22 +225,13 @@ func (m *chainReader) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error
wblk := wrapper.NewBlkWrapper(blk)
withdrawalTxs.Txs = append(withdrawalTxs.Txs, wblk.FilterTransactions(wrapper.TxTypeFilter(nebulav1.TxType_WithdrawTx))...)
}
leaves := make([][]byte, len(withdrawalTxs.Txs))
for _, tx := range withdrawalTxs.Txs {
content := tx.GetWithdrawTx()
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)
if err := trie.AddTx(tx); err != nil {
return nil, err
}
return tree, nil
}
return trie, nil
}
func (m *chainReader) blockNumberByHash(hash common.Hash) *uint256.Int {
......
......@@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/exchain/go-exchain/exchain"
......@@ -41,7 +40,7 @@ type ChainDB interface {
SubscribeChainEvent(ch chan<- exchain.ChainEvent) event.Subscription
EmitChain(block *nebulav1.Block, hash common.Hash)
ResetHeight(*uint256.Int, bool) error
GetWDT(number uint256.Int) (*wdt.MerkleTree, error)
GetWDT(number uint64) (*wdt.WDT, error)
}
var (
......@@ -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)
latest := m.CurrentHeight()
if latest.Cmp(cp.End()) < 0 {
if latest.Uint64() < cp.End() {
return nil, errors.New("checkpoint not ready")
}
if t, _ := m.wdtCache.Get(cp); t != nil {
return t.(*wdt.MerkleTree), nil
return t.(*wdt.WDT), nil
}
nt, err := m.generateWDT(cp)
if err != nil {
......@@ -140,11 +139,12 @@ func (m *chaindb) GetWDT(number uint256.Int) (*wdt.MerkleTree, error) {
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{
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))
if blk == nil {
return nil, errors.New("block not found in exchain")
......@@ -152,22 +152,13 @@ func (m *chaindb) generateWDT(cp exchain.Checkpoint) (*wdt.MerkleTree, error) {
wblk := wrapper.NewBlkWrapper(blk)
withdrawalTxs.Txs = append(withdrawalTxs.Txs, wblk.FilterTransactions(wrapper.TxTypeFilter(nebulav1.TxType_WithdrawTx))...)
}
leaves := make([][]byte, len(withdrawalTxs.Txs))
for _, tx := range withdrawalTxs.Txs {
content := tx.GetWithdrawTx()
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)
if err := trie.AddTx(tx); err != nil {
return nil, err
}
return tree, nil
}
return trie, nil
}
func (m *chaindb) SaveChainId(chainid *uint256.Int) error {
......
......@@ -3,7 +3,7 @@ package exchain
import "github.com/holiman/uint256"
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 (
type Checkpoint uint64
func (c Checkpoint) Start() *uint256.Int {
func (c Checkpoint) Start() uint64 {
// 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
}
func (c Checkpoint) End() *uint256.Int {
func (c Checkpoint) End() uint64 {
// 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
}
func ToCheckpoint(number uint256.Int) Checkpoint {
if number.IsZero() {
func ToCheckpoint(number uint64) Checkpoint {
if number == 0 {
return 0
}
cp := new(uint256.Int).Div(new(uint256.Int).SubUint64(&number, 1), uint256.NewInt(ChainCheckpointInterval))
return Checkpoint(cp.Uint64())
cp := (number - 1) / ChainCheckpointInterval
return Checkpoint(cp)
}
......@@ -111,12 +111,12 @@ func (s *server) GetWithdrawalProof(ctx context.Context, request *nodev1.Withdra
if receipt == nil {
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 {
return nil, err
}
item := wrapper.NewTxWrapper(tx).WithdrawalHash()
proof, err := tree.MerkleProof(item.Bytes())
proof, err := tree.Proof(item.Bytes())
if err != nil {
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
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{
Txhash: param.TxHash.String(),
}
......
......@@ -914,7 +914,7 @@ type WithdrawalProofResponse struct {
sizeCache protoimpl.SizeCache
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() {
......@@ -949,7 +949,7 @@ func (*WithdrawalProofResponse) Descriptor() ([]byte, []int) {
return file_node_v1_node_proto_rawDescGZIP(), []int{19}
}
func (x *WithdrawalProofResponse) GetProof() [][]byte {
func (x *WithdrawalProofResponse) GetProof() []string {
if x != nil {
return x.Proof
}
......@@ -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,
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, 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,
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,
......
......@@ -141,5 +141,5 @@ message WithdrawalProofRequest {
}
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
import (
"bytes"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
"math"
"sort"
"github.com/ethereum/go-ethereum/log"
"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.
type WithdrawalTree struct {
}
// 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)
// proofList implements ethdb.KeyValueWriter and collects the proofs as
// hex-strings for delivery to rpc-caller.
type proofList []string
// Sort by byte contents.
sort.Slice(leaves, func(i, j int) bool {
return lessThanBytes(leaves[i], leaves[j])
})
// 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
func (n *proofList) Put(key []byte, value []byte) error {
*n = append(*n, hexutil.Encode(value))
return nil
}
// Items returns the original items passed in when creating the Merkle tree.
func (m *MerkleTree) Items() [][]byte {
return m.branches[0]
func (n *proofList) Delete(key []byte) error {
panic("not supported")
}
// Root returns the top-most, Merkle root of the tree.
func (m *MerkleTree) Root() []byte {
return m.branches[len(m.branches)-1][0]
type WDT struct {
id *trie.ID
st *trie.StateTrie
}
// MerkleProof computes a Proof for a leaf from a tree's branches.
func (m *MerkleTree) MerkleProof(leaf []byte) ([][]byte, error) {
nextLeaf := leaf
proof := make([][]byte, m.depth)
for i := uint64(0); i < m.depth; i++ {
leftLeaf, rightLeaf, err := leafPair(m.branches[i], nextLeaf)
func NewWdt() *WDT {
virAccount := common.HexToAddress("0xffee")
id := trie.StorageTrieID(common.Hash{}, crypto.Keccak256Hash(virAccount.Bytes()), common.Hash{})
trieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
st, err := trie.NewStateTrie(id, trieDB)
if err != nil {
return nil, fmt.Errorf("could not find pair: %v", err)
}
if bytes.Equal(leftLeaf, nextLeaf) {
proof[i] = rightLeaf
} else {
proof[i] = leftLeaf
log.Error("failed to create state trie", "err", err)
return nil
}
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.
func VerifyMerkleBranch(root, item []byte, proof [][]byte) bool {
node := safeCopyBytes(item)
for i := 0; i < len(proof); i++ {
if lessThanBytes(node, proof[i]) {
node = hash(node[:], proof[i])
} else {
node = hash(proof[i], node[:])
}
return &WDT{
id: id,
st: st,
}
return bytes.Equal(root, node[:])
}
func leafPair(leaves [][]byte, leaf []byte) ([]byte, []byte, error) {
var found bool
var indexOfLeaf int
for i, item := range leaves {
if bytes.Equal(item, leaf) {
indexOfLeaf = i
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
func (w *WDT) AddTx(tx *nebulav1.Transaction) error {
// key is withdrawalHash
// value is []byte("1")
item := wrapper.NewTxWrapper(tx).WithdrawalHash()
addr := common.Address{}
return w.st.UpdateStorage(addr, item.Bytes(), []byte("1"))
}
// SortAndHash sorts the 2 bytes and keccak256 hashes them.
func SortAndHash(i []byte, j []byte) []byte {
sorted1, sorted2 := Sort2Bytes(i, j)
return hash(sorted1, sorted2)
func (w *WDT) Proof(key []byte) ([]string, error) {
var proof proofList
if err := w.st.Prove(key, &proof); err != nil {
return nil, err
}
return proof, nil
}
func hash(data ...[]byte) []byte {
return crypto.Keccak256(data...)
func (w *WDT) Root() common.Hash {
// root is the root hash of the trie
return w.st.Hash()
}
package wrapper
import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"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"
"github.com/golang/protobuf/proto"
log "github.com/sirupsen/logrus"
"math/big"
)
......@@ -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 {
if t.tx.TxType != nebulav1.TxType_WithdrawTx {
return common.Hash{}
......@@ -48,19 +72,11 @@ func (t *TxWrapper) WithdrawalHash() common.Hash {
if wtx == nil {
return common.Hash{}
}
param := exchain.ExChainWithdrawalParam{
Value: new(big.Int).SetBytes(wtx.Amount),
User: common.BytesToAddress(wtx.User),
Coin: wtx.Coin,
}
// 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
user := common.BytesToAddress(wtx.User)
coin := string(wtx.Coin)
amount := new(big.Int).SetBytes(wtx.Amount)
txHash := t.Hash()
return withdrawalHash(user, coin, amount, txHash)
}
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 {
// 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.
//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)
WithdrawalTxs(ctx context.Context, blockNumber uint64) ([]common.Hash, error)
}
......@@ -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)
}
output, err := n.client.OutputV0AtBlock(ctx, ref.Hash)
output, err := n.client.OutputV0AtBlock(ctx, ref.Number)
if err != nil {
return nil, fmt.Errorf("failed to get L2 output at block %s: %w", ref, err)
}
......
......@@ -25,7 +25,7 @@ type L2Source interface {
L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error)
BlockRefByNumber(ctx context.Context, num uint64) (eth.BlockRef, 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 {
......@@ -290,7 +290,7 @@ func (m *ManagedMode) OutputV0AtTimestamp(ctx context.Context, timestamp uint64)
if err != nil {
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) {
......@@ -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
// 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.
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) {
TxHash: common.HexToHash(tx.TxHash),
}
for i, p := range proof.Proof {
params.WithdrawalProof[i] = p[:]
params.WithdrawalProof[i] = common.HexToHash(p).Bytes()
}
ooProof := bindings.TypesOutputRootProof{
StateRoot: proof.Output.StateRoot,
......@@ -359,7 +359,7 @@ func (l *Operator) DoOperator(ctx context.Context) {
TxHash: common.HexToHash(tx.TxHash),
}
for i, p := range proof.Proof {
params.WithdrawalProof[i] = p[:]
params.WithdrawalProof[i] = common.HexToHash(p).Bytes()
}
ooProof := bindings.TypesOutputRootProof{
StateRoot: proof.Output.StateRoot,
......
......@@ -7,7 +7,7 @@ import (
type WithdrawalProof struct {
Output OutputV0
Proof []Bytes32
Proof []string
Value *big.Int
User common.Address
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