Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
893bf27b
Commit
893bf27b
authored
Apr 16, 2025
by
vicotor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update for withdrawal
parent
a5ab73f9
Changes
16
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
165 additions
and
299 deletions
+165
-299
engine.go
engine/engine.go
+18
-22
chainReader.go
exchain/chaindb/chainReader.go
+12
-21
chaindb.go
exchain/chaindb/chaindb.go
+12
-21
checkpoint.go
exchain/checkpoint.go
+9
-9
server.go
exchain/exchainapi/server.go
+2
-2
client.go
exchain/exchainclient/client.go
+1
-1
node.pb.go
exchain/protocol/gen/go/node/v1/node.pb.go
+3
-3
node.proto
exchain/protocol/proto/node/v1/node.proto
+1
-1
helper.go
exchain/wdt/helper.go
+0
-63
wdt.go
exchain/wdt/wdt.go
+44
-132
tx.go
exchain/wrapper/tx.go
+32
-16
tx_test.go
exchain/wrapper/tx_test.go
+23
-0
api.go
op-node/node/api.go
+2
-2
system.go
op-node/rollup/interop/managed/system.go
+3
-3
operator.go
op-proposer/proposer/operator.go
+2
-2
withdrawal_proof.go
op-service/eth/withdrawal_proof.go
+1
-1
No files found.
engine/engine.go
View file @
893bf27b
...
...
@@ -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
.
Merkle
Proof
(
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
{
...
...
exchain/chaindb/chainReader.go
View file @
893bf27b
...
...
@@ -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
uint
256
.
Int
)
(
*
wdt
.
MerkleTree
,
error
)
GetWDT
(
number
uint
64
)
(
*
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
uint
256
.
Int
)
(
*
wdt
.
MerkleTree
,
error
)
{
func
(
m
*
chainReader
)
GetWDT
(
number
uint
64
)
(
*
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
{
...
...
exchain/chaindb/chaindb.go
View file @
893bf27b
...
...
@@ -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
uint
256
.
Int
)
(
*
wdt
.
MerkleTree
,
error
)
GetWDT
(
number
uint
64
)
(
*
wdt
.
WDT
,
error
)
}
var
(
...
...
@@ -123,14 +122,14 @@ func (m *chaindb) ChainId() (*uint256.Int, error) {
}
}
func
(
m
*
chaindb
)
GetWDT
(
number
uint
256
.
Int
)
(
*
wdt
.
MerkleTree
,
error
)
{
func
(
m
*
chaindb
)
GetWDT
(
number
uint
64
)
(
*
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
{
...
...
exchain/checkpoint.go
View file @
893bf27b
...
...
@@ -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
uint
256
.
Int
)
Checkpoint
{
if
number
.
IsZero
()
{
func
ToCheckpoint
(
number
uint
64
)
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
)
}
exchain/exchainapi/server.go
View file @
893bf27b
...
...
@@ -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
.
Merkle
Proof
(
item
.
Bytes
())
proof
,
err
:=
tree
.
Proof
(
item
.
Bytes
())
if
err
!=
nil
{
return
nil
,
errors
.
New
(
fmt
.
Sprintf
(
"failed to get proof (%s)"
,
err
.
Error
()))
}
...
...
exchain/exchainclient/client.go
View file @
893bf27b
...
...
@@ -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
(),
}
...
...
exchain/protocol/gen/go/node/v1/node.pb.go
View file @
893bf27b
...
...
@@ -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
,
0x0
c
,
0x52
,
0x05
,
0x70
,
0x72
,
0x6f
,
0x6f
,
0x66
,
0x6f
,
0x6f
,
0x66
,
0x18
,
0x01
,
0x20
,
0x03
,
0x28
,
0x0
9
,
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
,
...
...
exchain/protocol/proto/node/v1/node.proto
View file @
893bf27b
...
...
@@ -141,5 +141,5 @@ message WithdrawalProofRequest {
}
message
WithdrawalProofResponse
{
repeated
bytes
proof
=
1
;
repeated
string
proof
=
1
;
}
exchain/wdt/helper.go
deleted
100644 → 0
View file @
a5ab73f9
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
}
exchain/wdt/wdt.go
View file @
893bf27b
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
()
}
exchain/wrapper/tx.go
View file @
893bf27b
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
)
{
...
...
exchain/wrapper/tx_test.go
0 → 100644
View file @
893bf27b
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"
)
}
}
op-node/node/api.go
View file @
893bf27b
...
...
@@ -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
)
}
...
...
op-node/rollup/interop/managed/system.go
View file @
893bf27b
...
...
@@ -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
)
}
op-proposer/proposer/operator.go
View file @
893bf27b
...
...
@@ -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
,
...
...
op-service/eth/withdrawal_proof.go
View file @
893bf27b
...
...
@@ -7,7 +7,7 @@ import (
type
WithdrawalProof
struct
{
Output
OutputV0
Proof
[]
Bytes32
Proof
[]
string
Value
*
big
.
Int
User
common
.
Address
Coin
[]
byte
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment