cmd/signer: implement tests

This commit is contained in:
Martin Holst Swende 2017-12-03 14:33:09 +01:00
parent 6f1054a01a
commit eb61c52626
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
3 changed files with 325 additions and 1 deletions

294
cmd/signer/api_test.go Normal file
View file

@ -0,0 +1,294 @@
package main
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"io/ioutil"
"math/big"
"os"
"path/filepath"
"testing"
"time"
)
//Used for testing
type HeadlessUI struct {
controller chan string
}
func (ui *HeadlessUI) ApproveTx(request *SignTxRequest, metadata Metadata, ch chan SignTxResponse) {
if "Y" == <-ui.controller {
ch <- SignTxResponse{request.transaction.Hash(), true, <-ui.controller}
} else {
ch <- SignTxResponse{request.transaction.Hash(), false, ""}
}
}
func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest, metadata Metadata, ch chan SignDataResponse) {
switch <-ui.controller {
case "Y":
ch <- SignDataResponse{true, <-ui.controller}
default:
ch <- SignDataResponse{false, ""}
}
}
func (ui *HeadlessUI) ApproveExport(request *ExportRequest, metadata Metadata, ch chan ExportResponse) {
switch <-ui.controller {
case "Y":
ch <- ExportResponse{true}
default:
ch <- ExportResponse{false}
}
}
func (ui *HeadlessUI) ApproveImport(request *ImportRequest, metadata Metadata, ch chan ImportResponse) {
switch <-ui.controller {
case "Y":
ch <- ImportResponse{true, <-ui.controller, <-ui.controller}
default:
ch <- ImportResponse{false, "", ""}
}
}
func (ui *HeadlessUI) ApproveListing(request *ListRequest, metadata Metadata, ch chan ListResponse) {
switch <-ui.controller {
case "A":
ch <- ListResponse{request.accounts}
case "1":
l := make([]Account, 1)
l[0] = request.accounts[1]
ch <- ListResponse{l}
default:
ch <- ListResponse{nil}
}
}
func (ui *HeadlessUI) ApproveNewAccount(requst *NewAccountRequest, metadata Metadata, ch chan NewAccountResponse) {
switch <-ui.controller {
case "Y":
ch <- NewAccountResponse{true, <-ui.controller}
default:
ch <- NewAccountResponse{false, ""}
}
}
func (ui *HeadlessUI) ShowError(message string) {
//stdout is used by communication
fmt.Fprint(os.Stderr, message)
}
func (ui *HeadlessUI) ShowInfo(message string) {
//stdout is used by communication
fmt.Fprint(os.Stderr, message)
}
func tmpDirName(t *testing.T) string {
d, err := ioutil.TempDir("", "eth-keystore-test")
if err != nil {
t.Fatal(err)
}
d, err = filepath.EvalSymlinks(d)
if err != nil {
t.Fatal(err)
}
return d
}
func setup(t *testing.T) (*SignerAPI, chan string) {
controller := make(chan string, 10)
db, err := NewAbiDBFromFile(fmt.Sprintf("./4byte.json"))
if err != nil {
utils.Fatalf(err.Error())
}
var (
ui = &HeadlessUI{controller}
api = NewSignerAPI(
1,
tmpDirName(t),
true,
ui,
db,
true)
)
return api, controller
}
func createAccount(control chan string, api *SignerAPI, t *testing.T) {
control <- "Y"
control <- "apassword"
_, err := api.New(context.Background())
if err != nil {
t.Fatal(err)
}
// Some time to allow changes to propagate
time.Sleep(250 * time.Millisecond)
}
func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
control <- "N"
acc, err := api.New(context.Background())
if err != ErrRequestDenied {
t.Fatal(err)
}
if acc.Address != (common.Address{}) {
t.Fatal("Empty address should be returned")
}
}
func list(control chan string, api *SignerAPI, t *testing.T) []Account {
control <- "A"
list, err := api.List(context.Background())
if err != nil {
t.Fatal(err)
}
return list
}
func TestNewAcc(t *testing.T) {
api, control := setup(t)
verifyNum := func(num int) {
if list := list(control, api, t); len(list) != num {
t.Errorf("Expected %d accounts, got %d", num, len(list))
}
}
// Testing create and create-deny
createAccount(control, api, t)
createAccount(control, api, t)
failCreateAccount(control, api, t)
failCreateAccount(control, api, t)
createAccount(control, api, t)
failCreateAccount(control, api, t)
createAccount(control, api, t)
failCreateAccount(control, api, t)
verifyNum(4)
// Testing listing:
// Listing one account
control <- "1"
list, err := api.List(context.Background())
if err != nil {
t.Fatal(err)
}
if len(list) != 1 {
t.Fatalf("List should only show one account")
}
// Listing denied
control <- "Nope"
list, err = api.List(context.Background())
if len(list) != 0 {
t.Fatalf("List should be empty")
}
if err != ErrRequestDenied {
t.Fatal("Expected deny")
}
}
func TestSignData(t *testing.T) {
api, control := setup(t)
//Create two accounts
createAccount(control, api, t)
createAccount(control, api, t)
control <- "1"
list, err := api.List(context.Background())
if err != nil {
t.Fatal(err)
}
a := list[0].Address
control <- "Y"
control <- "wrongpassword"
h, err := api.Sign(context.Background(), a, []byte("EHLO world"))
if h != nil {
t.Errorf("Expected nil-data, got %h", h)
}
if err != keystore.ErrDecrypt {
t.Errorf("Expected ErrLocked! %v", err)
}
control <- "No way"
h, err = api.Sign(context.Background(), a, []byte("EHLO world"))
if h != nil {
t.Errorf("Expected nil-data, got %h", h)
}
if err != ErrRequestDenied {
t.Errorf("Expected ErrRequestDenied! %v", err)
}
control <- "Y"
control <- "apassword"
h, err = api.Sign(context.Background(), a, []byte("EHLO world"))
if err != nil {
t.Fatal(err)
}
if h == nil || len(h) != 65 {
t.Errorf("Expected 65 byte signature (got %d bytes)", len(h))
}
}
func mkTestTx() TransactionArg {
to := common.HexToAddress("0x1337")
gas := (*hexutil.Big)(big.NewInt(21000))
gasPrice := (*hexutil.Big)(big.NewInt(2000000000))
value := (*hexutil.Big)(big.NewInt(1e18))
nonce := (hexutil.Uint64)(0)
tx := TransactionArg{
&to,
gas,
gasPrice,
value,
common.Hex2Bytes("01020304050607080a"),
&nonce}
return tx
}
func TestSignTx(t *testing.T) {
api, control := setup(t)
createAccount(control, api, t)
control <- "A"
list, err := api.List(context.Background())
if err != nil {
t.Fatal(err)
}
a := list[0].Address
methodSig := "test(uint)"
tx := mkTestTx()
control <- "Y"
control <- "wrongpassword"
h, err := api.SignTransaction(context.Background(), a, tx, &methodSig)
if h != nil {
t.Errorf("Expected nil-data, got %h", h)
}
if err != keystore.ErrDecrypt {
t.Errorf("Expected ErrLocked! %v", err)
}
control <- "No way"
h, err = api.SignTransaction(context.Background(), a, tx, &methodSig)
if h != nil {
t.Errorf("Expected nil-data, got %h", h)
}
if err != ErrRequestDenied {
t.Errorf("Expected ErrRequestDenied! %v", err)
}
control <- "Y"
control <- "apassword"
h, err = api.SignTransaction(context.Background(), a, tx, &methodSig)
if err != nil {
t.Fatal(err)
}
if h == nil || len(h) != 118 {
t.Errorf("Expected 181 byte rlp-data (got %d bytes)", len(h))
}
}

