diff --git a/cmd/replay.go b/cmd/replay.go index bb5a29e..ee7d2a4 100644 --- a/cmd/replay.go +++ b/cmd/replay.go @@ -3,6 +3,7 @@ package cmd import ( "encoding/json" "errors" + "io/ioutil" "log" "github.com/solipsis/go-keepkey/pkg/keepkey" @@ -39,6 +40,24 @@ var replayCmd = &cobra.Command{ rec := keepkey.Record{Messages: []keepkey.LogMsg{lm}} keepkey.Replay(kk, rec) + return + } + + if logFile != "" { + + buf, err := ioutil.ReadFile(logFile) + if err != nil { + log.Fatal(err) + } + + r := keepkey.Record{} + json.Unmarshal(buf, &r) + + if len(r.Messages) == 0 { + log.Fatal("Unable to parse messages, Validate that they are in valid JSON format") + } + + keepkey.Replay(kk, r) } }, diff --git a/pkg/keepkey/logreplay.go b/pkg/keepkey/logreplay.go index 0cd1159..561aba8 100644 --- a/pkg/keepkey/logreplay.go +++ b/pkg/keepkey/logreplay.go @@ -13,22 +13,19 @@ import ( "github.com/golang/protobuf/proto" ) -var test = `{"message_type":"EthereumSignTx","date":1550076468947,"message_enum":58,"message":{"addressNList":[2147483692,2147483708,2147483648,0,0],"nonce":"","gasPrice":"AVIabww=","gasLimit":"1PA=","to":"DYd19khDBnmnCemNKwy2JQ0oh+8=","value":"","dataInitialChunk":"qQWcuwAAAAAAAAAAAAAAAMU9lQ1zMBVO4yP8GfveHNZ5z763AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB29k+rNWL/AA=","dataLength":68,"toAddressNList":[],"addressType":3,"exchangeType":{"signedExchangeResponse":{"signature":"INyh1gt2zHXevVDuGDB8nYW2EH7mNDH7Ef0Qt1QD51ECXgkvyok+64dvHJSkVavoxwkjNH4neS+qfbbqKXTHuoQ=","responsev2":{"depositAddress":{"coinType":"bat","address":"0xc53d950d7330154ee323fc19fbde1cd679cfbeb7"},"depositAmount":"AdvZPqzVi/wA","expiration":1550077155180,"quotedRate":"BHUM45OVQAA=","withdrawalAddress":{"coinType":"ant","address":"0xbd5ffd40d55e9aee88a19f2340de40cadc60fc18"},"withdrawalAmount":"fuuj39He1AA=","returnAddress":{"coinType":"bat","address":"0xbd5ffd40d55e9aee88a19f2340de40cadc60fc18"},"apiKey":"atWDG3eEhLuEnaRRgKw1BHhI5crA+mZkVPT/eLjHOZ/qaozix+5ih7zXjbZhDKP1ONaz6QyoDI5jaLYCFEWVCw==","minerFee":"GelFjsQhwAA=","orderId":"Zw+8Oo1rRSykADwvwIrZAg=="}},"withdrawalCoinName":"ANT","withdrawalAddressNList":[2147483692,2147483708,2147483648,0,0],"returnAddressNList":[2147483692,2147483708,2147483648,0,0]},"chainId":1,"tokenValue":"","tokenTo":""},"from_device":false,"interface":"StandardWebUSB"}` - // Record blalahoeushtouthoesaut type Record struct { Messages []LogMsg `json:"messages"` } type LogMsg struct { - Type string `json:"message_type"` - Msg *json.RawMessage `json:"message"` + Type string `json:"message_type"` + FromDevice bool `json:"from_device"` + Msg *json.RawMessage `json:"message"` } -//func Replay(kk *Keepkey, l []byte) { -//replay( -//} - +// Replay plays a list of messages back to the device +// func Replay(kk *Keepkey, r Record) { for _, msg := range r.Messages { @@ -37,46 +34,13 @@ func Replay(kk *Keepkey, r Record) { continue } - // Attempt to reflectively instantiate protobuf - proto, err := reflectJSON(msg.Type, []byte(*msg.Msg)) - if err != nil { - log.Fatal(err) - } - - err = kk.SendRaw(proto) - if err != nil { - log.Fatal(err) - } - fmt.Println("sent") - - suc := new(kkProto.Success) - fmt.Println("Receiving") - err = kk.ReceiveRaw(suc) - if err != nil { - log.Fatal(err) - } - - } - -} - -// Replay blah -// TODO: make bidirectional -/* -func Replay(kk *Keepkey, r io.Reader) { - - fmt.Println("Replay") - sc := bufio.NewScanner(r) - for sc.Scan() { - - lm := &logMsg{} - json.Unmarshal([]byte(sc.Text()), &lm) - if lm.Type == "" { + // TODO: implement validating received responses against what was previously received + if msg.FromDevice { continue } // Attempt to reflectively instantiate protobuf - proto, err := reflectJSON(lm.Type, []byte(*lm.Msg)) + proto, err := reflectJSON(msg.Type, []byte(*msg.Msg)) if err != nil { log.Fatal(err) } @@ -85,23 +49,15 @@ func Replay(kk *Keepkey, r io.Reader) { if err != nil { log.Fatal(err) } - fmt.Println("sent") - suc := new(kkProto.Success) - fmt.Println("Receiving") - err = kk.ReceiveRaw(suc) + _, err = kk.ReceiveRaw() if err != nil { log.Fatal(err) } } - // We got an error other than EOF - if sc.Err() != nil { - log.Fatal(sc.Err()) - } } -*/ // reflectJSON attempts to unmarshal a given json string into // a reflected proto.Message using the typeName and typeRegistry @@ -111,7 +67,6 @@ func reflectJSON(typeName string, body []byte) (proto.Message, error) { return &kkProto.Ping{}, fmt.Errorf("No type with name %s found in TypeRegistry", typeName) } - fmt.Println("Fetched Type: ", t) p := reflect.New(t).Interface() pr, ok := p.(proto.Message) @@ -125,7 +80,6 @@ func reflectJSON(typeName string, body []byte) (proto.Message, error) { if err != nil { return &kkProto.Ping{}, fmt.Errorf("Unable to unmarshal parsed json:\n%s into type %s, With error: %s", body, typeName, err.Error()) } - fmt.Printf("PROTO: %T %+v\n", pr, pr) return pr, nil } diff --git a/pkg/keepkey/raw.go b/pkg/keepkey/raw.go index 0c4f210..64e3df3 100644 --- a/pkg/keepkey/raw.go +++ b/pkg/keepkey/raw.go @@ -3,6 +3,8 @@ package keepkey import ( "encoding/binary" "fmt" + "reflect" + "strings" "github.com/golang/protobuf/proto" "github.com/solipsis/go-keepkey/pkg/kkProto" @@ -47,10 +49,9 @@ func (kk *Keepkey) SendRaw(req proto.Message) error { return nil } -// ReceiveRaw receives a message from the device but does not take any additional actions based on the recieved message -// This is useful for recreating a previous exchange with the device -// TODO; make consistent with how debug/normal messages are handled in keepkeyExchange -func (kk *Keepkey) ReceiveRaw(result proto.Message) error { +// ReceiveRaw receives a message from the device but does not take any additional actions based on +// the recieved message. This is useful for recreating a previous exchange with the device +func (kk *Keepkey) ReceiveRaw() (proto.Message, error) { response := <-kk.deviceQueue kind := response.kind @@ -62,19 +63,29 @@ func (kk *Keepkey) ReceiveRaw(result proto.Message) error { // keepkey returned a failure, extract and return the message failure := new(kkProto.Failure) if err := proto.Unmarshal(reply, failure); err != nil { - return err + return &kkProto.Failure{}, err } - return fmt.Errorf("keepkey: %s", failure.GetMessage()) + return failure, fmt.Errorf("keepkey: %s", failure.GetMessage()) } - fmt.Println("kind", kind) + // reflectively instiate the appropriate type + typeName := strings.TrimPrefix(kkProto.Name(kind), "MessageType_") + t, ok := kkProto.TypeRegistry(typeName) + if !ok { + return &kkProto.Failure{}, fmt.Errorf("No type with name %s found in TypeRegistry", typeName) + } + p := reflect.New(t).Interface() + pr, ok := p.(proto.Message) + if !ok { + return &kkProto.Failure{}, fmt.Errorf("Reflected type does not implement proto.Message") + } // If the reply we got can be marshaled into our expected result marshal it - if kkProto.Type(result) == kind { - err := proto.Unmarshal(reply, result) - kk.log("Recieved message from device:\n%s:\n%s", kkProto.Name(kkProto.Type(result)), pretty(result)) - return err + if kkProto.Type(pr) == kind { + err := proto.Unmarshal(reply, pr) + kk.log("Recieved message from device:\n%s:\n%s", kkProto.Name(kkProto.Type(pr)), pretty(pr)) + return &kkProto.Failure{}, err } - return fmt.Errorf("keepkey: expected reply type %s, got %s", kkProto.Name(kkProto.Type(result)), kkProto.Name(kind)) + return &kkProto.Failure{}, fmt.Errorf("keepkey: expected reply type %s, got %s", kkProto.Name(kkProto.Type(pr)), kkProto.Name(kind)) }