Git Product home page Git Product logo

ledger-cosmos-go's Introduction

ledger-cosmos-go

Test Build status

This project is work in progress. Some aspects are subject to change.

Get source

Apart from cloning, be sure you install dep dependency management tool https://github.com/golang/dep

ledger-cosmos-go's People

Contributors

amaury1093 avatar brejski avatar chcmedeiros avatar cwgoes avatar dependabot[bot] avatar ftheirs avatar jleni avatar julienrbrt avatar odeke-em avatar rllola avatar tac0turtle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ledger-cosmos-go's Issues

FindLedgerCosmosUserApp could use a single defer and check on a named return err to close Ledger

Coming here from an audit of dependencies for cosmos-sdk, and per #15, I see this code

func FindLedgerCosmosUserApp() (*LedgerCosmos, error) {
ledgerAPI, err := ledger_go.FindLedger()
if err != nil {
return nil, err
}
app := LedgerCosmos{ledgerAPI, VersionInfo{}}
appVersion, err := app.GetVersion()
if err != nil {
defer ledgerAPI.Close()
if err.Error() == "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported" {
return nil, fmt.Errorf("are you sure the Cosmos app is open?")
}
return nil, err
}
err = app.CheckVersion(*appVersion)
if err != nil {
defer ledgerAPI.Close()
return nil, err
}
return &app, err
}

In there we have many of these patterns

if err != nil {
defer ledgerAPI.Close()
return nil, err
}

Problem

While the code surely works, we could improve it:
a) It invokes defer right before a return, that defeats the purpose of a defer and return
b) It duplicates defer ledgerAPI.Close() all around

Suggestion

We could improve it using a named return and check on an error as Go supports that, and that'll make for less, yet simple, more efficient and less bug prone code

--- before.go	2020-11-24 13:34:07.000000000 -0800
+++ after.go	2020-11-24 13:34:13.000000000 -0800
@@ -1,26 +1,25 @@
-func FindLedgerCosmosUserApp() (*LedgerCosmos, error) {
+func FindLedgerCosmosUserApp() (_ *LedgerCosmos, err error) {
 	ledgerAPI, err := ledger_go.FindLedger()
-
 	if err != nil {
 		return nil, err
 	}
+	defer func() {
+		if err != nil {
+			ledgerAPI.Close()
+		}
+	}()
 
-	app := LedgerCosmos{ledgerAPI, VersionInfo{}}
+	app := &LedgerCosmos{ledgerAPI, VersionInfo{}}
 	appVersion, err := app.GetVersion()
-
 	if err != nil {
-		defer ledgerAPI.Close()
 		if err.Error() == "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported" {
 			return nil, fmt.Errorf("are you sure the Cosmos app is open?")
 		}
 		return nil, err
 	}
 
-	err = app.CheckVersion(*appVersion)
-	if err != nil {
-		defer ledgerAPI.Close()
+	if err := app.CheckVersion(*appVersion); err != nil {
 		return nil, err
 	}
-
-	return &app, err
-}
+	return app, err
+}

Tag new release or use upstream in sdk

Only the master branch here is up to date with zondax/ledger-cosmos-go

If we would like to continue to maintain this fork, then we should tag a new release.

LedgerCosmos.GetBip32bytes has pedantic variables and non-idiomatic usages, unnecessary if err != nil, yet could just plainly invoke early returns

This code has what looks like C-style coding styles and is quite pedantic to declare the byteslice and the error, then switch between the various callers whose return signatures are ([]byte, error) instead of an immediate return

func (ledger *LedgerCosmos) GetBip32bytes(bip32Path []uint32, hardenCount int) ([]byte, error) {
var pathBytes []byte
var err error
switch ledger.version.Major {
case 1:
pathBytes, err = GetBip32bytesv1(bip32Path, 3)
if err != nil {
return nil, err
}
case 2:
pathBytes, err = GetBip32bytesv2(bip32Path, 3)
if err != nil {
return nil, err
}
default:
return nil, errors.New("App version is not supported")
}
return pathBytes, nil
}

Suggested fix

func (ledger *LedgerCosmos) GetBip32bytes(bip32Path []uint32, hardenCount int) ([]byte, error) { 
 	switch major := ledger.version.Major; major { 
 	case 1: 
 		return GetBip32bytesv1(bip32Path, 3) 
 	case 2: 
 		return GetBip32bytesv2(bip32Path, 3) 
 	default: 
 		return nil, fmt.Errorf("Ledger version: %d is not supported", major) 
 	} 
 } 

/cc @elias-orijtech @julienrbrt

unknown option after '#pragma GCC diagnostic' kind [-Wpragmas] #pragma GCC diagnostic ignored "-Wstringop-overflow"

go install -mod=readonly -tags "netgo ledger" -ldflags '-X github.com/cosmos/cosmos-sdk/version.Name=gaia -X github.com/cosmos/cosmos-sdk/version.ServerName=gaiad -X github.com/cosmos/cosmos-sdk/version.ClientName=gaiacli -X github.com/cosmos/cosmos-sdk/version.Version=0.0.0-14-gce9f584 -X github.com/cosmos/cosmos-sdk/version.Commit=ce9f584b36912ff9947f25e309f4b269785b0304 -X "github.com/cosmos/cosmos-sdk/version.BuildTags=netgo,ledger"' ./cmd/gaiacli

github.com/zondax/hid

In file included from E:\goSpace\pkg\mod\github.com\zondax\[email protected]\hid_enabled.go:40:0:
E:\goSpace\pkg\mod\github.com\zondax\[email protected]/hidapi/windows/hid.c: In function 'hid_enumerate':
E:\goSpace\pkg\mod\github.com\zondax\[email protected]/hidapi/windows/hid.c:431:9: warning: unknown option after '#pragma GCC diagnostic' kind [-Wpragmas]
#pragma GCC diagnostic ignored "-Wstringop-overflow"

When I build it on window, it occur above problems.

FindLedgerTendermintValidatorApp on error should unconditionally Close() the Ledger to avoid leaking

The code in

if err != nil {
if err.Error() == "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported" {
return nil, fmt.Errorf("are you sure the Tendermint Validator app is open?")
}
defer ledgerAPI.Close()
return nil, err
}

has 3 problems:
a) If the error's string matches "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported", the ledger won't be closed
b) It misuses defer; no point in invoking defer right before returning, without calling code that could potentially panic
c) If any error is encountered later in the chain, we have a leak, the Ledger won't be .Close()'d yet we shall return nil as per