View file

@ -80,6 +80,7 @@ func main() {
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
db, err := NewAbiDBFromFile(c.String("4bytedb")) db, err := NewAbiDBFromFile(c.String("4bytedb"))
if err != nil { if err != nil {
utils.Fatalf(err.Error()) utils.Fatalf(err.Error())
} }
@ -91,7 +92,8 @@ func main() {
c.Int64(utils.NetworkIdFlag.Name), c.Int64(utils.NetworkIdFlag.Name),
c.String("keystore"), c.String("keystore"),
c.Bool(utils.NoUSBFlag.Name), c.Bool(utils.NoUSBFlag.Name),
NewCommandlineUI(), db) NewCommandlineUI(), db,
c.Bool(utils.LightKDFFlag.Name))
listener net.Listener listener net.Listener
//err error //err error
) )

View file

@ -17,17 +17,37 @@
package main package main
import ( import (
"encoding/json"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"strings"
) )
type Accounts []Account
func (as Accounts) String() string{
var output []string
for _,a := range as{
output = append(output, a.String())
}
return strings.Join(output, "\n")
}
type Account struct { type Account struct {
Typ string `json:"type"` Typ string `json:"type"`
URL accounts.URL `json:"url"` URL accounts.URL `json:"url"`
Address common.Address `json:"address"` Address common.Address `json:"address"`
} }
func (a Account) String() string {
s, err := json.Marshal(a)
if err == nil {
return string(s)
}
return err.Error()
}
// TransactionArg represents a transaction for the signer. // TransactionArg represents a transaction for the signer.
type TransactionArg struct { type TransactionArg struct {
To *common.Address `json:"to"` To *common.Address `json:"to"`
@ -37,3 +57,11 @@ type TransactionArg struct {
Data hexutil.Bytes `json:"data"` Data hexutil.Bytes `json:"data"`
Nonce *hexutil.Uint64 `json:"nonce"` Nonce *hexutil.Uint64 `json:"nonce"`
} }
func (t TransactionArg) String() string {
s, err := json.Marshal(t)
if err == nil {
return string(s)
}
return err.Error()
}