Skip to content

Commit

Permalink
eth/client: SignMessage
Browse files Browse the repository at this point in the history
  • Loading branch information
martonp committed Oct 3, 2021
1 parent 8c334a7 commit f3641b4
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 21 deletions.
21 changes: 17 additions & 4 deletions client/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ type ethFetcher interface {
refund(opts *bind.TransactOpts, netID int64, secretHash [32]byte) (*types.Transaction, error)
swap(ctx context.Context, from *accounts.Account, secretHash [32]byte) (*swap.ETHSwapSwap, error)
unlock(ctx context.Context, pw string, acct *accounts.Account) error
signData(addr common.Address, data []byte) ([]byte, error)
}

// Check that ExchangeWallet satisfies the asset.Wallet interface.
Expand Down Expand Up @@ -561,10 +562,22 @@ func (*ExchangeWallet) Redeem(form *asset.RedeemForm) ([]dex.Bytes, asset.Coin,
}

// SignMessage signs the message with the private key associated with the
// specified funding Coin. A slice of pubkeys required to spend the Coin and a
// signature for each pubkey are returned.
func (*ExchangeWallet) SignMessage(coin asset.Coin, msg dex.Bytes) (pubkeys, sigs []dex.Bytes, err error) {
return nil, nil, asset.ErrNotImplemented
// specified funding Coin. Only a coin that came from the address this wallet
// is initialized with can be used to sign.
func (e *ExchangeWallet) SignMessage(coin asset.Coin, msg dex.Bytes) (pubkeys, sigs []dex.Bytes, err error) {
ethCoin, err := ethCoinFromID(coin.ID())
if err != nil {
return nil, nil, err
}

if !bytes.Equal(ethCoin.address.Bytes(), e.acct.Address.Bytes()) {
return nil, nil, fmt.Errorf("SignMessage: coin address: %v != wallet address: %v",
ethCoin.address, e.acct.Address)
}

data, err := e.node.signData(e.acct.Address, msg)

return []dex.Bytes{e.pubKey}, []dex.Bytes{data}, nil
}

// AuditContract retrieves information about a swap contract on the
Expand Down
49 changes: 49 additions & 0 deletions client/asset/eth/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ func (n *testNode) peers(ctx context.Context) ([]*p2p.PeerInfo, error) {
func (n *testNode) estimateGas(ctx context.Context, callMsg ethereum.CallMsg) (uint64, error) {
return n.initGas, n.initGasErr
}
func (n *testNode) signData(addr common.Address, data []byte) ([]byte, error) {
return nil, nil
}

func TestLoadConfig(t *testing.T) {
tests := []struct {
Expand Down Expand Up @@ -737,3 +740,49 @@ func TestMaxOrder(t *testing.T) {
}
}
}

func TestSignMessage(t *testing.T) {
node := &testNode{}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
address := "B6De8BB5ed28E6bE6d671975cad20C03931bE981"
account := accounts.Account{
Address: common.HexToAddress(address),
}
eth := &ExchangeWallet{
node: node,
ctx: ctx,
log: tLogger,
acct: &account,
}

msg := []byte("msg")

// Test error from bad coin
var badCoin badEthCoin
_, _, err := eth.SignMessage(&badCoin, msg)
if err == nil {
t.Fatalf("expected error for signing message with bad coin")
}

// Test error from coin with different account than wallet
differentAddress := common.HexToAddress("2b84C791b79Ee37De042AD2ffF1A253c3ce9bc27")
coin := EthCoin{
address: differentAddress,
value: 100,
}
_, _, err = eth.SignMessage(&coin, msg)
if err == nil {
t.Fatalf("expected error for signing message with different address than wallet")
}

// Test no error
coin = EthCoin{
address: account.Address,
value: 100,
}
_, _, err = eth.SignMessage(&coin, msg)
if err != nil {
t.Fatalf("unexpected error signing message: %v", err)
}
}
14 changes: 14 additions & 0 deletions client/asset/eth/rpcclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,20 @@ func (c *rpcclient) wallet(acct accounts.Account) (accounts.Wallet, error) {
return wallet, nil
}

// signData uses the private key of the address to sign a piece of data.
// The address must have been imported and unlocked to use this function.
func (c *rpcclient) signData(addr common.Address, data []byte) ([]byte, error) {
account := accounts.Account{Address: addr}
wallet, err := c.wallet(account)
if err != nil {
return nil, err
}

// The mime type is not used in the keystore wallet in geth. It treats
// any data like plain text.
return wallet.SignData(account, accounts.MimetypeTextPlain, data)
}

func (c *rpcclient) addSignerToOpts(txOpts *bind.TransactOpts, netID int64) error {
wallet, err := c.wallet(accounts.Account{Address: txOpts.From})
if err != nil {
Expand Down
58 changes: 41 additions & 17 deletions client/asset/eth/rpcclient_harness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
)

const (
Expand All @@ -60,21 +62,23 @@ const (
)

var (
gasPrice = big.NewInt(82e9)
homeDir = os.Getenv("HOME")
contractAddrFile = filepath.Join(homeDir, "dextest", "eth", "contract_addr.txt")
testDir = filepath.Join(homeDir, "dextest", "eth", "client_rpc_tests")
alphaNodeDir = filepath.Join(homeDir, "dextest", "eth", "alpha", "node")
ethClient = new(rpcclient)
ctx context.Context
tLogger = dex.StdOutLogger("ETHTEST", dex.LevelTrace)
simnetAddr = common.HexToAddress("2b84C791b79Ee37De042AD2ffF1A253c3ce9bc27")
simnetAcct = &accounts.Account{Address: simnetAddr}
participantAddr = common.HexToAddress("345853e21b1d475582E71cC269124eD5e2dD3422")
participantAcct = &accounts.Account{Address: participantAddr}
contractAddr common.Address
simnetID = int64(42)
newTxOpts = func(ctx context.Context, from *common.Address, value *big.Int) *bind.TransactOpts {
gasPrice = big.NewInt(82e9)
homeDir = os.Getenv("HOME")
contractAddrFile = filepath.Join(homeDir, "dextest", "eth", "contract_addr.txt")
testDir = filepath.Join(homeDir, "dextest", "eth", "client_rpc_tests")
alphaNodeDir = filepath.Join(homeDir, "dextest", "eth", "alpha", "node")
ethClient = new(rpcclient)
ctx context.Context
tLogger = dex.StdOutLogger("ETHTEST", dex.LevelTrace)
simnetPrivKey = "9447129055a25c8496fca9e5ee1b9463e47e6043ff0c288d07169e8284860e34"
simnetAddr = common.HexToAddress("2b84C791b79Ee37De042AD2ffF1A253c3ce9bc27")
simnetAcct = &accounts.Account{Address: simnetAddr}
participantPrivKey = "0695b9347a4dc096ae5c6f1935380ceba550c70b112f1323c211bade4d11651a"
participantAddr = common.HexToAddress("345853e21b1d475582E71cC269124eD5e2dD3422")
participantAcct = &accounts.Account{Address: participantAddr}
contractAddr common.Address
simnetID = int64(42)
newTxOpts = func(ctx context.Context, from *common.Address, value *big.Int) *bind.TransactOpts {
return &bind.TransactOpts{
GasPrice: gasPrice,
GasLimit: 1e6,
Expand Down Expand Up @@ -249,7 +253,7 @@ func TestImportAccounts(t *testing.T) {
t.Skip()
}
// The address of this will be 2b84C791b79Ee37De042AD2ffF1A253c3ce9bc27.
privB, err := hex.DecodeString("9447129055a25c8496fca9e5ee1b9463e47e6043ff0c288d07169e8284860e34")
privB, err := hex.DecodeString(simnetPrivKey)
if err != nil {
t.Fatal(err)
}
Expand All @@ -259,7 +263,7 @@ func TestImportAccounts(t *testing.T) {
}
spew.Dump(acct)
// The address of this will be 345853e21b1d475582E71cC269124eD5e2dD3422.
privB, err = hex.DecodeString("0695b9347a4dc096ae5c6f1935380ceba550c70b112f1323c211bade4d11651a")
privB, err = hex.DecodeString(participantPrivKey)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -949,3 +953,23 @@ func TestGetCodeAt(t *testing.T) {
t.Fatal("Contract on chain does not match one in code")
}
}

func TestSignMessage(t *testing.T) {
msg := []byte("test message")
err := ethClient.unlock(ctx, pw, simnetAcct)
if err != nil {
t.Fatalf("error unlocking account: %v", err)
}
signature, err := ethClient.signData(simnetAddr, msg)
if err != nil {
t.Fatalf("error signing text: %v", err)
}

privKey, _ := hex.DecodeString(simnetPrivKey)
ecdsa, _ := crypto.ToECDSA(privKey)
pubKey := crypto.FromECDSAPub(&ecdsa.PublicKey)

if !secp256k1.VerifySignature(pubKey, crypto.Keccak256(msg), signature[:len(signature)-1]) {
t.Fatalf("failed to verify signature")
}
}

0 comments on commit f3641b4

Please sign in to comment.