Deterministic hashes for ETH lockups
We're missing a nonce for truly unique hashes - for now, two identical transfers will only be executed once.
This commit is contained in:
parent
bc3714fc73
commit
7903402fa6
|
@ -141,6 +141,7 @@ func main() {
|
||||||
zap.String("source", hex.EncodeToString(k.SourceAddress[:])),
|
zap.String("source", hex.EncodeToString(k.SourceAddress[:])),
|
||||||
zap.String("target", hex.EncodeToString(k.TargetAddress[:])),
|
zap.String("target", hex.EncodeToString(k.TargetAddress[:])),
|
||||||
zap.String("amount", k.Amount.String()),
|
zap.String("amount", k.Amount.String()),
|
||||||
|
zap.String("hash", hex.EncodeToString(k.Hash())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
type BridgeWatcher interface {
|
||||||
|
WatchLockups(events chan *ChainLock) error
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
|
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChainLock struct {
|
||||||
|
TxHash common.Hash
|
||||||
|
|
||||||
|
SourceAddress vaa.Address
|
||||||
|
TargetAddress vaa.Address
|
||||||
|
|
||||||
|
SourceChain vaa.ChainID
|
||||||
|
TargetChain vaa.ChainID
|
||||||
|
|
||||||
|
TokenChain vaa.ChainID
|
||||||
|
TokenAddress vaa.Address
|
||||||
|
|
||||||
|
Amount *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns a deterministic hash of the given ChainLock, meant to be used
|
||||||
|
// as primary key for the distributed round of signing.
|
||||||
|
//
|
||||||
|
// TODO: the ETH contract is missing a nonce, so it's not yet deterministic
|
||||||
|
func (l *ChainLock) Hash() []byte {
|
||||||
|
|
||||||
|
// TODO: json.Marshal being deterministic is an implementation detail - what guarantees do we need?
|
||||||
|
// We do not necessarily need stable serialization across releases, but they do need to be unique.
|
||||||
|
|
||||||
|
b, err := json.Marshal(l)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write(b)
|
||||||
|
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
BridgeWatcher interface {
|
|
||||||
WatchLockups(events chan *ChainLock) error
|
|
||||||
}
|
|
||||||
|
|
||||||
ChainLock struct {
|
|
||||||
SourceAddress vaa.Address
|
|
||||||
TargetAddress vaa.Address
|
|
||||||
|
|
||||||
SourceChain vaa.ChainID
|
|
||||||
TargetChain vaa.ChainID
|
|
||||||
|
|
||||||
TokenChain vaa.ChainID
|
|
||||||
TokenAddress vaa.Address
|
|
||||||
|
|
||||||
Amount *big.Int
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -3,17 +3,19 @@ package ethereum
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/certusone/wormhole/bridge/pkg/common"
|
"sync"
|
||||||
"github.com/certusone/wormhole/bridge/pkg/ethereum/abi"
|
"time"
|
||||||
"github.com/certusone/wormhole/bridge/pkg/supervisor"
|
|
||||||
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
eth_common "github.com/ethereum/go-ethereum/common"
|
eth_common "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"sync"
|
|
||||||
"time"
|
"github.com/certusone/wormhole/bridge/pkg/common"
|
||||||
|
"github.com/certusone/wormhole/bridge/pkg/ethereum/abi"
|
||||||
|
"github.com/certusone/wormhole/bridge/pkg/supervisor"
|
||||||
|
"github.com/certusone/wormhole/bridge/pkg/vaa"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -30,8 +32,6 @@ type (
|
||||||
|
|
||||||
pendingLock struct {
|
pendingLock struct {
|
||||||
lock *common.ChainLock
|
lock *common.ChainLock
|
||||||
|
|
||||||
txHash eth_common.Hash
|
|
||||||
height uint64
|
height uint64
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -73,6 +73,7 @@ func (e *EthBridgeWatcher) Run(ctx context.Context) error {
|
||||||
return
|
return
|
||||||
case ev := <-sink:
|
case ev := <-sink:
|
||||||
lock := &common.ChainLock{
|
lock := &common.ChainLock{
|
||||||
|
TxHash: ev.Raw.TxHash,
|
||||||
SourceAddress: ev.Sender,
|
SourceAddress: ev.Sender,
|
||||||
TargetAddress: ev.Recipient,
|
TargetAddress: ev.Recipient,
|
||||||
SourceChain: vaa.ChainIDEthereum,
|
SourceChain: vaa.ChainIDEthereum,
|
||||||
|
@ -87,7 +88,6 @@ func (e *EthBridgeWatcher) Run(ctx context.Context) error {
|
||||||
e.pendingLocksGuard.Lock()
|
e.pendingLocksGuard.Lock()
|
||||||
e.pendingLocks[ev.Raw.TxHash] = &pendingLock{
|
e.pendingLocks[ev.Raw.TxHash] = &pendingLock{
|
||||||
lock: lock,
|
lock: lock,
|
||||||
txHash: ev.Raw.TxHash,
|
|
||||||
height: ev.Raw.BlockNumber,
|
height: ev.Raw.BlockNumber,
|
||||||
}
|
}
|
||||||
e.pendingLocksGuard.Unlock()
|
e.pendingLocksGuard.Unlock()
|
||||||
|
@ -121,7 +121,7 @@ func (e *EthBridgeWatcher) Run(ctx context.Context) error {
|
||||||
|
|
||||||
// Transaction was dropped and never picked up again
|
// Transaction was dropped and never picked up again
|
||||||
if pLock.height+4*e.minConfirmations <= blockNumberU {
|
if pLock.height+4*e.minConfirmations <= blockNumberU {
|
||||||
logger.Debug("lockup timed out", zap.Stringer("tx", pLock.txHash),
|
logger.Debug("lockup timed out", zap.Stringer("tx", pLock.lock.TxHash),
|
||||||
zap.Stringer("number", ev.Number))
|
zap.Stringer("number", ev.Number))
|
||||||
delete(e.pendingLocks, hash)
|
delete(e.pendingLocks, hash)
|
||||||
continue
|
continue
|
||||||
|
@ -129,7 +129,7 @@ func (e *EthBridgeWatcher) Run(ctx context.Context) error {
|
||||||
|
|
||||||
// Transaction is now ready
|
// Transaction is now ready
|
||||||
if pLock.height+e.minConfirmations <= ev.Number.Uint64() {
|
if pLock.height+e.minConfirmations <= ev.Number.Uint64() {
|
||||||
logger.Debug("lockup confirmed", zap.Stringer("tx", pLock.txHash),
|
logger.Debug("lockup confirmed", zap.Stringer("tx", pLock.lock.TxHash),
|
||||||
zap.Stringer("number", ev.Number))
|
zap.Stringer("number", ev.Number))
|
||||||
delete(e.pendingLocks, hash)
|
delete(e.pendingLocks, hash)
|
||||||
e.evChan <- pLock.lock
|
e.evChan <- pLock.lock
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: Apache 2
|
// SPDX-License-Identifier: Apache 2
|
||||||
|
|
||||||
// TODO(hendrik): reentrancy protection for all methods
|
// TODO(hendrik): reentrancy protection for all methods
|
||||||
|
// TODO(hendrik): switch-over feature
|
||||||
|
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
@ -197,6 +198,7 @@ contract Wormhole {
|
||||||
isWrappedAsset[asset] = true;
|
isWrappedAsset[asset] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(hendrik): nonce
|
||||||
function lockAssets(
|
function lockAssets(
|
||||||
address asset,
|
address asset,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
|
|
Loading…
Reference in New Issue