From c372a8d51b4cd2b2ad87f1c7b9e892218a848cba Mon Sep 17 00:00:00 2001 From: "Pluto (9R)" Date: Fri, 12 Jan 2024 16:04:03 -0400 Subject: [PATCH 1/2] Merge upstream --- .github/dependabot.yml | 16 ++++ .github/workflows/test.yml | 24 +++++ CHANGELOG.md | 7 ++ LICENSE | 2 +- common.go | 4 +- common_test.go | 6 +- go.mod | 25 ++++-- go.sum | 82 +++++++++-------- user_app.go | 142 ++++++++++++++++++++--------- user_app_test.go | 34 +++---- validator_app.go | 178 +++++++++++++++++++++++++++++++++++++ validator_app_test.go | 64 +++++++++++++ 12 files changed, 469 insertions(+), 115 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/test.yml create mode 100644 CHANGELOG.md create mode 100644 validator_app.go create mode 100644 validator_app_test.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4905250 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + time: "11:00" + open-pull-requests-limit: 10 + - package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + time: "11:00" + open-pull-requests-limit: 10 + labels: + - T:dependencies diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a3c7bbb --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,24 @@ +name: Test +on: + push: + branches: + - master + pull_request: + +jobs: + + Test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.18' + - name: test & coverage report creation + run: | + go test common.go -mod=readonly -timeout 5m -short -race -coverprofile=coverage.txt -covermode=atomic + go test common.go -mod=readonly -timeout 5m + - uses: codecov/codecov-action@v3.1.1 + with: + file: ./coverage.txt + fail_ci_if_error: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..539e6d7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# CHANGELOG + +## Unreleased + +### API-Breaking Changes + +* [#39](https://github.com/cosmos/ledger-cosmos-go/pull/39) Add support for SIGN_MODE_TEXTUAL by adding a new argument `p2 byte` to `SignSECP256K1`. diff --git a/LICENSE b/LICENSE index c8e8e95..5f0220d 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018 ZondaX GmbH + Copyright 2018 - 2022 ZondaX AG Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/common.go b/common.go index f2b8de5..7e2e601 100644 --- a/common.go +++ b/common.go @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 - 2022 ZondaX AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -90,7 +90,7 @@ func GetBip32bytesv1(bip32Path []uint32, hardenCount int) ([]byte, error) { } func GetBip32bytesv2(bip44Path []uint32, hardenCount int) ([]byte, error) { - message := make([]byte, 40) + message := make([]byte, 20) if len(bip44Path) != 5 { return nil, fmt.Errorf("path should contain 5 elements") } diff --git a/common_test.go b/common_test.go index 7615160..38d2196 100644 --- a/common_test.go +++ b/common_test.go @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 - 2022 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ func Test_PathGeneration0(t *testing.T) { pathBytes, err := GetBip32bytesv1(bip32Path, 0) if err != nil { - t.Fatalf( "Detected error, err: %s\n", err.Error()) + t.Fatalf("Detected error, err: %s\n", err.Error()) } fmt.Printf("Path: %x\n", pathBytes) @@ -106,7 +106,7 @@ func Test_PathGeneration0v2(t *testing.T) { pathBytes, err := GetBip32bytesv2(bip32Path, 0) if err != nil { - t.Fatalf( "Detected error, err: %s\n", err.Error()) + t.Fatalf("Detected error, err: %s\n", err.Error()) } fmt.Printf("Path: %x\n", pathBytes) diff --git a/go.mod b/go.mod index 90a1fe8..8153db1 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,25 @@ module github.com/thorchain/ledger-thorchain-go -go 1.12 +go 1.18 + +require ( + github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/stretchr/testify v1.8.1 + github.com/zondax/ledger-go v0.14.3 +) require ( - github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d - github.com/cosmos/ledger-go v0.9.2 github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pkg/errors v0.8.1 // indirect - github.com/stretchr/testify v1.3.0 - github.com/zondax/hid v0.9.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/zondax/hid v0.9.2 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 09fc3d8..bb9c1eb 100644 --- a/go.sum +++ b/go.sum @@ -1,46 +1,50 @@ -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= -github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= -github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/user_app.go b/user_app.go index ea07e67..af04631 100644 --- a/user_app.go +++ b/user_app.go @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 - 2022 ZondaX AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package ledger_thorchain_go import ( + "errors" "fmt" "math" @@ -34,43 +35,40 @@ const ( userMessageChunkSize = 250 ) -// LedgerTHORChain represents a connection to the THORChain app in a Ledger Nano S/X device +// LedgerCosmos represents a connection to the Cosmos app in a Ledger Nano S device type LedgerTHORChain struct { - api *ledger_go.Ledger + api ledger_go.LedgerDevice version VersionInfo } -// Compatibility with Cosmos apps calling convention -func FindLedgerCosmosUserApp() (*LedgerTHORChain, error) { - return FindLedgerTHORChainUserApp() -} - -// FindLedgerTHORChainUserApp finds a THORChain user app running in a ledger device -func FindLedgerTHORChainUserApp() (*LedgerTHORChain, error) { - ledgerAPI, err := ledger_go.FindLedger() - +// FindLedgerCosmosUserApp finds a Cosmos user app running in a ledger device +func FindLedgerTHORChainUserApp() (_ *LedgerTHORChain, rerr error) { + ledgerAdmin := ledger_go.NewLedgerAdmin() + ledgerAPI, err := ledgerAdmin.Connect(0) if err != nil { return nil, err } - app := LedgerTHORChain{ledgerAPI, VersionInfo{}} - appVersion, err := app.GetVersion() + defer func() { + if rerr != nil { + ledgerAPI.Close() + } + }() + 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 THORChain app is open?") + err = errors.New("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, nil } // Close closes a connection with the THORChain user app @@ -85,11 +83,13 @@ func (ledger *LedgerTHORChain) CheckVersion(ver VersionInfo) error { return err } - switch version.Major { + switch major := version.Major; major { + case 1: + return CheckVersion(ver, VersionInfo{0, 1, 5, 1}) case 2: return CheckVersion(ver, VersionInfo{0, 2, 0, 0}) default: - return fmt.Errorf("App version is not supported") + return fmt.Errorf("App version %d is not supported", major) } } @@ -103,7 +103,7 @@ func (ledger *LedgerTHORChain) GetVersion() (*VersionInfo, error) { } if len(response) < 4 { - return nil, fmt.Errorf("invalid response") + return nil, errors.New("invalid response") } ledger.version = VersionInfo{ @@ -116,14 +116,17 @@ func (ledger *LedgerTHORChain) GetVersion() (*VersionInfo, error) { return &ledger.version, nil } -// SignSECP256K1 signs a transaction using THORChain user app +// SignSECP256K1 signs a transaction using Cosmos user app. It can either use +// SIGN_MODE_LEGACY_AMINO_JSON (P2=0) or SIGN_MODE_TEXTUAL (P2=1). // this command requires user confirmation in the device -func (ledger *LedgerTHORChain) SignSECP256K1(bip32Path []uint32, transaction []byte) ([]byte, error) { - switch ledger.version.Major { +func (ledger *LedgerTHORChain) SignSECP256K1(bip32Path []uint32, transaction []byte, p2 byte) ([]byte, error) { + switch major := ledger.version.Major; major { + case 1: + return ledger.signv1(bip32Path, transaction) case 2: - return ledger.signv2(bip32Path, transaction) + return ledger.signv2(bip32Path, transaction, p2) default: - return nil, fmt.Errorf("App version is not supported") + return nil, fmt.Errorf("App version %d is not supported", major) } } @@ -149,7 +152,7 @@ func (ledger *LedgerTHORChain) GetBip32bytes(bip32Path []uint32, hardenCount int var pathBytes []byte var err error - switch ledger.version.Major { + switch major := ledger.version.Major; major { case 1: pathBytes, err = GetBip32bytesv1(bip32Path, 3) if err != nil { @@ -161,13 +164,13 @@ func (ledger *LedgerTHORChain) GetBip32bytes(bip32Path []uint32, hardenCount int return nil, err } default: - return nil, fmt.Errorf("App version is not supported") + return nil, fmt.Errorf("App version %d is not supported", major) } return pathBytes, nil } -func (ledger *LedgerTHORChain) signv2(bip32Path []uint32, transaction []byte) ([]byte, error) { +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))) @@ -182,7 +185,64 @@ func (ledger *LedgerTHORChain) signv2(bip32Path []uint32, transaction []byte) ([ if err != nil { return nil, err } - header := []byte{userCLA, userINSSignSECP256K1, 0, 0, byte(len(pathBytes))} + 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, errors.New("Not enough tokens were provided") + case "PARSER ERROR: JSMN_ERROR_INVAL": + return nil, errors.New("Unexpected character in JSON string") + case "PARSER ERROR: JSMN_ERROR_PART": + return nil, errors.New("The JSON string is not a complete.") + } + return nil, errors.New(errorMsg) + } + return nil, err + } + + finalResponse = response + if packetIndex > 1 { + transaction = transaction[chunk:] + } + packetIndex++ + + } + return finalResponse, nil +} + +func (ledger *LedgerCosmos) signv2(bip32Path []uint32, transaction []byte, p2 byte) ([]byte, error) { + var packetIndex byte = 1 + var packetCount = 1 + byte(math.Ceil(float64(len(transaction))/float64(userMessageChunkSize))) + + var finalResponse []byte + + var message []byte + + if p2 > 1 { + return nil, errors.New("only values of SIGN_MODE_LEGACY_AMINO (P2=0) and SIGN_MODE_TEXTUAL (P2=1) are allowed") + } + + for packetIndex <= packetCount { + chunk := userMessageChunkSize + if packetIndex == 1 { + pathBytes, err := ledger.GetBip32bytes(bip32Path, 3) + if err != nil { + return nil, err + } + header := []byte{userCLA, userINSSignSECP256K1, 0, p2, byte(len(pathBytes))} message = append(header, pathBytes...) } else { if len(transaction) < userMessageChunkSize { @@ -194,7 +254,7 @@ func (ledger *LedgerTHORChain) signv2(bip32Path []uint32, transaction []byte) ([ payloadDesc = byte(2) } - header := []byte{userCLA, userINSSignSECP256K1, payloadDesc, 0, byte(chunk)} + header := []byte{userCLA, userINSSignSECP256K1, payloadDesc, p2, byte(chunk)} message = append(header, transaction[:chunk]...) } @@ -205,17 +265,17 @@ func (ledger *LedgerTHORChain) signv2(bip32Path []uint32, transaction []byte) ([ errorMsg := string(response) switch errorMsg { case "ERROR: JSMN_ERROR_NOMEM": - return nil, fmt.Errorf("Not enough tokens were provided") + return nil, errors.New("Not enough tokens were provided") case "PARSER ERROR: JSMN_ERROR_INVAL": - return nil, fmt.Errorf("Unexpected character in JSON string") + return nil, errors.New("Unexpected character in JSON string") case "PARSER ERROR: JSMN_ERROR_PART": - return nil, fmt.Errorf("The JSON string is not a complete.") + return nil, errors.New("The JSON string is not a complete.") } - return nil, fmt.Errorf(errorMsg) + return nil, errors.New(errorMsg) } if err.Error() == "[APDU_CODE_DATA_INVALID] Referenced data reversibly blocked (invalidated)" { errorMsg := string(response) - return nil, fmt.Errorf(errorMsg) + return nil, errors.New(errorMsg) } return nil, err } @@ -234,13 +294,13 @@ func (ledger *LedgerTHORChain) signv2(bip32Path []uint32, transaction []byte) ([ // this command requires user confirmation in the device 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") + return nil, "", errors.New("hrp len should be <10") } hrpBytes := []byte(hrp) for _, b := range hrpBytes { if !validHRPByte(b) { - return nil, "", fmt.Errorf("all characters in the HRP must be in the [33, 126] range") + return nil, "", errors.New("all characters in the HRP must be in the [33, 126] range") } } @@ -267,7 +327,7 @@ func (ledger *LedgerTHORChain) getAddressPubKeySECP256K1(bip32Path []uint32, hrp return nil, "", err } if len(response) < 35+len(hrp) { - return nil, "", fmt.Errorf("Invalid response") + return nil, "", errors.New("Invalid response") } pubkey = response[0:33] diff --git a/user_app_test.go b/user_app_test.go index 4c9c74f..d8d0efc 100644 --- a/user_app_test.go +++ b/user_app_test.go @@ -1,5 +1,5 @@ /******************************************************************************* -* (c) 2018 ZondaX GmbH +* (c) 2018 - 2022 ZondaX AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,11 +20,13 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "github.com/btcsuite/btcd/btcec" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "strings" "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // Ledger Test Mnemonic: equip will roof matter pink blind book anxiety banner elbow sun young @@ -46,8 +48,6 @@ func Test_UserGetVersion(t *testing.T) { } defer userApp.Close() - userApp.api.Logging = true - version, err := userApp.GetVersion() require.Nil(t, err, "Detected error") fmt.Println(version) @@ -65,8 +65,6 @@ func Test_UserGetPublicKey(t *testing.T) { } defer userApp.Close() - userApp.api.Logging = true - path := []uint32{44, 118, 5, 0, 21} pubKey, err := userApp.GetPublicKeySECP256K1(path) @@ -91,8 +89,6 @@ func Test_GetAddressPubKeySECP256K1_Zero(t *testing.T) { } defer userApp.Close() - userApp.api.Logging = true - hrp := "cosmos" path := []uint32{44, 118, 0, 0, 0} @@ -117,8 +113,6 @@ func Test_GetAddressPubKeySECP256K1(t *testing.T) { } defer userApp.Close() - userApp.api.Logging = true - hrp := "cosmos" path := []uint32{44, 118, 5, 0, 21} @@ -143,8 +137,6 @@ func Test_UserPK_HDPaths(t *testing.T) { } defer userApp.Close() - userApp.api.Logging = true - path := []uint32{44, 118, 0, 0, 0} expected := []string{ @@ -180,7 +172,7 @@ func Test_UserPK_HDPaths(t *testing.T) { hex.EncodeToString(pubKey), "Public key 44'/118'/0'/0/%d does not match\n", i) - _, err = btcec.ParsePubKey(pubKey[:], btcec.S256()) + _, err = btcec.ParsePubKey(pubKey[:]) require.Nil(t, err, "Error parsing public key err: %s\n", err) } @@ -212,12 +204,10 @@ func Test_UserSign(t *testing.T) { } defer userApp.Close() - userApp.api.Logging = true - path := []uint32{44, 118, 0, 0, 5} message := getDummyTx() - signature, err := userApp.SignSECP256K1(path, message) + signature, err := userApp.SignSECP256K1(path, message, 0) if err != nil { t.Fatalf("[Sign] Error: %s\n", err.Error()) } @@ -233,13 +223,13 @@ func Test_UserSign(t *testing.T) { return } - pub2, err := btcec.ParsePubKey(pubKey[:], btcec.S256()) + pub2, err := btcec.ParsePubKey(pubKey[:]) if err != nil { t.Fatalf("[ParsePK] Error: " + err.Error()) return } - sig2, err := btcec.ParseDERSignature(signature[:], btcec.S256()) + sig2, err := ecdsa.ParseDERSignature(signature[:]) if err != nil { t.Fatalf("[ParseSig] Error: " + err.Error()) return @@ -260,15 +250,13 @@ func Test_UserSign_Fails(t *testing.T) { } defer userApp.Close() - userApp.api.Logging = true - path := []uint32{44, 118, 0, 0, 5} message := getDummyTx() garbage := []byte{65} message = append(garbage, message...) - _, err = userApp.SignSECP256K1(path, message) + _, err = userApp.SignSECP256K1(path, message, 0) assert.Error(t, err) errMessage := err.Error() diff --git a/validator_app.go b/validator_app.go new file mode 100644 index 0000000..617c536 --- /dev/null +++ b/validator_app.go @@ -0,0 +1,178 @@ +/******************************************************************************* +* (c) 2018 - 2022 ZondaX AG +* +* 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 ( + "errors" + "math" + + "github.com/zondax/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.LedgerDevice +} + +// 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, rerr error) { + ledgerAdmin := ledger_go.NewLedgerAdmin() + ledgerAPI, err := ledgerAdmin.Connect(0) + if err != nil { + return nil, err + } + + defer func() { + if rerr != nil { + defer ledgerAPI.Close() + } + }() + + ledgerCosmosValidatorApp := &LedgerTendermintValidator{ledgerAPI} + appVersion, err := ledgerCosmosValidatorApp.GetVersion() + if err != nil { + if err.Error() == "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported" { + err = errors.New("are you sure the Tendermint Validator app is open?") + } + return nil, err + } + + req := RequiredTendermintValidatorAppVersion() + if err := CheckVersion(*appVersion, req); 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, errors.New("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, errors.New("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 new file mode 100644 index 0000000..c61a26c --- /dev/null +++ b/validator_app_test.go @@ -0,0 +1,64 @@ +/******************************************************************************* +* (c) 2018 - 2022 ZondaX AG +* +* 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() + + 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() + + 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") +} From f09d69ec5e5a4df835b01c2df493f35f4d2d03e6 Mon Sep 17 00:00:00 2001 From: "Pluto (9R)" Date: Fri, 12 Jan 2024 16:10:38 -0400 Subject: [PATCH 2/2] drop github workflows --- .github/dependabot.yml | 16 ---------------- .github/workflows/test.yml | 24 ------------------------ 2 files changed, 40 deletions(-) delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 4905250..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: "/" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 10 - - package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 10 - labels: - - T:dependencies diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index a3c7bbb..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Test -on: - push: - branches: - - master - pull_request: - -jobs: - - Test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 - with: - go-version: '1.18' - - name: test & coverage report creation - run: | - go test common.go -mod=readonly -timeout 5m -short -race -coverprofile=coverage.txt -covermode=atomic - go test common.go -mod=readonly -timeout 5m - - uses: codecov/codecov-action@v3.1.1 - with: - file: ./coverage.txt - fail_ci_if_error: true