req := RequiredTendermintValidatorAppVersion()
err = CheckVersion(*appVersion, req)
if err !=nil {
return nil, err
}

Suggestion

This could could use named returns and checking if the err is non-nil then unconditionally invoking (*Ledger).Close() so:

--- before.go	2020-11-24 13:26:39.000000000 -0800
+++ after.go	2020-11-24 13:26:52.000000000 -0800
@@ -1,27 +1,26 @@
-func FindLedgerTendermintValidatorApp() (*LedgerTendermintValidator, error) {
+func FindLedgerTendermintValidatorApp() (_ *LedgerTendermintValidator, err error) {
 	ledgerAPI, err := ledger_go.FindLedger()
-
 	if err != nil {
 		return nil, err
 	}
+	defer func() {
+		if err != nil {
+			ledgerAPI.Close()
+		}
+	}()
 
-	ledgerCosmosValidatorApp := LedgerTendermintValidator{ledgerAPI}
-
+	ledgerCosmosValidatorApp := &LedgerTendermintValidator{ledgerAPI}
 	appVersion, err := ledgerCosmosValidatorApp.GetVersion()
-
 	if err != nil {
 		if err.Error() == "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported" {
-			return nil, fmt.Errorf("are you sure the Tendermint Validator app is open?")
+			err = fmt.Errorf("are you sure the Tendermint Validator app is open?")
 		}
-		defer ledgerAPI.Close()
 		return nil, err
 	}
 
 	req := RequiredTendermintValidatorAppVersion()
-	err = CheckVersion(*appVersion, req)
-	if err !=nil {
+	if err := CheckVersion(*appVersion, req); err != nil {
 		return nil, err
 	}
-
-	return &ledgerCosmosValidatorApp, err
+	return ledgerCosmosValidatorApp, err
 }

LedgerCosmos.CheckVersion error on unsupported version should display the Ledger major version for a better user experience

This switch

switch version.Major {
case 1:
return CheckVersion(ver, VersionInfo{0, 1, 5, 1})
case 2:
return CheckVersion(ver, VersionInfo{0, 2, 1, 0})
default:
return errors.New("App version is not supported")
sends back an error without context but really we should make it return the Ledger's major version so

Suggestion

 default: 
 	return fmt.Errorf("Ledger device version: %d is not supported", version.Major)

/cc @elias-orijtech @julienrbrt

depends on zondax/ledger-go

this repository depends on zondax/ledger-go but we maintain a fork of ledger-go in the cosmos org.

Reckon that if we want to keep maintaining these forks, we should merge:

and use cosmos/ledger-go as the canonical import here to ensure that we don't end up with import spaghetti.

Rework fork relations

Currently this repo shows that was forked from jsnathan/ledger-goclient but that's incorrect. Originally, it was forked from Zondax/ledger-cosmos-go but that repository will be deprecated and all the work and updates will be done here.
Check how to remove forked from legend in order to mark this repo as primary repository for this Go package.

LedgerCosmos.getAddressPubKeySECP256K1 could use string operations as they are for properly checking runes instead of bytes

This code takes a string and then converts it to a byteslice then manually runs some operations on it

hrpBytes := []byte(hrp)
for _, b := range hrpBytes {
if !validHRPByte(b) {
return nil, "", errors.New("all characters in the HRP must be in the [33, 126] range")
}
}

and then

message := append(header, byte(len(hrpBytes)))
message = append(message, hrpBytes...)

From reading the above, it seems that Ledger ONLY deals with ASCII but I've seen them display emoji so seems that they are capable of using Unicode for future purposes like Unicode passphrases or keys, in which I could use runes (Unicode code point with a very high range) that are invalid but when turned into bytes (very narrow range) overflow and pass the error checks

Suggestions

Use string operations

func validHRPRune(r rune) bool {
	// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
	return r >= 33 && r <= 126
}

func (ledger *LedgerCosmos) getAddressPubKeySECP256K1(bip32Path []uint32, hrp string... {
   ...
   if strings.ContainsFunc(hrp, func(r rune) bool { return !validHRPRune(r) }) {
      return nil, "", errors.New("all runes in the HRP must be in the [33, 126] range")
   }

  ...
  message := append(header, byte(len(hrp)))
  message = append(message, hrp...)

Use bytes.ContainsFunc

   if bytes.ContainsFunc(hrpBytes, invalidHRPByte) {
      return nil, "", errors.New("all runes in the HRP must be in the [33, 126] range")
   }

/cc @elias-orijtech @julienrbrt

all: unnecessary use of fmt.Errorf for non-formatting errors, could instead use errors.New

If we grep for fmt.Errorf, we can see all the uses are with static strings

$ git grep 'fmt.Errorf'
common.go:              return nil, fmt.Errorf("maximum bip32 depth = 10")
common.go:              return nil, fmt.Errorf("path should contain 5 elements")
user_app.go:                    return nil, fmt.Errorf("are you sure the Cosmos app is open?")
user_app.go:            return fmt.Errorf("App version is not supported")
user_app.go:            return nil, fmt.Errorf("invalid response")
user_app.go:            return nil, fmt.Errorf("App version is not supported")
user_app.go:            return nil, fmt.Errorf("App version is not supported")
user_app.go:                                    return nil, fmt.Errorf("Not enough tokens were provided")
user_app.go:                                    return nil, fmt.Errorf("Unexpected character in JSON string")
user_app.go:                                    return nil, fmt.Errorf("The JSON string is not a complete.")
user_app.go:                            return nil, fmt.Errorf(errorMsg)
user_app.go:                                    return nil, fmt.Errorf("Not enough tokens were provided")
user_app.go:                                    return nil, fmt.Errorf("Unexpected character in JSON string")
user_app.go:                                    return nil, fmt.Errorf("The JSON string is not a complete.")
user_app.go:                            return nil, fmt.Errorf(errorMsg)
user_app.go:                            return nil, fmt.Errorf(errorMsg)
user_app.go:            return nil, "", fmt.Errorf("hrp len should be <10")
user_app.go:                    return nil, "", fmt.Errorf("all characters in the HRP must be in the [33, 126] range")
user_app.go:            return nil, "", fmt.Errorf("Invalid response")
validator_app.go:                       return nil, fmt.Errorf("are you sure the Tendermint Validator app is open?")
validator_app.go:               return nil, fmt.Errorf("invalid response")
validator_app.go:               return nil, fmt.Errorf("invalid response. Too short")

These should instead use the "errors" package to cut out the dependencies used as well as binary/app size.

Refactor/rework release process

This issue is related to cosmos/gaia#1801

  • align with any changes done in cosmos/ledger-go, move them to zondax/ledger-go
  • deprecate cosmos/ledger-go
  • release a new version of zondax/ledger-go
  • update ledger-cosmos-go to point to zondax/ledger-go
  • release ledger-cosmos-go
  • rework fork relations so cosmos/ledger-cosmos-go is the main repo

version check is too strict for custom projects

The projects, who are using cosmos-sdk, has no option for the their ledger app versioning.
For the terra, it should make its own ledger application because BIP32 CoinType is different. But below Version checking mechanism force us to use specific version in our custom ledger app

func (ledger *LedgerCosmos) CheckVersion(ver VersionInfo) error {

Cannot detect ledger on MacOS Ventura 13.6 or MacOs Sonoma

The observed behavior so far is something in MacOS Venture 13.6 upgrade seems to have created a situation where cosmos binaries built after the upgrade cannot detect a ledger.

Oddly binaries built before the upgrade on the same machine detect the ledger just fine.

I've tried various go versions like 1.19, 1.20 and 1.21 and the problem persists.

Error message will be something like failed to retrieve device: ledger nano S: hidapi: failed to open device

All the binaries I've tried have been using ledger-cosmos-go 0.12.2.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.