From 3ac8e98d80ea8eaab2ab3bd6235adfcc6e3bab14 Mon Sep 17 00:00:00 2001 From: HildisviniOttar Date: Thu, 15 Jul 2021 12:47:10 +0930 Subject: [PATCH] THORChain changes Support THORChain Ledger app v2.0.0 --- .circleci/config.yml | 18 ----- appveyor.yml | 2 +- common.go | 10 +-- common_test.go | 2 +- go.mod | 2 +- user_app.go | 106 ++++++------------------- user_app_test.go | 20 ++--- validator_app.go | 176 ------------------------------------------ validator_app_test.go | 68 ---------------- 9 files changed, 43 insertions(+), 361 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100644 validator_app.go delete mode 100644 validator_app_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 53905f2..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: 2 - -jobs: - build: - docker: - - image: golang:1.12 - environment: - GO111MODULE: "on" - working_directory: /ledger-cosmos-go - steps: - - checkout - - run: go build - - run: go test common.go -workflows: - version: 2 - build_all: - jobs: - - build diff --git a/appveyor.yml b/appveyor.yml index 1cc50fb..8743c6e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,6 +20,6 @@ install: - go env build_script: - - go build -x ./common.go ./user_app.go ./validator_app.go + - go build -x ./common.go ./user_app.go - go test ./common diff --git a/common.go b/common.go index 4b0b55b..f2b8de5 100644 --- a/common.go +++ b/common.go @@ -14,7 +14,7 @@ * limitations under the License. ********************************************************************************/ -package ledger_cosmos_go +package ledger_thorchain_go import ( "encoding/binary" @@ -53,20 +53,20 @@ func NewVersionRequiredError(req VersionInfo, ver VersionInfo) error { // CheckVersion compares the current version with the required version func CheckVersion(ver VersionInfo, req VersionInfo) error { if ver.Major != req.Major { - if (ver.Major > req.Major){ + if ver.Major > req.Major { return nil } return NewVersionRequiredError(req, ver) } if ver.Minor != req.Minor { - if (ver.Minor > req.Minor) { + if ver.Minor > req.Minor { return nil } return NewVersionRequiredError(req, ver) } - if (ver.Patch >= req.Patch){ + if ver.Patch >= req.Patch { return nil } return NewVersionRequiredError(req, ver) @@ -95,7 +95,7 @@ func GetBip32bytesv2(bip44Path []uint32, hardenCount int) ([]byte, error) { return nil, fmt.Errorf("path should contain 5 elements") } for index, element := range bip44Path { - pos := index*4 + pos := index * 4 value := element if index < hardenCount { value = 0x80000000 | element diff --git a/common_test.go b/common_test.go index 84f30d4..7615160 100644 --- a/common_test.go +++ b/common_test.go @@ -14,7 +14,7 @@ * limitations under the License. ********************************************************************************/ -package ledger_cosmos_go +package ledger_thorchain_go import ( "fmt" diff --git a/go.mod b/go.mod index 987c76e..90a1fe8 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/cosmos/ledger-cosmos-go +module github.com/thorchain/ledger-thorchain-go go 1.12 diff --git a/user_app.go b/user_app.go index e21a73b..e8b6f98 100644 --- a/user_app.go +++ b/user_app.go @@ -14,13 +14,14 @@ * limitations under the License. ********************************************************************************/ -package ledger_cosmos_go +/* THORChain uses same CLA (0x55) as Cosmos app, so okay to use cosmos/ledger-go for transport */ +package ledger_thorchain_go import ( "fmt" "math" - "github.com/cosmos/ledger-go" + ledger_go "github.com/cosmos/ledger-go" ) const ( @@ -33,27 +34,27 @@ const ( userMessageChunkSize = 250 ) -// LedgerCosmos represents a connection to the Cosmos app in a Ledger Nano S device -type LedgerCosmos struct { +// LedgerTHORChain represents a connection to the THORChain app in a Ledger Nano S/X device +type LedgerTHORChain struct { api *ledger_go.Ledger version VersionInfo } -// FindLedgerCosmosUserApp finds a Cosmos user app running in a ledger device -func FindLedgerCosmosUserApp() (*LedgerCosmos, error) { +// FindLedgerTHORChainUserApp finds a THORChain user app running in a ledger device +func FindLedgerTHORChainUserApp() (*LedgerTHORChain, error) { ledgerAPI, err := ledger_go.FindLedger() if err != nil { return nil, err } - app := LedgerCosmos{ledgerAPI, VersionInfo{}} + app := LedgerTHORChain{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, fmt.Errorf("are you sure the THORChain app is open?") } return nil, err } @@ -67,30 +68,28 @@ func FindLedgerCosmosUserApp() (*LedgerCosmos, error) { return &app, err } -// Close closes a connection with the Cosmos user app -func (ledger *LedgerCosmos) Close() error { +// Close closes a connection with the THORChain user app +func (ledger *LedgerTHORChain) Close() error { return ledger.api.Close() } // VersionIsSupported returns true if the App version is supported by this library -func (ledger *LedgerCosmos) CheckVersion(ver VersionInfo) error { +func (ledger *LedgerTHORChain) CheckVersion(ver VersionInfo) error { version, err := ledger.GetVersion() if err != nil { return err } switch version.Major { - case 1: - return CheckVersion(ver, VersionInfo{0, 1, 5, 1}) case 2: - return CheckVersion(ver, VersionInfo{0, 2, 1, 0}) + return CheckVersion(ver, VersionInfo{0, 2, 0, 0}) default: return fmt.Errorf("App version is not supported") } } -// GetVersion returns the current version of the Cosmos user app -func (ledger *LedgerCosmos) GetVersion() (*VersionInfo, error) { +// GetVersion returns the current version of the THORChain user app +func (ledger *LedgerTHORChain) GetVersion() (*VersionInfo, error) { message := []byte{userCLA, userINSGetVersion, 0, 0, 0} response, err := ledger.api.Exchange(message) @@ -103,7 +102,7 @@ func (ledger *LedgerCosmos) GetVersion() (*VersionInfo, error) { } ledger.version = VersionInfo{ - AppMode: response[0], + AppMode: response[0], //Debug is 0xFF, otherwise 0x00 Major: response[1], Minor: response[2], Patch: response[3], @@ -112,12 +111,10 @@ func (ledger *LedgerCosmos) GetVersion() (*VersionInfo, error) { return &ledger.version, nil } -// SignSECP256K1 signs a transaction using Cosmos user app +// SignSECP256K1 signs a transaction using THORChain user app // this command requires user confirmation in the device -func (ledger *LedgerCosmos) SignSECP256K1(bip32Path []uint32, transaction []byte) ([]byte, error) { +func (ledger *LedgerTHORChain) SignSECP256K1(bip32Path []uint32, transaction []byte) ([]byte, error) { switch ledger.version.Major { - case 1: - return ledger.signv1(bip32Path, transaction) case 2: return ledger.signv2(bip32Path, transaction) default: @@ -127,8 +124,8 @@ func (ledger *LedgerCosmos) SignSECP256K1(bip32Path []uint32, transaction []byte // GetPublicKeySECP256K1 retrieves the public key for the corresponding bip32 derivation path (compressed) // this command DOES NOT require user confirmation in the device -func (ledger *LedgerCosmos) GetPublicKeySECP256K1(bip32Path []uint32) ([]byte, error) { - pubkey, _, err := ledger.getAddressPubKeySECP256K1(bip32Path, "cosmos", false) +func (ledger *LedgerTHORChain) GetPublicKeySECP256K1(bip32Path []uint32) ([]byte, error) { + pubkey, _, err := ledger.getAddressPubKeySECP256K1(bip32Path, "thor", false) return pubkey, err } @@ -139,11 +136,11 @@ func validHRPByte(b byte) bool { // GetAddressPubKeySECP256K1 returns the pubkey (compressed) and address (bech( // this command requires user confirmation in the device -func (ledger *LedgerCosmos) GetAddressPubKeySECP256K1(bip32Path []uint32, hrp string) (pubkey []byte, addr string, err error) { +func (ledger *LedgerTHORChain) GetAddressPubKeySECP256K1(bip32Path []uint32, hrp string) (pubkey []byte, addr string, err error) { return ledger.getAddressPubKeySECP256K1(bip32Path, hrp, true) } -func (ledger *LedgerCosmos) GetBip32bytes(bip32Path []uint32, hardenCount int) ([]byte, error) { +func (ledger *LedgerTHORChain) GetBip32bytes(bip32Path []uint32, hardenCount int) ([]byte, error) { var pathBytes []byte var err error @@ -165,60 +162,7 @@ func (ledger *LedgerCosmos) GetBip32bytes(bip32Path []uint32, hardenCount int) ( return pathBytes, nil } -func (ledger *LedgerCosmos) signv1(bip32Path []uint32, transaction []byte) ([]byte, error) { - var packetIndex byte = 1 - var packetCount = 1 + byte(math.Ceil(float64(len(transaction))/float64(userMessageChunkSize))) - - var finalResponse []byte - - var message []byte - - for packetIndex <= packetCount { - chunk := userMessageChunkSize - if packetIndex == 1 { - pathBytes, err := ledger.GetBip32bytes(bip32Path, 3) - if err != nil { - return nil, err - } - header := []byte{userCLA, userINSSignSECP256K1, packetIndex, packetCount, byte(len(pathBytes))} - message = append(header, pathBytes...) - } else { - if len(transaction) < userMessageChunkSize { - chunk = len(transaction) - } - header := []byte{userCLA, userINSSignSECP256K1, packetIndex, packetCount, byte(chunk)} - message = append(header, transaction[:chunk]...) - } - - response, err := ledger.api.Exchange(message) - if err != nil { - if err.Error() == "[APDU_CODE_BAD_KEY_HANDLE] The parameters in the data field are incorrect" { - // In this special case, we can extract additional info - errorMsg := string(response) - switch errorMsg { - case "ERROR: JSMN_ERROR_NOMEM": - return nil, fmt.Errorf("Not enough tokens were provided") - case "PARSER ERROR: JSMN_ERROR_INVAL": - return nil, fmt.Errorf("Unexpected character in JSON string") - case "PARSER ERROR: JSMN_ERROR_PART": - return nil, fmt.Errorf("The JSON string is not a complete.") - } - return nil, fmt.Errorf(errorMsg) - } - return nil, err - } - - finalResponse = response - if packetIndex > 1 { - transaction = transaction[chunk:] - } - packetIndex++ - - } - return finalResponse, nil -} - -func (ledger *LedgerCosmos) signv2(bip32Path []uint32, transaction []byte) ([]byte, error) { +func (ledger *LedgerTHORChain) signv2(bip32Path []uint32, transaction []byte) ([]byte, error) { var packetIndex byte = 1 var packetCount = 1 + byte(math.Ceil(float64(len(transaction))/float64(userMessageChunkSize))) @@ -283,7 +227,7 @@ func (ledger *LedgerCosmos) signv2(bip32Path []uint32, transaction []byte) ([]by // GetAddressPubKeySECP256K1 returns the pubkey (compressed) and address (bech( // this command requires user confirmation in the device -func (ledger *LedgerCosmos) getAddressPubKeySECP256K1(bip32Path []uint32, hrp string, requireConfirmation bool) (pubkey []byte, addr string, err error) { +func (ledger *LedgerTHORChain) getAddressPubKeySECP256K1(bip32Path []uint32, hrp string, requireConfirmation bool) (pubkey []byte, addr string, err error) { if len(hrp) > 83 { return nil, "", fmt.Errorf("hrp len should be <10") } @@ -322,7 +266,7 @@ func (ledger *LedgerCosmos) getAddressPubKeySECP256K1(bip32Path []uint32, hrp st } pubkey = response[0:33] - addr = string(response[33:len(response)]) + addr = string(response[33:]) return pubkey, addr, err } diff --git a/user_app_test.go b/user_app_test.go index 3fe30ce..4c9c74f 100644 --- a/user_app_test.go +++ b/user_app_test.go @@ -14,7 +14,7 @@ * limitations under the License. ********************************************************************************/ -package ledger_cosmos_go +package ledger_thorchain_go import ( "crypto/sha256" @@ -30,7 +30,7 @@ import ( // Ledger Test Mnemonic: equip will roof matter pink blind book anxiety banner elbow sun young func Test_UserFindLedger(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -40,7 +40,7 @@ func Test_UserFindLedger(t *testing.T) { } func Test_UserGetVersion(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -59,7 +59,7 @@ func Test_UserGetVersion(t *testing.T) { } func Test_UserGetPublicKey(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -85,7 +85,7 @@ func Test_UserGetPublicKey(t *testing.T) { } func Test_GetAddressPubKeySECP256K1_Zero(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -111,7 +111,7 @@ func Test_GetAddressPubKeySECP256K1_Zero(t *testing.T) { } func Test_GetAddressPubKeySECP256K1(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -137,7 +137,7 @@ func Test_GetAddressPubKeySECP256K1(t *testing.T) { } func Test_UserPK_HDPaths(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -206,7 +206,7 @@ func getDummyTx() []byte { } func Test_UserSign(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -254,7 +254,7 @@ func Test_UserSign(t *testing.T) { } func Test_UserSign_Fails(t *testing.T) { - userApp, err := FindLedgerCosmosUserApp() + userApp, err := FindLedgerTHORChainUserApp() if err != nil { t.Fatalf(err.Error()) } @@ -273,6 +273,6 @@ func Test_UserSign_Fails(t *testing.T) { errMessage := err.Error() if errMessage != "Invalid character in JSON string" && errMessage != "Unexpected characters" { - assert.Fail(t, "Unexpected error message returned: " + errMessage ) + assert.Fail(t, "Unexpected error message returned: "+errMessage) } } diff --git a/validator_app.go b/validator_app.go deleted file mode 100644 index 2f27bb1..0000000 --- a/validator_app.go +++ /dev/null @@ -1,176 +0,0 @@ -/******************************************************************************* -* (c) 2018 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -package ledger_cosmos_go - -import ( - "fmt" - "math" - - "github.com/cosmos/ledger-go" -) - -const ( - validatorCLA = 0x56 - - validatorINSGetVersion = 0 - validatorINSPublicKeyED25519 = 1 - validatorINSSignED25519 = 2 - - validatorMessageChunkSize = 250 -) - -// Validator app -type LedgerTendermintValidator struct { - // Add support for this app - api *ledger_go.Ledger -} - -// RequiredCosmosUserAppVersion indicates the minimum required version of the Tendermint app -func RequiredTendermintValidatorAppVersion() VersionInfo { - return VersionInfo{0, 0, 5, 0,} -} - -// FindLedgerCosmosValidatorApp finds a Cosmos validator app running in a ledger device -func FindLedgerTendermintValidatorApp() (*LedgerTendermintValidator, error) { - ledgerAPI, err := ledger_go.FindLedger() - - if err != nil { - return nil, err - } - - 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?") - } - defer ledgerAPI.Close() - return nil, err - } - - req := RequiredTendermintValidatorAppVersion() - err = CheckVersion(*appVersion, req) - if err !=nil { - return nil, err - } - - return &ledgerCosmosValidatorApp, err -} - -// Close closes a connection with the Cosmos user app -func (ledger *LedgerTendermintValidator) Close() error { - return ledger.api.Close() -} - -// GetVersion returns the current version of the Cosmos user app -func (ledger *LedgerTendermintValidator) GetVersion() (*VersionInfo, error) { - message := []byte{validatorCLA, validatorINSGetVersion, 0, 0, 0} - response, err := ledger.api.Exchange(message) - - if err != nil { - return nil, err - } - - if len(response) < 4 { - return nil, fmt.Errorf("invalid response") - } - - return &VersionInfo{ - AppMode: response[0], - Major: response[1], - Minor: response[2], - Patch: response[3], - }, nil -} - -// GetPublicKeyED25519 retrieves the public key for the corresponding bip32 derivation path -func (ledger *LedgerTendermintValidator) GetPublicKeyED25519(bip32Path []uint32) ([]byte, error) { - pathBytes, err := GetBip32bytesv1(bip32Path, 10) - if err != nil { - return nil, err - } - - header := []byte{validatorCLA, validatorINSPublicKeyED25519, 0, 0, byte(len(pathBytes))} - message := append(header, pathBytes...) - - response, err := ledger.api.Exchange(message) - - if err != nil { - return nil, err - } - - if len(response) < 4 { - return nil, fmt.Errorf("invalid response. Too short") - } - - return response, nil -} - -// SignSECP256K1 signs a message/vote using the Tendermint validator app -func (ledger *LedgerTendermintValidator) SignED25519(bip32Path []uint32, message []byte) ([]byte, error) { - var packetIndex byte = 1 - var packetCount = 1 + byte(math.Ceil(float64(len(message))/float64(validatorMessageChunkSize))) - - var finalResponse []byte - - var apduMessage []byte - - for packetIndex <= packetCount { - chunk := validatorMessageChunkSize - if packetIndex == 1 { - pathBytes, err := GetBip32bytesv1(bip32Path, 10) - if err != nil { - return nil, err - } - header := []byte{ - validatorCLA, - validatorINSSignED25519, - packetIndex, - packetCount, - byte(len(pathBytes))} - - apduMessage = append(header, pathBytes...) - } else { - if len(message) < validatorMessageChunkSize { - chunk = len(message) - } - header := []byte{ - validatorCLA, - validatorINSSignED25519, - packetIndex, - packetCount, - byte(chunk)} - - apduMessage = append(header, message[:chunk]...) - } - - response, err := ledger.api.Exchange(apduMessage) - if err != nil { - return nil, err - } - - finalResponse = response - if packetIndex > 1 { - message = message[chunk:] - } - packetIndex++ - - } - return finalResponse, nil -} diff --git a/validator_app_test.go b/validator_app_test.go deleted file mode 100644 index 08340ac..0000000 --- a/validator_app_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* -* (c) 2018 ZondaX GmbH -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -package ledger_cosmos_go - -import ( - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "testing" -) - -func Test_ValGetVersion(t *testing.T) { - validatorApp, err := FindLedgerTendermintValidatorApp() - if err != nil { - t.Fatalf( err.Error()) - } - defer validatorApp.Close() - - validatorApp.api.Logging = true - - version, err := validatorApp.GetVersion() - require.Nil(t, err, "Detected error") - assert.Equal(t, uint8(0x0), version.AppMode, "TESTING MODE NOT ENABLED") - assert.Equal(t, uint8(0x0), version.Major, "Wrong Major version") - assert.Equal(t, uint8(0x9), version.Minor, "Wrong Minor version") - assert.Equal(t, uint8(0x0), version.Patch, "Wrong Patch version") -} - -func Test_ValGetPublicKey(t *testing.T) { - validatorApp, err := FindLedgerTendermintValidatorApp() - if err != nil { - t.Fatalf( err.Error()) - } - defer validatorApp.Close() - - validatorApp.api.Logging = true - - path := []uint32{44, 118, 0, 0, 0} - - for i := 1; i < 10; i++ { - pubKey, err := validatorApp.GetPublicKeyED25519(path) - require.Nil(t, err, "Detected error, err: %s\n", err) - - assert.Equal( - t, - 32, - len(pubKey), - "Public key has wrong length: %x, expected length: %x\n", pubKey, 32) - } - -} - -func Test_ValSignED25519(t *testing.T) { - t.Skip("Go support is still not available. Please refer to the Rust library") -}